Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
600 views
in Technique[技术] by (71.8m points)

c++ - Python ctypes: how to free memory? Getting invalid pointer error

I want to get some string from a C/C++ library with ctypes into python. My code looks like this:

Code in lib:

const char* get(struct something *x) 
{
    [...]
    // buf is a stringstream
   return strdup(buf.str().c_str());
}

void freeme(char *ptr)
{
    free(ptr);
}

Python code:

fillprototype(lib.get, c_char_p, POINTER(some_model)])
fillprototype(lib.freeme, None, [c_char_p])

// what i want to do here: get a string into python so that i can work
// with it and release the memory in the lib.
c_str = lib.get(some_model)
y = ''.join(c_str)
lib.freeme(c_str) 

If i print() c_str, everything is there. Problem is (or seems to be) in the last Python line. I cannot free the memory - the library is getting a wrong pointer. What I am doing wrong here? (And please don't suggest boost::python or so).

*** glibc detected *** python2: munmap_chunk(): invalid pointer: 0x00000000026443fc ***
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As David Schwartz pointed out, if you set restype to c_char_p, ctypes returns a regular Python string object. A simple way to get around this is to use a void * and cast the result:

string.c:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

char *get(void)
{
    char *buf = "Hello World";
    char *new_buf = strdup(buf);
    printf("allocated address: %p
", new_buf);
    return new_buf;
}

void freeme(char *ptr)
{
    printf("freeing address: %p
", ptr);
    free(ptr);
}

Python usage:

from ctypes import *

lib = cdll.LoadLibrary('./string.so')
lib.freeme.argtypes = c_void_p,
lib.freeme.restype = None
lib.get.argtypes = []
lib.get.restype = c_void_p

>>> ptr = lib.get()
allocated address: 0x9facad8
>>> hex(ptr)
'0x9facad8'
>>> cast(ptr, c_char_p).value
'Hello World'
>>> lib.freeme(ptr)
freeing address: 0x9facad8

You can also use a subclass of c_char_p. It turns out that ctypes doesn't call the getfunc for a subclass of a simple type.

class c_char_p_sub(c_char_p):
    pass

lib.get.restype = c_char_p_sub

The value attribute returns the string. You can leave the parameter for freeme as the more generic c_void_p. That accepts any pointer type or integer address.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...