from ctypes import *

charptr = POINTER(c_char)

test = CDLL('test.so')
test.initializetest.argtypes = []
test.initializetest.restype = charptr
test.searchtest.argtypes = [charptr]
test.searchtest.restype = c_int

buf = test.initializetest()
test.searchtest(buf)
print cast(buf, c_char_p).value
# TODO Release the "buf" memory or it will leak.

EDIT

Initially I used c_char_p to pass the buffer between the functions but c_char_p is like a const pointer. If used as a restype, you will actually get a Python str back. So for initializetest it will create a string from the allocated memory (by copying data) and throw the pointer away.

Now we're creating a new type, a POINTER to c_char. This is then used in both functions.

For Python, this type points to a single char so we have to cast it to get the whole string after searchtest is done. We cast to c_char_p because we just want to read the value so a const pointer is OK.

As a side note, this illustrates the disastrous effect of using c_char_p with functions that modify the array (as searchtest above does):

>>> libc.memset.argtypes = [c_char_p, c_int, c_int]
>>> foo = 'Python'
>>> foo
'Python'
>>> libc.memset(foo, ord('x'), 3)
44808532
>>> foo
'xxxhon'
>>>

Note how we've managed to change an immutable Python string!

The argtypes setup line isn't even needed because ctypes assumes c_char_p if Python str is used as argument.

Answer from yak on Stack Overflow
🌐
Python
docs.python.org › 3 › library › ctypes.html
ctypes — A foreign function library for Python
The current memory block contents can be accessed (or changed) with the raw property; if you want to access it as NUL terminated string, use the value property: >>> from ctypes import * >>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes >>> print(sizeof(p), repr(p.raw)) 3 b'\x00\x00\x00' >>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string >>> print(sizeof(p), repr(p.raw)) 6 b'Hello\x00' >>> print(repr(p.value)) b'Hello' >>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer >>> print(sizeof(p), repr(p.raw)) 10 b'Hello\x00\x00\x00\x00\x00' >>> p.value = b"Hi" >>> print(sizeof(p), repr(p.raw)) 10 b'Hi\x00lo\x00\x00\x00\x00\x00' >>>
🌐
CodersLegacy
coderslegacy.com › home › python › python ctypes tutorial
Python ctypes Tutorial - CodersLegacy
October 27, 2022 - Hence it worked! Every ctype datatype has a value attribute, which returns a native python object. Hence we were able to return a string from the char pointer (which is basically C’s version of a string).
Top answer
1 of 2
17
from ctypes import *

charptr = POINTER(c_char)

test = CDLL('test.so')
test.initializetest.argtypes = []
test.initializetest.restype = charptr
test.searchtest.argtypes = [charptr]
test.searchtest.restype = c_int

buf = test.initializetest()
test.searchtest(buf)
print cast(buf, c_char_p).value
# TODO Release the "buf" memory or it will leak.

EDIT

Initially I used c_char_p to pass the buffer between the functions but c_char_p is like a const pointer. If used as a restype, you will actually get a Python str back. So for initializetest it will create a string from the allocated memory (by copying data) and throw the pointer away.

Now we're creating a new type, a POINTER to c_char. This is then used in both functions.

For Python, this type points to a single char so we have to cast it to get the whole string after searchtest is done. We cast to c_char_p because we just want to read the value so a const pointer is OK.

As a side note, this illustrates the disastrous effect of using c_char_p with functions that modify the array (as searchtest above does):

>>> libc.memset.argtypes = [c_char_p, c_int, c_int]
>>> foo = 'Python'
>>> foo
'Python'
>>> libc.memset(foo, ord('x'), 3)
44808532
>>> foo
'xxxhon'
>>>

Note how we've managed to change an immutable Python string!

The argtypes setup line isn't even needed because ctypes assumes c_char_p if Python str is used as argument.

2 of 2
1

Maybe by using restype like described here

class Test(object):
    def __init__(self):
        self.test_library=CDLL("./test.so")
        self.test_initialize = self.test_library.initializetest
        self.test_initialize.argtypes = []
        self.test_initialize.restype = c_char_p # c_char_p is a pointer to a string
        self.test_search = self.test_library.searchtest
        self.test_search.restype = c_int
        self.test_search.argtypes = [c_char_p]
        self.m = c_char_p(self.test_initialize())

    def search(self):
        return self.test_search(self.m).value

r = Test()
print r.search()

EDIT: corrected after test :)

