Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

ctypes: delay conversion from c_char_p to string

23 views
Skip to first unread message

Brendan Miller

unread,
Apr 21, 2010, 6:15:36 PM4/21/10
to pytho...@python.org
I have a function exposed through ctypes that returns a c_char_p.
Since I need to deallocate that c_char_p, it's inconvenient that
ctypes copies the c_char_p into a string instead of giving me the raw
pointer. I believe this will cause a memory leak, unless ctypes is
smart enough to free the string itself after the copy... which I
doubt.

Is there some way to tell ctypes to return an actual c_char_p, or is
my best bet to return a c_void_p and cast to c_char_p when I'm reading
to convert to a string?

Thanks
Brendan

Brendan Miller

unread,
Apr 21, 2010, 6:29:24 PM4/21/10
to pytho...@python.org
Here's the method I was using. Note that tmp_char_ptr is of type
c_void_p. This should avoid the memory leak, assuming I am
interpreting the semantics of the cast correctly. Is there a cleaner
way to do this with ctypes?

def get_prop_string(self, prop_name):
# Have to work with c_void_p to prevent ctypes from copying to a string
# without giving me an opportunity to destroy the original string.
tmp_char_ptr = _get_prop_string(self._props, prop_name)
prop_val = cast(tmp_char_ptr, c_char_p).value
_string_destroy(tmp_char_ptr)
return prop_val

Zvezdan Petkovic

unread,
Apr 22, 2010, 10:49:23 AM4/22/10
to Python List

On Apr 21, 2010, at 6:29 PM, Brendan Miller wrote:

> Here's the method I was using. Note that tmp_char_ptr is of type
> c_void_p. This should avoid the memory leak, assuming I am
> interpreting the semantics of the cast correctly. Is there a cleaner
> way to do this with ctypes?
>
> def get_prop_string(self, prop_name):
> # Have to work with c_void_p to prevent ctypes from copying to a string
> # without giving me an opportunity to destroy the original string.
> tmp_char_ptr = _get_prop_string(self._props, prop_name)
> prop_val = cast(tmp_char_ptr, c_char_p).value
> _string_destroy(tmp_char_ptr)
> return prop_val

Is this what you want?

=====

import ctypes.util


libc = ctypes.CDLL(ctypes.util.find_library('libc'))

libc.free.argtypes = [ctypes.c_void_p]
libc.free.restype = None
libc.strdup.argtype = [ctypes.c_char_p]
libc.strdup.restype = ctypes.POINTER(ctypes.c_char)


def strdup_and_free(s):
s_ptr = libc.strdup(s)
print s_ptr.contents
i = 0
while s_ptr[i] != '\0':
print s_ptr[i],
i += 1
print
libc.free(s_ptr)


if __name__ == '__main__':
strdup_and_free('My string')

=====

That is an equivalent of this C program:

=====

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


void
strdup_and_free(char *s)
{
char *s_ptr = strdup(s);

printf("%c\n", *s_ptr);
printf("%s\n", s_ptr);
free(s_ptr);
}


int
main(int argc, char *argv[])
{
strdup_and_free("My string");
return 0;
}

=====

To prove that the pointer was actually freed try inserting one more
libc.free(s_ptr) at the end of the function.
You should get Abort trap because you are attempting to free storage that has already been freed (I did on Mac OS X 10.6)


If you insert a second free(s_ptr) call in the C program you also get an Abort trap:

strdup2(15785) malloc: *** error for object 0x100100080: pointer being freed was not allocated

(Again, this is an error message on Mac OS X 10.6)


FWIW, this technique for getting pointer *is* mentioned in ctypes documentation.

Best regards,

Zvezdan


>
> On Wed, Apr 21, 2010 at 3:15 PM, Brendan Miller <catp...@catphive.net> wrote:
>> I have a function exposed through ctypes that returns a c_char_p.
>> Since I need to deallocate that c_char_p, it's inconvenient that
>> ctypes copies the c_char_p into a string instead of giving me the raw
>> pointer. I believe this will cause a memory leak, unless ctypes is
>> smart enough to free the string itself after the copy... which I
>> doubt.
>>
>> Is there some way to tell ctypes to return an actual c_char_p, or is
>> my best bet to return a c_void_p and cast to c_char_p when I'm reading
>> to convert to a string?
>>
>> Thanks
>> Brendan
>>

> --
> http://mail.python.org/mailman/listinfo/python-list

Zvezdan Petkovic

unread,
Apr 22, 2010, 12:18:09 PM4/22/10
to Python List
On Apr 22, 2010, at 10:49 AM, Zvezdan Petkovic wrote:
> libc.strdup.argtype = [ctypes.c_char_p]

Correcting my typo. This should be in plural:

libc.strdup.argtypes = [ctypes.c_char_p]

Brendan Miller

unread,
Apr 22, 2010, 5:34:11 PM4/22/10
to Zvezdan Petkovic, Python List

Ah, so c_char_p's are converted to python strings by ctypes, but
POINTER(c_char) is *not*.

Thanks

Thomas Heller

unread,
Apr 24, 2010, 8:57:06 AM4/24/10
to pytho...@python.org
Brendan Miller schrieb:

> I have a function exposed through ctypes that returns a c_char_p.
> Since I need to deallocate that c_char_p, it's inconvenient that
> ctypes copies the c_char_p into a string instead of giving me the raw
> pointer. I believe this will cause a memory leak, unless ctypes is
> smart enough to free the string itself after the copy... which I
> doubt.
>
> Is there some way to tell ctypes to return an actual c_char_p, or is
> my best bet to return a c_void_p and cast to c_char_p when I'm reading
> to convert to a string?

Yes, there is. When you create a subclass of c_char_p (or any other 'simple'
ctypes type like c_wchar_p or even c_int and alike) then the automatic conversion
to native Python types like string, unicode, integer is not done.
The function will return an instance of that specific class; you can
retrive the value via the .value property and deallocate the resources
in the destructor for example.


--
Thanks,
Thomas

0 new messages