Change

foo = POINTER(temp_foo)

to

foo = pointer(temp_foo)

can solve the problem.

Please see http://docs.python.org/library/ctypes.html#ctypes-pointers for more information.

Answer from czchen on Stack Overflow
Top answer
1 of 3
38

Here's a working example. It looks like you are passing the wrong type to the function and the structure was declared incorrectly for the Data member. Note below how to declare the type of a fixed-size array when declaring the Python structure.

test.c ("cl /W4 /LD .c" on Windows):

#include <stdio.h>

#ifdef _WIN32
#   define SMBUS_API __declspec(dllexport)
#else
#   define SMBUS_API
#endif

#define SMB_MAX_DATA_SIZE 5

typedef void* SMBUS_HANDLE;

typedef struct _SMB_REQUEST {
    unsigned char Address;
    unsigned char Command;
    unsigned char BlockLength;
    unsigned char Data[SMB_MAX_DATA_SIZE];
} SMB_REQUEST;

SMBUS_API int SmBusReadByte(SMBUS_HANDLE handle, SMB_REQUEST *request) {
    for(unsigned char i = 0; i < request->BlockLength; ++i)
        request->Data[i] = i;
    return request->BlockLength;
}

SMBUS_API SMBUS_HANDLE OpenSmbus(void) {
    return (void*)0x12345678;
}

test.py:

import ctypes as ct

SMB_MAX_DATA_SIZE = 5
ARRAY = ct.c_ubyte * SMB_MAX_DATA_SIZE

class SMB_REQUEST(ct.Structure):
    _fields_ = (('Address', ct.c_ubyte),
                ('Command', ct.c_ubyte),
                ('BlockLength', ct.c_ubyte),
                ('Data', ARRAY))

dll = ct.CDLL('./test')
smbus_read_byte = dll.SmBusReadByte
smbus_read_byte.argtypes = ct.c_void_p, ct.POINTER(SMB_REQUEST)
smbus_read_byte.restype = ct.c_int
open_smbus = dll.OpenSmbus
open_smbus.argtypes = ()
open_smbus.restype = ct.c_void_p

handle = open_smbus()
print(f'{handle = :08X}h')

smb_request = SMB_REQUEST(1, 2, 5)

print(f'returned = {smbus_read_byte(handle, smb_request)}')
print(f'Address = {smb_request.Address}')
print(f'Command = {smb_request.Command}')
print(f'BlockLength = {smb_request.BlockLength}')
for i, b in enumerate(smb_request.Data):
    print(f'Data[{i}] = {b:02X}h')

Output:

handle = 12345678h
returned = 5
Address = 1
Command = 2
BlockLength = 5
Data[0] = 00h
Data[1] = 01h
Data[2] = 02h
Data[3] = 03h
Data[4] = 04h
2 of 3
7

You're almost there. You should use c_char * SMB_MAX_DATA_SIZE as the type for the definition of Data. This works for me on Mac OS X:

Shared library:

$ cat test.c
#include <stdio.h>

#define SMB_MAX_DATA_SIZE 16

typedef struct _SMB_REQUEST
{
  unsigned char Address;
  unsigned char Command;
  unsigned char BlockLength;
  unsigned char Data[SMB_MAX_DATA_SIZE];
} SMB_REQUEST;

int SmBusReadByte(void *handle, SMB_REQUEST *request)
{
  printf("SmBusReadByte: handle=%p request=[%d %d %d %s]\n", handle, 
      request->Address, request->Command, request->BlockLength, request->Data);
  return 13;
}

$ gcc test.c -fPIC -shared -o libtest.dylib

Python driver:

$ cat test.py
import ctypes

SMB_MAX_DATA_SIZE = 16

class SMB_REQUEST(ctypes.Structure):
    _fields_ = [("Address", ctypes.c_ubyte),
                ("Command", ctypes.c_ubyte),
                ("BlockLength", ctypes.c_ubyte),
                ("Data", ctypes.c_char * SMB_MAX_DATA_SIZE)]

libtest = ctypes.cdll.LoadLibrary('libtest.dylib')

req = SMB_REQUEST(1, 2, 3, 'test')

result = libtest.SmBusReadByte(ctypes.c_voidp(0x12345678), ctypes.byref(req))

print 'result: %d' % result

$ python test.py
SmBusReadByte: handle=0x12345678 request=[1 2 3 test]
result: 13

UPDATE

You're having problems because you need to set the result type of open_smbus to void*. By default, ctypes assumes that functions return ints. You need to say this:

open_smbus.restype = ctypes.c_void_p

You were getting an error because you were using c_void_p() (note the extra parentheses). There's an important distinction between c_void_p and c_void_p(). The former is a type, and the latter is an instance of a type. c_void_p represents the C type void*, whereas c_void_p() represents an actual pointer instance (with a default value of 0).