🌐
Turing
turing.com › kb › ctypes-modules-and-their-implementation-in-python
Ctypes Modules and their implementation in Python.
Each ctypes data type has an attribute that returns a native Python object that will help you return a string from the char pointer.
🌐
Python.org
discuss.python.org › python help
Which string type is faster for ctypes - Python Help - Discussions on Python.org
February 1, 2024 - I would like to know which one is faster ? In Odin, I can convert the c_char_p into a wchar pointer, but if ctypes can do it, it is good for me. And I have a second question. Ctypes will convert python strings toc_char_p or c_wchar_p automatically, But does t...
Top answer
1 of 5
18

Your problem is that greeting was allocated on the stack, but the stack is destroyed when the function returns. You could allocate the memory dynamically:

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

const char* hello(char* name) {
    char* greeting = malloc(100);
    snprintf("Hello, %s!\n", 100, name)
    printf("%s\n", greeting);
    return greeting;
}

But that's only part of the battle because now you have a memory leak. You could plug that with another ctypes call to free().

...or a much better approach is to read up on the official C binding to python (python 2.x at http://docs.python.org/2/c-api/ and python 3.x at http://docs.python.org/3/c-api/). Have your C function create a python string object and hand that back. It will be garbage collected by python automatically. Since you are writing the C side, you don't have to play the ctypes game.

...edit..

I didn't compile and test, but I think this .py would work:

import ctypes

# define the interface
hello = ctypes.cdll.LoadLibrary('./hello.so')
# find lib on linux or windows
libc = ctypes.CDLL(ctypes.util.find_library('c'))
# declare the functions we use
hello.hello.argtypes = (ctypes.c_char_p,)
hello.hello.restype = ctypes.c_char_p
libc.free.argtypes = (ctypes.c_void_p,)

# wrap hello to make sure the free is done
def hello(name):
    _result = hello.hello(name)
    result = _result.value
    libc.free(_result)
    return result

# do the deed
print hello("Frank")
2 of 5
6

In hello.c you return a local array. You have to return a pointer to an array, which has to be dynamically allocated using malloc.

char* hello(char* name)
{ 
    char hello[] = "Hello ";
    char excla[] = "!\n";
    char *greeting = malloc ( sizeof(char) * ( strlen(name) + strlen(hello) + strlen(excla) + 1 ) );
    if( greeting == NULL) exit(1);
    strcpy( greeting , hello);
    strcat(greeting, name);
    strcat(greeting, excla);
    return greeting;
}
🌐
Python
svn.python.org › projects › ctypes › trunk › ctypes › docs › manual › tutorial.html
ctypes tutorial
The current memory block contents can be accessed (or changed) with the raw property, if you want to access it as NUL terminated string, use the string property: >>> from ctypes import * >>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes >>> print sizeof(p), repr(p.raw) 3 '\x00\x00\x00' >>> p = create_string_buffer("Hello") # create a buffer containing a NUL terminated string >>> print sizeof(p), repr(p.raw) 6 'Hello\x00' >>> print repr(p.value) 'Hello' >>> p = create_string_buffer("Hello", 10) # create a 10 byte buffer >>> print sizeof(p), repr(p.raw) 10 'Hello\x00\x00\x00\x00\x00' >>> p.value = "Hi" >>> print sizeof(p), repr(p.raw) 10 'Hi\x00lo\x00\x00\x00\x00\x00' >>>
Find elsewhere
🌐
ProgramCreek
programcreek.com › python › example › 1243 › ctypes.c_char_p
Python Examples of ctypes.c_char_p
def c_str(string): """Create ctypes char * from a Python string. Parameters ---------- string : string type Python string. Returns ------- str : c_char_p A char pointer that can be passed to C API.
🌐
dbader.org
dbader.org › blog › python-ctypes-tutorial
Extending Python With C Libraries and the “ctypes” Module – dbader.org
September 5, 2017 - While basic types, ints and floats, generally get marshalled by ctypes trivially, strings pose a problem. In Python, strings are immutable, meaning they cannot change.
Top answer
1 of 2
2

The value received is a byte string in both Python 2 and Python 3. Python 3 displays byte string with the b'' syntax by default to indicate it is a byte (data) string and not a Unicode (text) string. If you want to process the char* as text, decode it from data bytes to text appropriately. For example if the value represents a simple ASCII string:

>>> s = b'ascii string'
>>> print(s)
b'ascii string'
>>> print(s.decode('ascii'))
ascii string

.decode() takes an encoding and converts a byte string to a Unicode string. Of course as mentioned in the other answer, you could pass a wchar_t* from C++ to the callback instead and ctypes will do the decoding for you with ctypes.wstring_at.

Another important point is ctypes will convert c_char_p to an actual Python bytes object and the address used by string_at is actually the buffer in the bytes object, which is copied from the pointer passed by C++ and stops at the first zero byte. If your callback intends to modify the buffer and intend C++ to see the changes, or the data buffer contains zero bytes and needs to process the entire length, this won't work.

C demo code:

#include <stdio.h>

typedef void (*callback)(char*,size_t);

__declspec(dllexport)
void func(callback cb) {
    char data[] = {'a','b','c',0,'d','e','f'};
    if(cb)
        cb(data,sizeof data);
    for(int i = 0; i < sizeof data; ++i)
        printf("%c ",data[i]);
    printf("\n");
}

Failing example with c_char_p:

from ctypes import *

dll = CDLL('./test')
CALLBACK = CFUNCTYPE(None,c_char_p,c_size_t)

@CALLBACK
def callback(s,size):
    print(string_at(s,size))
    s[0] = b'x'
    s[1] = b'y'
    s[size-1] = b'z'
    print(string_at(s,size))

dll.func.argtypes = CALLBACK,
dll.func.restype = None

dll.func(callback)

Note the byte string after the zero byte is incorrect, and you can't modify the buffer:

b'abc\x00\x8b\x01\x00'
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 234, in 'calling callback function'
  File "C:\Users\metolone\test.py", line 9, in callback
    s[0] = b'x'
TypeError: 'bytes' object does not support item assignment
a b c   d e f

Passing example with POINTER(c_char) and added some display options:

from ctypes import *

dll = CDLL('./test')
CALLBACK = CFUNCTYPE(None,POINTER(c_char),c_size_t)

@CALLBACK
def callback(s,size):
    print(string_at(s,size))                  # display as bytes
    print(string_at(s,size).decode('ascii'))  # display as text
    print(string_at(s,size).hex())            # display as hexadecimal
    s[0] = b'x'
    s[1] = b'y'
    s[size-1] = b'z'
    print(string_at(s,size))

dll.func.argtypes = CALLBACK,
dll.func.restype = None

dll.func(callback)

Note the buffer is correct, can be modified, and C shows the change.

b'abc\x00def'                   # byte string display
abc def                         # text string display
61626300646566                  # hex string display
b'xyc\x00dez'
x y c   d e z
2 of 2
0

The b'...' is the syntax for a bytes literal and not part of the string, it's telling you that the "string" is actually a bytes object, not a str. Changing %s to %r in your format string shows this more clearly:

$ python3 test.py
In py_callback(), len = 6, str = <b'foobar'>
$ python2 test.py
In py_callback(), len = 6, str = <'foobar'>

There's not a perfectly reliable way to go from char* to str because the meaning of str has changed. In Python 3, str went from being a sequence of bytes to a unicode string (the python 2 unicode type).

You can get around it in several ways.

Wrapper function for ctypes

The conversion of char* will be perfectly consistent depending on which Python major version you have -- 3 will always have bytes, 2 will always have str -- so you can write a function that you call instead of ctypes.string_at.

import sys
if sys.version_info[0] >= 3:
    def c_string_at(buf, len):
        return ctypes.string_at(buf, len).decode()
else:
    c_string_at = ctypes.string_at

Solve it in C++ by making your callback parameter const wchar_t*

extern "C" void testLib2(void *(*callback)(const wchar_t*, size_t))
{
    callback(L"foobar", 6);
}
def py_callback(buf, count):
    value = ctypes.wstring_at(buf, count)
    print("In py_callback(), count = %d, str = <%s>, type = %s" % (
        count, value, type(value).__name__))

callback = ctypes.CFUNCTYPE(None, ctypes.c_wchar_p, ctypes.c_size_t)
_lib = ctypes.CDLL('./libfoo.so')
_lib.testLib2(callback(py_callback))
$ python2 test.py
In py_callback(), count = 6, str = <foobar>, type = unicode
$ python3 test.py
In py_callback(), count = 6, str = <foobar>, type = str
🌐
GitHub
github.com › alonho › course › blob › master › doc › ctypes.md
course/doc/ctypes.md at master · alonho/course
ctypes type | C type | Python type -------------------------------------------------------------------- c_bool | _Bool | bool (1) c_char | char | 1-character string c_wchar | wchar_t | 1-character unicode string c_byte | char | int/long c_ubyte | unsigned char | int/long c_short | short | int/long c_ushort | unsigned short | int/long c_int | int | int/long c_uint | unsigned int | int/long c_long | long | int/long c_ulong | unsigned long | int/long c_longlong | long long | int/long c_ulonglong | unsigned long long | int/long c_float | float | float c_double | double | float c_longdouble | long double | float c_char_p | char * | string or None c_wchar_p | wchar_t * | unicode or None c_void_p | void * | int/long or None
Author: alonho
🌐
Hyunyoung2
hyunyoung2.github.io › 2018 › 05 › 11 › How_To_Use_Ctypes_In_Python1
How to wrapping function written in another language with ctypes
Basic types, ints, and floats generally get marshalled byt ctypes trivially, But in python string is immutable.
🌐
Python
python-list.python.narkive.com › pJK2bMLY › ctypes-delay-conversion-from-c-char-p-to-string
ctypes: delay conversion from c_char_p to string
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.
🌐
Scaler
scaler.com › home › topics › python ctypes module
Python Ctypes Module - Scaler Topics
May 4, 2023 - Using Ctypes data types, you can return a string from the char pointer using a Python native object.
Top answer
1 of 2
18

Assigning a new value to instances of the pointer types c_char_p, c_wchar_p, and c_void_p changes the memory location they point to, not the contents of the memory block (of course not, because Python strings are immutable):

>>> s = "Hello, World"
>>> c_s = c_char_p(s)
>>> print c_s
c_char_p('Hello, World')
>>> c_s.value = "Hi, there"
>>> print c_s
c_char_p('Hi, there')
>>> print s                 # first string is unchanged
Hello, World
>>>

You should be careful, however, not to pass them to functions expecting pointers to mutable memory. If you need mutable memory blocks, ctypes has a create_string_buffer function which creates these in various ways. The current memory block contents can be accessed (or changed) with the raw property, if you want to access it as NUL terminated string, use the string property:

Says the ctypes tutorial. What I gather from this is that only if the function would work with a const char*, would passing in the python string be valid. Keep in mind, it won't have a null termination.

I'd suggest using create_string_buffer anyhow.

2 of 2
6

The type ctypes.c_char_p represents a nul-terminated string. If a C function takes a const char* you can pass a Python string to it and it will receive a nul-terminated version.

A Windows example DLL:

// test.c - cl /LD test.c
#include <string.h>

__declspec(dllexport)
char* func(char* a, size_t len, const char* b) {
    if(strlen(b) * 2 >= len)
        return NULL;
    strcpy_s(a, len, b);
    strcat_s(a, len, b);
    return a;
}

Python:

Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from ctypes import *
>>> x = CDLL('test')
>>> x.func.restype = c_char_p
>>> x.func.argtypes = c_char_p, c_int, c_char_p
>>> s = create_string_buffer(10)
>>> x.func(s, len(s), 'abcd')
'abcdabcd'
🌐
Dalkescientific
dalkescientific.com › writings › NBN › ctypes.html
ctypes
the number 10 and string spam 30 bytes in the output · The ctypes interface by default only handles Python integer, long, and string data types.
Top answer
1 of 2
17
from ctypes import *

charptr = POINTER(c_char)

test = CDLL('test.so')
test.initializetest.argtypes = []
test.initializetest.restype = charptr
test.searchtest.argtypes = [charptr]
test.searchtest.restype = c_int

buf = test.initializetest()
test.searchtest(buf)
print cast(buf, c_char_p).value
# TODO Release the "buf" memory or it will leak.

Initially I used c_char_p to pass the buffer between the functions but c_char_p is like a const pointer. If used as a restype, you will actually get a Python str back. So for initializetest it will create a string from the allocated memory (by copying data) and throw the pointer away.

Now we're creating a new type, a POINTER to c_char. This is then used in both functions.

For Python, this type points to a single char so we have to cast it to get the whole string after searchtest is done. We cast to c_char_p because we just want to read the value so a const pointer is OK.

As a side note, this illustrates the disastrous effect of using c_char_p with functions that modify the array (as searchtest above does):

>>> libc.memset.argtypes = [c_char_p, c_int, c_int]
>>> foo = 'Python'
>>> foo
'Python'
>>> libc.memset(foo, ord('x'), 3)
44808532
>>> foo
'xxxhon'

Note how we've managed to change an immutable Python string!

The argtypes setup line isn't even needed because ctypes assumes c_char_p if Python str is used as argument.

2 of 2
1

Maybe by using restype like described here

class Test(object):
    def __init__(self):
        self.test_library=CDLL("./test.so")
        self.test_initialize = self.test_library.initializetest
        self.test_initialize.argtypes = []
        self.test_initialize.restype = c_char_p # c_char_p is a pointer to a string
        self.test_search = self.test_library.searchtest
        self.test_search.restype = c_int
        self.test_search.argtypes = [c_char_p]
        self.m = c_char_p(self.test_initialize())

    def search(self):
        return self.test_search(self.m).value

r = Test()
print r.search()

EDIT: corrected after test :)