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.

Original source