The following code works:

test.py:

import ctypes
lib = ctypes.CDLL("./libtest.so")
string_buffers = [ctypes.create_string_buffer(8) for i in range(4)]
pointers = (ctypes.c_char_p*4)(*map(ctypes.addressof, string_buffers))
lib.test(pointers)
results = [s.value for s in string_buffers]
print results

test.c (compiled to libtest.so with gcc test.c -o libtest.so -shared -fPIC):

#include <string.h>
void test(char **strings) {
    strcpy(strings[0],"this");
    strcpy(strings[1],"is");
    strcpy(strings[2],"a");
    strcpy(strings[3],"test!");
}

As Aya said, you should make sure there is room for the terminating zero. But I think your main problem was that the string buffer was garbage collected or something similar, as there was no direct reference to it anymore. Or something else is causing trouble in the creation process of the string buffers when no references are stored for them. For example this results in four times the same address instead of different addresses:

import ctypes
pointers = [ctypes.addressof(ctypes.create_string_buffer(8)) for i in range(4)]
print pointers
Answer from Florian Rhiem on Stack Overflow
Top answer
1 of 3
12

The following code works:

test.py:

import ctypes
lib = ctypes.CDLL("./libtest.so")
string_buffers = [ctypes.create_string_buffer(8) for i in range(4)]
pointers = (ctypes.c_char_p*4)(*map(ctypes.addressof, string_buffers))
lib.test(pointers)
results = [s.value for s in string_buffers]
print results

test.c (compiled to libtest.so with gcc test.c -o libtest.so -shared -fPIC):

#include <string.h>
void test(char **strings) {
    strcpy(strings[0],"this");
    strcpy(strings[1],"is");
    strcpy(strings[2],"a");
    strcpy(strings[3],"test!");
}

As Aya said, you should make sure there is room for the terminating zero. But I think your main problem was that the string buffer was garbage collected or something similar, as there was no direct reference to it anymore. Or something else is causing trouble in the creation process of the string buffers when no references are stored for them. For example this results in four times the same address instead of different addresses:

import ctypes
pointers = [ctypes.addressof(ctypes.create_string_buffer(8)) for i in range(4)]
print pointers
2 of 3
4

In this line:

results = (c_char_p * 4)(addressof(create_string_buffer(7)))

You're creating a single buffer of 7 bytes, then trying to use it to hold 4 character pointers (which are probably 4 bytes each), and then also copying 4 8-byte strings into the random addresses it might happen to point to. You need to allocate a buffer for each string, and also allocate the array of pointers. Something like this:

s = []
for i in range(4):
    s[i] = create_string_buffer(8)
results = (c_char_p * 4)(s);
🌐
Python
docs.python.org › 3 › library › ctypes.html
ctypes — A foreign function library for Python
This function creates a mutable character buffer. The returned object is a ctypes array of c_char.
Discussions

