1

I have a shared library with a function that takes a int ** like this:

void printarray(int **array, int n, int m)
{
        int i, j;
        for (i = 0; i < m; i++)
                for (j = 0; j < n; j++)
                        printf("%d\n", array[i][j]);
}

I would like to call the function from Python withctypes. I have written the following to do this:

from ctypes import *

cdll.LoadLibrary("libssa.so")
libssa = CDLL("libssa.so")

a = [[1, 2, 3], [4, 5, 6]]
 
T = ((c_int * 3) * 2)
array = T()
for i in range(2):
    for j in range(3):
        array[i][j] = a[i][j]

libssa.printarray(array, c_int(3), c_int(2))

I expected that the type T would be an array of pointers to arrays of ints, and that array would be a pointer to an object of that type.

However, a segmentation fault occurs whenever an access to array is made in the C code. In particular, for this example, valgrind points to this line: printf("%d\n", array[i][j]); as the source of the segfault.

The question is, what is the proper way to construct a ctypes object that can be used as an argument of type int **?

1
  • "I expected that the type T would be an array of pointers to arrays of ints, and that array would be a pointer to an object of that type." It is, but just because you have pointers to arrays of ints, doesn't mean those pointers to arrays of ints actually have arrays of ints that they're pointing at. But regardless, a multidimensional array works differently from that in C. Commented Jul 16, 2020 at 20:58

2 Answers 2

2

Be aware that int** is not equal to int[][] (see this post). This is due to the internal structure of an multidimensional array: It could be represented as

                      +-------------+
array ->  array[0] -> | array[0][0] |
                      +-------------+
                      | array[0][1] |
                      +-------------+
                      |     ...     |
                      +-------------+
          array[1] -> | array[1][0] |
                      +-------------+
                            ...      

where each square shows how the corresponding value can be accessed. But int** would be a structure like

         +------+      +-----+
int** -> | int* | ---> | int |
         +------+      +-----+
         | int* | -\   | int |
         +------+  |   +-----+
           ...     |   | ... |
                   |   +-----+
                   |
                   |   +-----+
                   \-> | int |
                       +-----+
                         ... 

Each square should represent a memory location, with the type in it. In contrast to Python, the size of the array is not saved in c, so access with array[i][j] fails when the pointer was passed as an parameter. Better use a one-dimensional array and calculate the second dimension yourself, so

void printarray(int *array, int n, int m)
{
        int i, j;
        for (i = 0; i < m; i++)
                for (j = 0; j < n; j++)
                        printf("%d\n", array[i + j*m]);
}

or array[j + i*n], depending on your implementation.

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

1 Comment

The problem isn't just that sizes aren't tracked intrinsically; the problem in OP's code is that the int*s in the main array don't have allocations of their own to point to.
0

An array of pointers is different than a two-dimensional array. Here's what would call your function.

DLL code (modified for Windows and m/n in for loops were backward):

#include <stdio.h>

__declspec(dllexport)
void printarray(int **array, int n, int m)
{
        int i, j;
        for (i = 0; i < n; i++)
                for (j = 0; j < m; j++)
                        printf("array[%d][%d] = %d\n", i, j, array[i][j]);
}

test.py:

from ctypes import *

dll = CDLL('./test')
dll.printarray.argtypes = POINTER(POINTER(c_int)),c_int,c_int
dll.printarray.restype = None

def wrap(a):
    x = len(a)
    y = len(a[0])
    arr = (POINTER(c_int) * x)()  # allocate array of pointers in X dimension
    for i in range(x):
        arr[i] = (c_int * y)(*a[i]) # allocate array in Y dimension and populate it
    return arr,x,y

a = [[1,2,3],[4,5,6]]

dll.printarray(*wrap(a))

Output:

array[0][0] = 1
array[0][1] = 2
array[0][2] = 3
array[1][0] = 4
array[1][1] = 5
array[1][2] = 6

Comments

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.