🌐
SageMath
doc.sagemath.org › html › en › thematic_tutorials › numerical_sage › ctypes_examples.html
More complicated ctypes example - Thematic Tutorials
Note that in case you need to refer to a struct before it is completely defined (as in the linked list) you can first declare it with “Pass”, and then specify the field contents as above. Also note the POINTER operator which creates a pointer out of any ctypes type.
🌐
Python
docs.python.org › 3 › library › ctypes.html
ctypes — A foreign function library for Python
This means, if you have POINTER(c_int) in the argtypes list of a function or as the type of a member field in a structure definition, only instances of exactly the same type are accepted.
Top answer
1 of 2
7

Your definition of PicamCameraID structure is incorrect: sensor_name and serial_number are arrays:

"""
struct PicamCameraID {
  PicamModel             model;
  PicamComputerInterface computer_interface;
  pichar                 sensor_name[PicamStringSize_SensorName];
  pichar                 serial_number[PicamStringSize_SerialNumber]; 
};
"""
import ctypes as c

PicamStringSize_SensorName = PicamStringSize_SerialNumber = 64
PicamModel = PicamComputerInterface = c.c_int
pichar = c.c_char

class PicamCameraID(c.Structure):
    _fields_ = [("model", PicamModel),
                ("computer_interface", PicamComputerInterface),
                ("sensor_name", pichar * PicamStringSize_SensorName),
                ("serial_number", pichar * PicamStringSize_SerialNumber)]

It seems the second argument is just a string, so you don't need to apply pointer() to it. Here's the ctypes prototype for Picam_ConnectDemoCamera() function:

"""
Picam_ConnectDemoCamera(PicamModel model, const pichar* serial_number,
                        PicamCameraID* id)
"""
pichar_p = c.c_char_p # assume '\0'-terminated C strings
Picam_ConnectDemoCamera.argtypes = [PicamModel, pichar_p,
                                    c.POINTER(PicamCameraID)]
Picam_ConnectDemoCamera.restype = c.c_int # assume it returns C int

To call it:

picam_id = PicamCameraID()
rc = Picam_ConnectDemoCamera(2, "serial number here", c.byref(picam_id))
print(rc)
print(picam_id.sensor_name.value) # C string
print(picam_id.sensor_name.raw)   # raw memory
2 of 2
0

Likely, you're not entering the exact structure fields in the exact same order. If you don't, then you usually get random data or segfaults. To access a library with ctypes, you usually need to look inside the header (.h file) to copy the exact structure definition. Alternatively, use cffi with ffi.verify() if you don't want to depend on the precise layout.

Top answer
1 of 2
8

