使用ctypes传递结构体指针
到目前为止,我做了一个小的 ctypes 和 Python 代码,具体步骤如下:
Python 调用一个 C 语言的函数,并传入一个指向空指针的指针作为参数。
C 代码创建了一个名为
ReturnStruct
的结构体,并实例化它和它的数据成员,然后将 Python 传入的指针指向这个结构体。Python 多次调用另一个 C 函数来增加一些值。
Python 然后检查这些值。
最后,Python 调用一个 C 函数来释放结构体的指针。
到目前为止,我已经完成了前面三个步骤,但在最后两个部分遇到了问题。以下是 C 代码:
#include <stdio.h>
#include <stdlib.h>
//#include "runSolver.h"
#define SMB_MAX_DATA_SIZE 16
typedef struct testStruct {
double *x[11];
double *u[10];
} Test;
typedef struct returnStruct_t {
Test* vars;
} ReturnStruct;
void initalize_returnStruct(void** returnStruct){
ReturnStruct* new_returnStruct = (ReturnStruct *)malloc(sizeof(ReturnStruct));
Test* varsStruct = (Test*)malloc(sizeof(Test)*3);
int dataSize = 5;
int i;
for(i = 0; i < 3; i++){
int x;
for(x = 0; x < 11; x++)
varsStruct[i].x[x] = (double *)malloc(sizeof(double)*5);
for(x = 0; x < 10; x++)
varsStruct[i].u[x] = (double *)malloc(sizeof(double)*5);
}
new_returnStruct->vars = varsStruct;
*returnStruct = new_returnStruct;
}
void free_returnStruct(void* data){
ReturnStruct* returnStruct = data;
int i;
for(i = 0; i < 3; i++){
int x;
for(x = 1; x < 11; x++)
free(returnStruct->vars[i].x[x]);
for(x = 0; x < 10; x++)
free(returnStruct->vars[i].u[x]);
}
free(returnStruct->vars);
free(returnStruct);
}
void parallelSolver(void* data){
ReturnStruct* VarsArray = data;
fprintf(stderr, " This is the value: %f \n", VarsArray->vars[0].x[0][0]);
fprintf(stderr, " This is the value: %f \n", VarsArray->vars[0].x[10][4]);
fprintf(stderr, " This is the value: %f \n", VarsArray->vars[0].u[0][0]);
fprintf(stderr, " This is the value: %f \n", VarsArray->vars[0].u[9][2]);
VarsArray->vars[0].x[0][0] += 20.0;
VarsArray->vars[0].x[10][4] += 203.0;
VarsArray->vars[0].u[0][0] += 202.0;
VarsArray->vars[0].u[9][2] += 202.0;
}
这是 Python 代码:
#!/usr/bin/python
import sys
import ctypes as ct
numOpt = 3
class vars_t(ct.Structure):
_fields_ = [("u", ct.POINTER(ct.c_double*10)),
("x", ct.POINTER(ct.c_double*11))]
class returnStruct_t(ct.Structure):
_fields_ = [("vars", vars_t*numOpt)]
runSolver = ct.CDLL('./runSolverParallel.so')
returnStructPointer = ct.POINTER(returnStruct_t)
runSolver.parallelSolver.argtypes = [ct.c_void_p()]
varsd = ct.c_void_p()
runSolver.initalize_returnStruct(ct.byref(varsd))
runSolver.parallelSolver(varsd)
runSolver.parallelSolver(varsd)
runSolver.parallelSolver(varsd)
runSolver.parallelSolver(varsd)
varsdb = ct.cast(varsd, returnStruct_t)
print(varsdb.contents.vars[0].x[0][0])
runSolver.free_returnStruct(varsd)
代码运行得很好,直到我到达这三行:
varsdb = ct.cast(varsd, returnStruct_t)
print(varsdb.contents.vars[0].x[0][0])
runSolver.free_returnStruct(varsd)
这些行都导致了段错误(seg faults)。如果有人能给我一些建议,让这个代码正常工作,我将非常感激!
错误信息看起来是这样的:
Starting program: /usr/bin/python UserDefinedCode.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
This is the value: 0.000000
This is the value: 0.000000
This is the value: 0.000000
This is the value: 0.000000
This is the value: 20.000000
This is the value: 203.000000
This is the value: 202.000000
This is the value: 202.000000
This is the value: 40.000000
This is the value: 406.000000
This is the value: 404.000000
This is the value: 404.000000
This is the value: 60.000000
This is the value: 609.000000
This is the value: 606.000000
This is the value: 606.000000
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff33795d4 in ?? () from /usr/lib/python2.7/lib-dynload/_ctypes.so
(gdb) where
#0 0x00007ffff33795d4 in ?? () from /usr/lib/python2.7/lib-dynload/_ctypes.so
#1 0x00007ffff3386ea4 in ffi_call_unix64 () from /usr/lib/python2.7/lib-dynload/_ctypes.so
#2 0x00007ffff33868c5 in ffi_call () from /usr/lib/python2.7/lib-dynload/_ctypes.so
#3 0x00007ffff33772c2 in _ctypes_callproc () from /usr/lib/python2.7/lib-dynload/_ctypes.so
#4 0x00007ffff3377aa2 in ?? () from /usr/lib/python2.7/lib-dynload/_ctypes.so
#5 0x00000000004d91b6 in PyObject_Call ()
#6 0x000000000054c0da in PyEval_EvalFrameEx ()
#7 0x000000000054c272 in PyEval_EvalFrameEx ()
#8 0x0000000000575d92 in PyEval_EvalCodeEx ()
#9 0x00000000004c1352 in PyRun_SimpleFileExFlags ()
#10 0x00000000004c754f in Py_Main ()
#11 0x00007ffff68cb76d in __libc_start_main (main=0x41ba10 <main>, argc=2, ubp_av=0x7fffffffe1d8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe1c8)
at libc-start.c:226
#12 0x000000000041ba41 in _start ()
1 个回答
这里你至少有四个问题(其实有五个,但有一个不重要)。
导致你程序崩溃的那一行通常是:
varsdb = ct.cast(varsd, returnStruct_t)
这是因为你试图把一个 void *
类型转换成 returnStruct_t
,而不是 returnStruct_t *
。由于 returnStruct_t
的大小远大于一个指针,这样做很可能会超出分配的内存范围。即使没有崩溃,它也会给你一些无效的值。这就相当于下面这段 C 代码:
returnStruct_t varsdb = *(returnStruct_t *)(&varsd);
你想要的其实是:
returnStruct_t *varsdb = (returnStruct_t *)(varsd);
换句话说:
varsdb = ct.cast(varsd, returnStructPointer)
修正这个问题后,我经常还是会在尝试访问 varsdb.contents.vars[0].x[0][0]
时遇到崩溃(不过 varsdb.contents.vars[0].x[0]
本身是安全的)。
下一个问题是你没有正确地定义你的结构体。下面是 C 代码:
typedef struct testStruct {
double *x[11];
double *u[10];
} Test;
这是 Python 代码:
class vars_t(ct.Structure):
_fields_ = [("u", ct.POINTER(ct.c_double*10)),
("x", ct.POINTER(ct.c_double*11))]
你把 u
和 x
搞混了。所以你所称的 x
,并把它当作一个包含 11 个 double 的数组,实际上是 u
,它是一个包含 10 个 double 的数组。因此每次你访问 x[10] 时,都是在越界。
修正这个问题后,我打印出来的值还是一堆无效值。用 clang
编译时,结果总是接近 6.92987533417e-310
。
我觉得这个问题完全出在 C 代码里。我经常在 C 代码中的 x[10][4]
和 u[9][2]
行打印出无效的数字。再次使用同样的编译,我得到的合理值和无效值的比例差不多,比如 26815615859885194199148049996411692254958731641184786755447122887443528060147093953603748596333806855380063716372972101707507765623893139892867298012168192.000000
,前者有合理值,而后者却是 nan
。
当我在 valgrind
下运行一个简单的 C 驱动程序时,每四次 fprintf
就会出现一次这个:
==85323== Use of uninitialised value of size 8
所以你可能在 C 代码的分配或初始化中有一个越界错误,有时你能侥幸逃过,但并不总是。
另外,这两者的类型也不一样:
typedef struct returnStruct_t {
Test* vars;
} ReturnStruct;
class returnStruct_t(ct.Structure):
_fields_ = [("vars", vars_t*numOpt)]
前者是一个指向 Test
对象数组的单指针,而后者是 3 个 Test
对象。所以,你又一次试图把一个指针当作该类型的值来读取,这样做会超出分配的内存范围。
修正这个问题后,我不再遇到崩溃,最终得到的合理值像 80.0
,即使在过程中打印出无效值。但当然,我在过程中还是会看到那些无效的打印,valgrind
也仍在抱怨,所以显然这还不是最后一个问题。
你的代码中还有一个明显的内存泄漏——虽然这与当前问题没有直接关系,但这表明你可能在其他地方也有类似的错误。你是这样分配 x
数组的:
for(x = 0; x < 11; x++)
varsStruct[i].x[x] = (double *)malloc(sizeof(double)*5);
… 然后这样释放它们:
for(x = 1; x < 11; x++)
free(returnStruct->vars[i].x[x]);
所以 x[0]
从未被释放。