Dealing with char*** in ctypes
I have this function in C library that I want to access from python using ctypes: static const char *names[] = {"Backplane", "ADC", "External", "Reserved"}; int test_char_ptr(const char ***names_, int *count_) { *names_ = names; *count_ = 4; return 0; } I can then do this in C app: const char ... More on discuss.python.org
🌐 discuss.python.org
3
0
December 13, 2023
pointers - Python ctypes: How to modify an existing char* array - Stack Overflow
I'm working on a Python application that makes use of libupnp which is a C library. I'm using CTypes to use the library which is easy enough. The problem I'm having is when I'm registering a callback More on stackoverflow.com
🌐 stackoverflow.com
July 22, 2016
Multi-dimensional char array (array of strings) in python ctypes - Stack Overflow
I'm trying to pass an array of character arrays to a C function using ctypes. More on stackoverflow.com
🌐 stackoverflow.com
python - How to produce a character array in ctypes and pass its pointer as a function argument - Stack Overflow
I would like to pass function a character buffer buf from Python ctypes. This character buffer originally appears in Python as a list of characters, so I first convert it to a C character array using More on stackoverflow.com
🌐 stackoverflow.com
January 31, 2019
🌐
NumPy
numpy.org › doc › stable › reference › generated › numpy.char.chararray.ctypes.html
numpy.char.chararray.ctypes — NumPy v2.4 Manual
This attribute creates an object that makes it easier to use arrays when calling shared libraries with the ctypes module.
🌐
NumPy
numpy.org › devdocs › reference › generated › numpy.char.chararray.ctypes.html
numpy.char.chararray.ctypes — NumPy v2.5.dev0 Manual
This attribute creates an object that makes it easier to use arrays when calling shared libraries with the ctypes module.
🌐
ProgramCreek
programcreek.com › python › example › 3579 › ctypes.c_char
Python Examples of ctypes.c_char
Args: pointer : int or ctypes pointer address of a buffer count : int Number of items to read. dtype : data-type, optional Data-type of the returned array; default: float. Examples: >>> s = numpy.ones(3, dtype=numpy.int32) >>> ptr = s.ctypes.data >>> frompointer(ptr, count=6, dtype=numpy.int16) [1, 0, 1, 0, 1, 0] ''' dtype = numpy.dtype(dtype) count *= dtype.itemsize buf = (ctypes.c_char * count).from_address(pointer) a = numpy.ndarray(count, dtype=numpy.int8, buffer=buf) return a.view(dtype) Example #7 ·
🌐
GitHub
gist.github.com › nzjrs › 990493
Python ctypes example · GitHub
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters ... You can define a function which calls malloc() in your shared object and call it through ctypes.
🌐
University of Texas at Austin
het.as.utexas.edu › HET › Software › Numpy › reference › generated › numpy.chararray.ctypes.html
numpy.chararray.ctypes — NumPy v1.9 Manual
This attribute creates an object that makes it easier to use arrays when calling shared libraries with the ctypes module.
🌐
LinkedIn
linkedin.com › pulse › calling-c-from-python-using-ctypes-warren-jitsing
Calling C from Python Using Ctypes
August 27, 2021 - We create mutable character buffers using the ctypes.create_string_buffer. This takes an initial bytes object and length of the buffer, or just a length argument. When just a length argument is given, the buffer is initialized with null bytes. >>> in_buffer: ctypes.Array[ctypes.c_char] = ctypes.create_string_buffer(b"Hello World!") >>> out_buffer: ctypes.Array[ctypes.c_char] = ctypes.create_string_buffer(20) >>> in_buffer.raw b'Hello World!\x00' >>> out_buffer.raw b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' We can now run the function and print the results ·
Find elsewhere
🌐
Bytes
bytes.com › topic › python › answers › 837648-ctypes-get-full-contents-character-array
ctypes: Get full contents of character array - Python
December 12, 2008 - Your requests are coming too fast. Please slow down your access rate to continue browsing our site
🌐
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.
🌐
SciPy
docs.scipy.org › doc › › numpy-1.9.2 › reference › generated › numpy.chararray.ctypes.html
chararray.ctypes - Numpy and Scipy Documentation
Standard array subclasses · numpy.chararray · index · next · previous · chararray.ctypes¶ · An object to simplify the interaction of the array with the ctypes module. This attribute creates an object that makes it easier to use arrays when calling shared libraries with the ctypes module.
🌐
NumPy
numpy.org › doc › 2.1 › reference › generated › numpy.char.chararray.ctypes.html
numpy.char.chararray.ctypes — NumPy v2.1 Manual
This attribute creates an object that makes it easier to use arrays when calling shared libraries with the ctypes module.
🌐
Python.org
discuss.python.org › python help
Dealing with char*** in ctypes - Python Help - Discussions on Python.org
December 13, 2023 - I have this function in C library that I want to access from python using ctypes: static const char *names[] = {"Backplane", "ADC", "External", "Reserved"}; int test_char_ptr(const char ***names_, int *count_) { *names_ = names; *count_ = 4; return 0; } I can then do this in C app: const char **items; unsigned nr_items; result = test_char_ptr(&items, &nr_items); for (unsigned i = 0; i
🌐
Reddit
reddit.com › r/learnpython › help with ctypes arrays of pointers.
r/learnpython on Reddit: Help with ctypes arrays of pointers.
July 25, 2012 -

Hello. I am trying to translate the following c++ code into python using ctypes. char* adcArray = new char[length]; (it really is done using a data type called ViInt8 which is a byte). I have tried the following

1

adcArrayType = ctypes.c_byte * length
adcArray = adcArrayType()

2

adcArrayType = ctypes.POINTER(ctypes.c_byte) * length
adcArray = adcArrayType()

3

adcArray = [ctypes.c_byte] * length

and a few other and cant seem to get it right. The function takes in adcArray as one of its arguments and fills it with length number of 8 bit values. I then need to be able to go through and look at each byte individually and print it to a file. I have been able to get the c++ function to take in the value using style number 2 but it doesnt seem to change the data. I also have gotten it to work another way but I was not able to see the data that may have been stored. Any help is greatly appreciated. Thanks

🌐
Google Groups
groups.google.com › g › comp.lang.python › c › Ggmvia6XRj8
ctypes return char array with null chars
>>> x=(c_char*10)() >>> x <__main__.c_char_Array_10 object at 0x00A049E0> >>> x.raw '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' >>> x.value ''
Top answer
1 of 2
19

Mark's answer is quite helpful in that it passes a character array to the C function, which is what the OP really wanted, but in case there's folks finding their way here who really want to pass a byte-array, an approach seems to be to build a ctypes.c_char backed by the memory of your byte-array, and pass that.

My example here disregards the argument declaration recommend by Mark, which indeed seems like a good idea.

import ctypes

# libFoo.c:
# (don't forget to use extern "C" if this is a .cpp file)
#
# void foo(unsigned char* buf, size_t bufSize) {
#   for (size_t n = 0; n < bufSize; ++n) {
#     buf[n] = n;
#   }
# }

fooLib = ctypes.cdll.LoadLibrary('./lib/libFoo.dylib')

ba = bytearray(10)

char_array = ctypes.c_char * len(ba)

fooLib.foo(char_array.from_buffer(ba), len(ba))

for b in ba:
  print b

# 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
2 of 2
6

The minimum you need (Python 3) is:

hello = b"hello"
encryptPy.encrypt(5, hello)
encryptPy.decrypt(5, hello)

But it is best practice to declare the argument types and return values as well so ctypes can perform argument type checking. Full program:

#!python3
import ctypes as ct

encryptPy = ct.CDLL('/home/aradhak/Documents/libencrypt.so')    
encryptPy.encrypt.argtypes ct.c_int, ct.c_char_p
encryptPy.encrypt.restype = None
encryptPy.decrypt.argtypes = ct.c_int, ct.c_char_p
encryptPy.decrypt.restype = None

hello = b"hello"
encryptPy.encrypt(len(hello), hello)
encryptPy.decrypt(len(hello), hello)

Note that when passing a Python byte string, consider the buffer immutable. In this case you only are reading the buffer, but if you need to allow the C function to mutate the string use:

hello = ct.create_string_buffer(b'hello', 5)

Below works as well, but will be length 6. A terminating null will be included.

hello = ct.create_string_buffer(b'hello')
🌐
Post.Byes
post.bytes.com › home › forum › topic › python
ctypes: Get full contents of character array - Post.Byes - Bytes
September 12, 2008 - _fields_ = [...("array", c_char * 12)...] however, ctypes seems to try to return struct.array as a Python string rather than a character array, and stops as soon as it encounters a null within the character array. > I ended up having to define a dummy struct class dummystruct(Str ucture): _fields_ = [] > and declare array as: ("array", dummystruct) > then use string_at(byref (struct.array), 12).