The problem is that the higher level "POINTER" from ctypes is, in Python, a different object than "a generic pointer" (ctypes.CArgObject by ctypes.byref)which is returned or a single number representing a memory address (which is what is returned by ctype's adrresof) - you can either annotate your function to receive a `ctypes.c_voidp and call it with _lib.mymethod(ctypes.addressof(a)) instead -

Or if you want to work on the stronged-typed side to avoid errors that would crash Python (a type error raises a Python exception instead - a wrong parameter passed to a C unction would cause a segmentation fault on the Python interpreter itself), you have to create a variable to hold the new "type" which is a POINTER to your structure - and then create an instance of this type with the address of your structure:

mystruct_pointer = ctypes.POINTER(mystruct)
_lib.mymethod.argtypes = (mystruct_pointer,)
_lib.mymethod.restype = ctypes.c_int

s = mystruct()

_lib.mymethod(mystruct_pointer.from_address(ctypes.addressof(s)))
2 of 2
6

(I know that this is an old question, but I think the accepted answer is an unnecessary workaround, so I want to leave this here for posterity.)

Actually ctypes should explicitly support using byref() to pass a pointer like that:

ctypes exports the byref() function which is used to pass parameters by reference. The same effect can be achieved with the pointer() function, although pointer() does a lot more work since it constructs a real pointer object, so it is faster to use byref() if you don’t need the pointer object in Python itself.

The likely cause of this is that you have defined your struct in more than one place (e.g. in different modules) - if the argtypes assignment sees one definition and the function call sees the other, this confusing error arises. In other words, ctypes tries to match two mystruct types that are (probably) identical in contents, and have the exact same name, but they are not the same type. As long as the base struct type is a single type object, it doesn't matter if you construct a pointer to it using pointer(), byref() or POINTER()() - ctypes will detect that the underlying (pointed-to) type is the same.

To verify if this is the case, try assert(_lib.mymethod.argtypes[0]._type_ == type(s)) right before calling the external function.

🌐
Samuelstevens
samuelstevens.me › writing › optimizing-python-code-with-ctypes
Optimizing Python Code with ctypes
All structs are classes that inherit ... has types like c_char (char), and c_char_p (*char). ctypes also includes POINTER() which creates a pointer type from any type passed to it....
Find elsewhere
Top answer
1 of 1
4

I think what you may be missing is knowing exactly where you want the struct memory to be allocated. The c code below provides a function that allocates memory for the struct and returns a pointer to it (new_struct()).

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

typedef struct {
    int a;
    int b;
} my_struct;

my_struct *new_struct()
{
    my_struct *struct_instance = (my_struct *)malloc(sizeof(my_struct));
    memset(struct_instance, 0, sizeof(my_struct));
    return struct_instance;
}

int modify_struct(my_struct *ms) {
    ms->a = 1;
    ms->b = 2;
    return 0;
}

void print_struct_c(my_struct *ms) {
    printf("my_struct {\n"
           "    a = %d\n"
           "    b = %d\n"
           "}\n", ms->a, ms->b);
}

From Python, to get the pointer, call the C function that does the allocation, then you can pass that to the other C functions that take it as a parameter.

import ctypes

lib_file_path = <<< path to lib file >>>

# Very simple example of how to declare a ctypes structure to twin the
# C library's declaration. This doesn't need to be declared if the Python
# code isn't going to need access to the struct's data members.
class MyStruct(ctypes.Structure):
    _fields_ = [('a', ctypes.c_int),
                ('b', ctypes.c_int)]

def print_struct(s):
    # Print struct that was allocated via Python ctypes.
    print("my_struct.a = %d, my_struct.b = %d" % (s.a, s.b))

def print_struct_ptr(sptr):
    # Print pointer to struct. Note the data members of the pointer are 
    # accessed via 'contents'.
    print("my_struct_ptr.contents.a = %d, my_struct_ptr.contents.b = %d" 
          % (sptr.contents.a, sptr.contents.b))


my_c_lib = ctypes.cdll.LoadLibrary(lib_file_path)

# If you don't need to access the struct's data members from Python, then 
# it's not necessary to declare MyStruct above. Also, in that case,
# 'restype' and 'argtypes' (below) can be set to ctypes.c_void_p instead.
my_c_lib.new_struct.restype     =  ctypes.POINTER(MyStruct)
my_c_lib.modify_struct.argtypes = [ctypes.POINTER(MyStruct)]

# Call C function to create struct instance.
my_struct_c_ptr = my_c_lib.new_struct()
print_struct_ptr(my_struct_c_ptr)

my_c_lib.modify_struct(my_struct_c_ptr)
print_struct_ptr(my_struct_c_ptr)

# Allocating struct instance from Python, then passing to C function.
my_struct_py = MyStruct(0, 0)
print_struct(my_struct_py)

my_c_lib.modify_struct(ctypes.byref(my_struct_py))
print_struct(my_struct_py)

# Data members of Python allocated struct can be acessed directly.
my_struct_py.a = 555

my_c_lib.print_struct_c(ctypes.byref(my_struct_py)) # Note use of 'byref()'
                                                    # to invoke c function.

The code above has been updated to include an example of how to allocate a struct instance via Python, and how to access the data members of either the C allocated or Python allocated struct (note differences in the print functions).

🌐
CodersLegacy
coderslegacy.com › home › structs with python ctypes
Structs with Python Ctypes - CodersLegacy
October 30, 2022 - But basically in ctypes, to access the object which a pointer points to, we use the .contents attribute on the pointer.
🌐
Post.Byes
post.bytes.com › home › forum › topic › python
ctypes: return a pointer to a struct - Post.Byes - Bytes
First define a struct type IP2LocationReco rd by subclassing from ctypes.Structur e. Then define a pointer type as ctypes.POINTER( IP2LocationReco rd) and set that as the function's restype attribute. See the ctypes tutorial or reference for details.
🌐
ProgramCreek
programcreek.com › python › example › 1122 › ctypes.Structure
Python Examples of ctypes.Structure
To avoid that, raise an exception if the window manager # connection is not available. from ctypes import cdll, c_int, pointer, Structure from ctypes.util import find_library app_services = cdll.LoadLibrary(find_library("ApplicationServices")) if app_services.CGMainDisplayID() == 0: _tk_unavailable = "cannot run without OS X window manager" else: class ProcessSerialNumber(Structure): _fields_ = [("highLongOfPSN", c_int), ("lowLongOfPSN", c_int)] psn = ProcessSerialNumber() psn_p = pointer(psn) if ( (app_services.GetCurrentProcess(psn_p) < 0) or (app_services.SetFrontProcess(psn_p) < 0) ): _tk_unavailable = "cannot run without OS X gui process" else: # not OS X import Tkinter try: Tkinter.Button() except Tkinter.TclError as msg: # assuming tk is not available _tk_unavailable = "tk not available: %s" % msg if _tk_unavailable: raise unittest.SkipTest(_tk_unavailable) return ·
🌐
Medium
medium.com › @datasciencefilmmaker › my-god-its-full-of-stars-2-d75d01d2cb94
My God, It’s Full of Stars (2/7) — Accessing C Structures in Python Using Ctypes | by Data Science Filmmaker | Medium
January 18, 2024 - It makes extensive use of structures and pointers to make accessing memory more efficient than the equivalent object-oriented version, and way more efficient than the Python equivalent would be. I wanted to take advantage of the speed of C while still using mostly Python syntax. Enter the “ctypes” library.
Top answer
1 of 1
2

You have at least four problems here (actually, five, but one isn't relevant).

The line that was (usually) causing your segfault is this:

varsdb = ct.cast(varsd, returnStruct_t)

This is because you're trying to cast a void * to a returnStruct_t, rather than to a returnStruct_t *. Since a returnStruct_t is much larger than a pointer, there's a good chance this will run off the end of an allocated page. Even if it doesn't segfault, it's giving you garbage values. It's equivalent to this C code:

returnStruct_t varsdb = *(returnStruct_t *)(&varsd);

What you wanted was the equivalent of:

returnStruct_t *varsdb = (returnStruct_t *)(varsd);

In other words:

varsdb = ct.cast(varsd, returnStructPointer)

After fixing that, I often, but not always, still get a segfault later, on trying to access varsdb.contents.vars[0].x[0][0] (varsdb.contents.vars[0].x[0] itself is always safe).

The next problem is that you haven't defined your struct properly. Here's the C:

typedef struct testStruct {
  double *x[11];
  double *u[10];
} Test;

And here's the Python:

class vars_t(ct.Structure):
    _fields_ = [("u", ct.POINTER(ct.c_double*10)),
                    ("x", ct.POINTER(ct.c_double*11))]

You've mixed up u and x. So, what you're calling x, and treating as an array of 11 doubles, is actually u, an array of 10 doubles. So every time you touch x[10], you're going past the end of the array.


After fixing that one too, I get garbage values printed out. With the clang build, it's always close to 6.92987533417e-310.

I think this one is purely in the C code. I often get garbage numbers printed out from those x[10][4] and u[9][2] lines in C. Again, with the same build, I get about an equal mix of reasonable values, numbers like 26815615859885194199148049996411692254958731641184786755447122887443528060147093953603748596333806855380063716372972101707507765623893139892867298012168192.000000 for both, and reasonable values for the former but nan for the latter.

And when I run a simple C driver under valgrind, every fourth fprintf I get this:

==85323== Use of uninitialised value of size 8

So you've presumably got an off-by-one error in your allocation or initialization code in C, and you sometimes but not always get away with it.


Also, these aren't the same type:

typedef struct returnStruct_t {
  Test* vars;
} ReturnStruct;

class returnStruct_t(ct.Structure):
    _fields_ = [("vars", vars_t*numOpt)]

The former is a single pointer long, and that pointer points at an array of Test objects. The latter is 3 Test objects. So, once again, you're trying to read a pointer to something as a value of that type, and here you're going way past the end of the allocated value.

After fixing that, I no longer get any crashes, and I get reasonable final values like 80.0 even when I get the garbage printouts along the way. But of course I still get those garbage printouts along the way, and valgrind is still complaining, so obviously this still isn't the last problem.


You've also got an obvious leak in your code—which isn't directly relevant to the problem, but it's a good sign that you've probably got similar errors elsewhere. You allocate the x arrays like this:

for(x = 0; x < 11; x++)
  varsStruct[i].x[x] = (double *)malloc(sizeof(double)*5);    

… then free them like this:

for(x = 1; x < 11; x++) 
  free(returnStruct->vars[i].x[x]);

So x[0] never gets freed.

🌐
Terryoy
terryoy.github.io › 2014 › 03 › using-ctypes-to-wrap-c-library.html
Use Ctypes to Wrap C Libraries in Python - Hello, World!
# it's a good practice to declare the structures and the functions at first like in C # ... # declare Structures class Context(Structure): pass ContextPtr = ctypes.POINTER(Context) # ...
🌐
tentacles666
tentacles666.wordpress.com › 2012 › 01 › 21 › python-ctypes-dereferencing-a-pointer-to-a-c
Python ctypes: dereferencing a pointer to a C structure | tentacles666
March 19, 2013 - class Foo (ctypes.Structure): _fields_ = [ ("bar", c_int) ] Next, you need to tell ctypes that newfoo() returns a pointer to such a thing: