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.

Answer from bryanegr on Stack Overflow
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'
🌐
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).
🌐
Python
docs.python.org › 3 › library › ctypes.html
ctypes — A foreign function library for Python
None is passed as a C NULL pointer, bytes objects and strings are passed as pointer to the memory block that contains their data (char* or wchar_t*). Python integers are passed as the platform’s default C int type, their value is masked to ...
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 :)

🌐
dbader.org
dbader.org › blog › python-ctypes-tutorial
Extending Python With C Libraries and the “ctypes” Module – dbader.org
September 5, 2017 - Again, ctypes makes easy things easy—passing ints around works seamlessly and does pretty much what you expect it to. 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.
🌐
Crankuptheamps
devnull.crankuptheamps.com › documentation › html › 5.2.1.0 › dev-guides › python › html › chapters › advanced-topics.html
12. Advanced Topics — Python Developer Guide 5.2.1.0 documentation
The ctypes module handles type conversions between Python and C types. In this case, the module passes the underlying Python client as the first argument of the C function. The two Python strings are passed as NULL-terminated char * arrays.
🌐
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.
🌐
Python
svn.python.org › projects › ctypes › trunk › ctypes › docs › manual › tutorial.html
ctypes tutorial
None, integers, longs, byte strings ... these function calls. None is passed as a C NULL pointer, byte strings and unicode strings are passed as pointer to the memory block that contains their data (char * or wchar_t *)....
Find elsewhere
Top answer
1 of 2
8

There are a number of problems:

  1. Define .argtypes for your functions. It will catch errors passing incorrect parameters. Add the line below and note it is plural and is a tuple of argument types. The comma makes a 1-tuple:

python_p_printme.argtypes = c_char_p,

  1. Once you make that change, you'll get an error because this code:

    c_sends = pointer(c_char_p(CHAR.encode('utf-8')

is actually sending a C char** (a pointer to a c_char_p). Once you've set the argtypes properly, you can just call the function with a byte string and it will work. Your function becomes:

def printmeP(CHAR):
    print("In Print function")
    print(CHAR)
    python_p_printme(CHAR.encode())
    print("DONE function - Python")
  1. There is one more subtle problem. While the program may appear to work at this point Python strings are immutable so if the function being called requires a mutable string, you must allocate a mutable buffer using either create_unicode_buffer (for c_wchar_p) or create_string_buffer (for c_char_p); otherwise, the strcat in your C code is going to corrupt the Python string.

Here's a full example:

test.cpp

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

// For Windows compatibility
#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

// For C++ compiler compatibility
#ifdef __cplusplus
extern "C" {
#endif

API void alter_me(char* param, size_t length) {
    // truncates if additional info exceeds length
    strncat_s(param, length, " adding some info", length - 1);
}

#ifdef __cplusplus
}
#endif

test.py

from ctypes import *

lib = CDLL('./test')
alter_me = lib.alter_me
alter_me.argtypes = c_char_p,c_size_t
alter_me.restype = None

data = create_string_buffer(b'test',size=10)
alter_me(data,sizeof(data))
print(data.value)

data = create_string_buffer(b'test',size=50)
alter_me(data,sizeof(data))
print(data.value)

Output:

b'test addi'
b'test adding some info'

Note you do not need to use create_string_buffer if the C function does not alter the buffer, such as if the C parameter is const char*. Then you could just call printme(b'test string').

2 of 2
2

You could use create_string_buffer.

The documentation can be found here: https://docs.python.org/3/library/ctypes.html#ctypes.create_string_buffer

ctypes.create_string_buffer(init_or_size, size=None)

This function creates a mutable character buffer. The returned object is a ctypes array of c_char.

init_or_size must be an integer which specifies the size of the array, or a bytes object which will be used to initialize the array items.

With buf.value.decode("utf-8") you can convert the buffer back to a UTF-8 python string.

A small example with your C code library might look like this:

from ctypes import *

mylib_lib = cdll.LoadLibrary("<your-lib>")
mylib_lib.printme.argtypes = c_char_p,

buf = create_string_buffer(128)
buf.value = b'Hello World'
mylib_lib.printme(buf)
print("print on python side:", buf.value.decode("utf-8"))

It would output:

Start c Function!
Hello World
...
print on python side: Hello World!

on the console.

🌐
Docutils
docutils.sourceforge.io › sandbox › docpy-writer › input › tutorial.txt
tutorial.txt
ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2 >>> As has been mentioned before, all Python types except integers, strings, and unicode strings have to be wrapped in their corresponding ``ctypes`` type, so that they can be converted to the required C data type:: >>> from ctypes import * >>> printf = cdll.msvcrt.printf >>> printf("An int %d, a double %f\n", 1234, c_double(3.14)) # doctest: +SKIP Integer 1234, double 3.1400001049 34 >>> Calling functions with your own custom data types ------------------------------------------------- You can also customize ``ctypes`` argument conversion to allow instances of your own classes be used as function arguments.
🌐
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.
🌐
Stack Overflow
stackoverflow.com › questions › 44640384 › python-ctypes-how-to-pass-string-and-array-arguments
excel - Python ctypes how to pass string and array arguments - Stack Overflow
June 19, 2017 - ByRef means an argument has to be passed by reference, i.e. the argument is the address of the value. Define some pointer types: c_long_p = ctypes.POINTER(ctypes.c_long); c_double_p = ctypes.POINTER(ctypes.c_double); c_float_p = ctypes.POINTER(ctypes.c_float) to use in the prototype readparameter.argtypes = (c_long_p, ctypes.c_char_p, c_double_p, c_double_p, c_long_p, c_float_p, c_float_p, c_long_p).
🌐
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. So string cannot change in python. that is problem when you use string as arguement. ... As you know, after some testing, there is nothing of changes. So In this time. I need a little marshalling work. i.e. We need to convert the original string bytes using str.encode, and then pass this to the constructor for a ctypes.string_buffer.String_buffers are Mutable.