Jacob Marble
Jacob Marble

Reputation: 30142

How to handle array of strings (char **) in ctypes in a 64-bit environment?

I'm using ctypes to work with libgphoto2 in Python. The following code succeeds on a 32-bit machine, but fails with a Segmentation Fault on a 64-bit machine (Linux, Ubuntu):

import ctypes

gp = ctypes.CDLL('libgphoto2.so')
a = gp.gp_library_version(0)
x = ctypes.c_char_p.from_address(a)
x.value

libphoto2, ctypes and python are all from repositories, so I assume that the problem is in my code; I need to consider pointers in a more general way or something like that. gp_library_version() returns const char **, an array of string constants. Other gp_* functions work fine.

Upvotes: 1

Views: 2065

Answers (2)

jsbueno
jsbueno

Reputation: 110440

If you don't annotate anything about functions in ctypes, it is assumed that they return 32bit integers. In 32 bit processes, these integers and pointers are mostly interchangeable - not so for 64 bit builds.

Just doing

gp.gp_library_version.restype = ctypes.c_void_p

before your call should suffice in this case (You will still need the x = ctypes.c_char_p.from_address(a) line).

Check the API docs on wether you should free this pointer yourself after yu use it (probably yes in this case).

Upvotes: 1

Adam Rosenfield
Adam Rosenfield

Reputation: 400414

The problem is that, by default, ctypes assumes that all functions return int, which is 32 bits. But, on a 64-bit system, a pointer is 64 bits, so you're dropping the upper 32 bits from the pointer return value, so you then get a segfault when you try to dereference that invalid pointer.

You can change the expected return type of a function by assigning a type to its restype attribute like so:

gp = ctypes.CDLL('libgphoto2.so')
gp.gp_library_version.restype = ctypes.POINTER(ctypes.c_char_p)
a = gp.gp_library_version(0)
print a[0].value

Upvotes: 3

Related Questions