6

I'm having difficulty passing string from Python to C by ctypes:

My C code(compiled into a hello.so)

...
typedef struct test{
    unsigned int    a;
    unsigned char*  b;
} my_struct;

int hello(my_struct *in) {
    FILE *fp;
    ...
    fprintf(fp, "%d\t%s\n", in->a, in->b);
    fclose(fp);
    return 0;
}

My Python code:

...
from ctypes import *
...
class TEST_STRUCT(Structure):
    _fields_ = [("a", c_int),
                ("b", c_char_p)]
...
    hello_lib = ctypes.cdll.LoadLibrary("hello.so")
    hello = hello_lib.hello
    hello.argtypes = [POINTER(TEST_STRUCT)]
    name = create_string_buffer(b"test")
    hello_args = TEST_STRUCT(1, name)
    hello(ctypes.byref(hello_args))
...

I get the error: hello_args = TEST_STRUCT(1, name) TypeError: expected string, c_char_Array_5 found

I tried to change c_char_p to c_wchar_p or c_char*5 or c_wchar*5 etc. Sometimes it can run without error, the first int parameter of the struct can be printed correctly, but not the second string pointer, the best I can get is just the first character 't' instead of the whole word "test".

BTW, my python3 version is 3.3.0

3
  • I don't know anything about sending data between C and Python, but what happens when you assign name as name="test"? Commented Jul 1, 2016 at 19:11
  • If I do Python just as this: hello_lib = ctypes.cdll.LoadLibrary("hello.so") hello = hello_lib.hello hello.argtypes = [POINTER(TEST_STRUCT)] #name = create_string_buffer(b"test") name = "test" hello_args = TEST_STRUCT(1, name) hello(ctypes.byref(hello_args)) I get: TypeError: bytes or integer address expected instead of str instance Commented Jul 1, 2016 at 19:21
  • I wonder if that refers to a bytes object name = bytes("name", 'utf-8') Commented Jul 1, 2016 at 19:31

1 Answer 1

1

The solution depends on whether you need the data type to be mutable (from Python's perspective).

If don't need the data to be mutable, then don't bother with the buffer constructor. Just pass the bytes object direct to the TEST_STRUCT constructor. eg.

from ctypes import *

class TEST_STRUCT(Structure):
    _fields_ = [("a", c_int),
                ("b", c_char_p)]

hello_args = TEST_STRUCT(1, b"test")

If you need a mutable buffer then you need to specify your char* type slightly differently in the TEST_STRUCT class. eg.

from ctypes import *

class TEST_STRUCT(Structure):
    _fields_ = [("a", c_int),
                ("b", POINTER(c_char))] # rather than c_char_p

name = create_string_buffer(b"test")
hello_args = TEST_STRUCT(1, name)

c_char_p versus POINTER(c_char)

Ostensibly, whilst these two appear similar they are subtly different. POINTER(c_char) is for any buffer of char data. c_char_p is specifically for NULL terminated strings. As a side effect of this condition all c_char_p objects are immutable (on the python side) so as to make this guarantee easier to enforce. eg. {65, 66, 67, 0} is a string ("ABC"), whereas {1, 2, 3} would not be.

Sign up to request clarification or add additional context in comments.

2 Comments

Could you explain the difference between c_char_p and POINTER(c_char)? I thought they were the same thing.
Just added an edit to clarify. The difference isn't immediately obvious and isn't really well spelt out by the documentation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.