使用ctypes传递struct指针

时间:2014-08-29 21:42:09

标签: python c ctypes

所以我到目前为止所做的是构建一个小的ctypes和python代码,它执行以下操作:

  • Python使用指向void指针的指针作为参数调用C函数。

  • C代码创建一个类型ReturnStruct的结构并实例化它,然后它的数据成员将python传入的指针设置为指向此结构。

  • Python多次调用另一个C函数来增加某些值。

  • Python然后检查值。

  • Python调用C函数来解除分配结构指针。

到目前为止,我已经完成了前3个阶段的工作,但我在最后两个部分遇到了问题。这是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错误。关于如何正常工作的任何建议将不胜感激!

错误如下所示:

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 个答案:

答案 0 :(得分:2)

这里至少有四个问题(实际上,五个,但一个不相关)。

导致你的段错误的那条线是这样的:

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))]

你混淆了ux。所以,你所谓的x,并且被视为11个双打的数组,实际上是u,一个10个双打的数组。因此,每当你触摸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]永远不会被释放。