Access data at memory address with ctypes
c, ctypes, pointers, python
Solution
Your C function returns `void`, not a `CONTEXT *`. So, your code is just casting random uninitialized memory to a `CONTEXT *`, and then trying to deref it.
On top of that, even if it did return a `CONTEXT` object by reference, `tmp[1]` would try to deref the memory after that object, so it would still be garbage.
When you try to interpret random memory as a double, you're going to get segfaults or values like `2.15880221124e-314` if you're lucky—if you're unlucky, you'll get correct-looking but still random values like `0.0`.
Meanwhile, since your C function modifies its arguments in place, you don't need to do anything fancy here. Just use the variables you passed in.
So:
def runner():
kk = (1,2,3)
n = 3
mm = n + 1
wts = (c_double * n)(1, 1, 1)
res = (c_double * mm)(0)
kks = (c_int * len(kk))(*kk)
n = c_int(n)
ex = c_double(0)
libc.cprogram.restype = None
libc.cprogram(wts, res, kks, n, ex)
print wts[1]
This works, and prints out `1.0`.
And if your C function did return an array of `CONTEXT` structs matching your `ctypes` declaration, this would all work fine. For example:
#include <stdlib.h>
typedef struct {
double *wts;
double res;
int kks;
int n;
double ex;
} CONTEXT;
CONTEXT *cprogram(double *wts, double *res, int *kks, int n, double *ex) {
int m;
m=n+1;
res[0]=1.0;
kks[0]=1.0;
CONTEXT *contexts = malloc(sizeof(CONTEXT) * 4);
for (int i=0; i!=4; ++i) {
double *wtsses = malloc(sizeof(double) * 5);
for (int j=0; j!=4; ++j) {
wtsses[j] = i + j;
}
CONTEXT context = { wtsses, *res, *kks, m, *ex };
contexts[i] = context;
}
return contexts;
}
Compile this, run your existing Python script with an added `print tmp[1].wts[1]`, and it will print out `2.0`.
Finally, for your followup, first let's change the C code to take `int *n`, where `*n` is the input:
void cprogram(double *wts, double *res, int *kks, int *n, double *ex) {
int m;
m=*n+1;
res[0]=1.0;
kks[0]=1.0;}
Now, to call this from Python, you have to create a `c_int` and pass a pointer to it. Since you're already doing the first half (which wasn't necessary before—just set `argtypes` instead… but it's necessary now, which is convenient), it's just a one-line change:
libc.cprogram(wts, res, kks, pointer(n), ex)
This even works if `n` is an in-out parameter.
But really, you don't need to see the pointer object at all from the Python; the only thing you're doing with it is creating it to pass to the function, then letting it get collected. To pass a C pointer without creating a ctypes pointer object (which again works even if `n` is an in-out parameter), use `byref`:
libc.cprogram(wts, res, kks, byref(n), ex)
Problem
I am trying to create a Python wrapper for an old C program that takes inputs as pointers. At this point, I can get the program to run but cannot figure out how to get back the values at the designated pointers. This is a simplified C script: ``` #include <stdlib.h> void cprogram(double *wts, double *res, int *kks, int n, double *ex) { int m; m=n+1; res[0]=1.0; kks[0]=1.0;} ``` And this is my simplified Python code: ``` from ctypes import * import sys libc = CDLL("src/program.so") class CONTEXT(Structure): _fields_ = [ ("wts", POINTER(c_double)), //tried just to see if it would work ("res", c_double), ("kks", c_int), ("n", c_int), ("ex", c_double)] def runner(): kk = (1,2,3) n = 3 mm = n + 1 wts = (c_double * n)(1, 1, 1) res = (c_double * mm)(0) kks = (c_int * len(kk))(*kk) n = c_int(n) ex = c_double(0) libc.cprogram.restype = POINTER(CONTEXT) tmp = libc.cprogram(wts, res, kks, n, ex) runner() ``` I have tried commands like `print tmp[1].wts[1]` and `print tmp[2]` but this only prints the memory address and not the value (or incredibly small values that are incorrect like 2.15880221124e-314). I would like to be able to return a list of the values of wts.