指针作为扩展类型中的属性 cython



我是cython的新手,对C有一些了解和一些python经验。目前我正在尝试了解扩展类型,但我无法理解以下示例中的指针值会发生什么(说明下方的代码)。

作为练习,我正在实现一个虚拟求解器。问题由两个数字"a"和"b"表示,解决方案由"s"表示,其中s = a*b。

我定义了两个相应的 C 结构,problemsolution。问题结构有两个 int 成员,"a"和"b";解决方案结构有一个成员 's'。有一个函数可以初始化问题结构,init_problem(problem *p,int a, int b)。此外,还有一个函数将指针指向结构并返回解决方案,solution solve(problem *p)。最后,另外两个函数打印值(void dump_problem(problem *p)void dump_solution(solution *s))。所有这些都使用cdef声明。

接下来,我使用了三种方式将 C 代码公开给 python:一个包装 C 函数的def函数do_things(int a,int b),以及两个cdef class,一个使用结构作为属性,另一个使用指向结构的指针作为属性(分别为Solver_sSolver_p),包括打印问题和解决方案的包装方法。Solver_s课堂按预期工作;但是,使用 Solver_p 时,指针似乎未初始化,返回不正确的值(请参阅 test_pointers 和 输出 部分)。

我想我错过了关于指针及其范围的要点,但我无法理解发生了什么。任何帮助,非常感谢。我在OS X 10.11.6(El Capitan)中使用python 3.5.3和cython 0.25.2

PS:第一次问SO,所以如果我不清楚,我很乐意澄清!

指针.pyx

from libc.stdio cimport printf
cdef struct problem:
int a
int b
cdef struct solution:
int s
cdef void init_problem(problem *p,int a, int b):
p.a = a
p.b = b
cdef solution solve(problem *p):
cdef solution s
s.s = p.a * p.b
return(s)
cdef void dump_problem(problem *p):
printf("Problem dump: a = %d,b = %dn",p.a,p.b)
cdef void dump_solution(solution *s):
printf("Solution dump: s= %dn",s.s)
def do_things(int a,int b):
cdef problem p
init_problem(&p,a,b)
cdef solution s = solve(&p)
dump_problem(&p)
dump_solution(&s)
cdef class Solver_s: #Structs as attributes of Solver
cdef problem p
cdef solution s
def __cinit__(self,int a,int b):
print("tInside Solver_s __cinit__")
init_problem(&self.p,a,b)
dump_problem(&self.p)
self.s = solve(&self.p)
dump_solution(&self.s)
print("tGetting out of Solver_s __cinit__")
def show_problem(self):
dump_problem(&self.p)
def show_solution(self):
dump_solution(&self.s)
cdef class Solver_p: #Pointers to structs as attributes
cdef problem *pptr
cdef solution *sptr
def __cinit__(self,int a, int b):
print("tInside Solver_p __cinit__")
cdef problem p
self.pptr = &p
cdef solution s
self.sptr = &s
init_problem(self.pptr,a,b)
dump_problem(self.pptr) #It shows right values
self.sptr[0] = solve(self.pptr)
dump_solution(self.sptr) #It shows right values
print("tGetting out of Solver_p  __cinit__")

def show_problem(self):
dump_problem(self.pptr) 
def show_solution(self):
dump_solution(self.sptr)

test_pointers.py

import pyximport; pyximport.install()
import pointers
print("tSolving as a function")
pointers.do_things(2,3)
print("tSolving as a Extended Type, structs as attributes")
sol_s = pointers.Solver_s(4,5)
print("t###Problem definition unsing Solver_s methods###")
sol_s.show_problem()
print("t###Solution definition using Solver_s methods###")
sol_s.show_solution()

print("tSolving as a Extended Type, pointers to structs as attributes")
sol_p = pointers.Solver_p(6,7)
print("t###Problem definition unsing Solver_p methods###")
print("t###Gives weird values###")
sol_p.show_problem()
print("t###Solution definition using Solver_p methods###")
print("t###Gives weird values###")
sol_p.show_solution()

输出

Solving as a function
Problem dump: a = 2,b = 3
Solution dump: s= 6
Solving as a Extended Type, structs as attributes
Inside Solver_s __cinit__
Problem dump: a = 4,b = 5
Solution dump: s= 20
Getting out of Solver_s __cinit__
###Problem definition unsing Solver_s methods###
Problem dump: a = 4,b = 5
###Solution definition using Solver_s methods###
Solution dump: s= 20
Solving as a Extended Type, pointers to structs as attributes
Inside Solver_p __cinit__
Problem dump: a = 6,b = 7
Solution dump: s= 42
Getting out of Solver_p  __cinit__
###Problem definition unsing Solver_p methods###
###Gives weird values###
Problem dump: a = 1,b = 0
###Solution definition using Solver_p methods###
###Gives weird values###
Solution dump: s= 185295816

Solver_p.__cinit__中,ps是局部变量,只在调用__cinit__期间存在。Solver_p实例持续到调用之后,因此在其大部分生存期内,指针都是指向无效数据的。

解决方案是改为分配堆内存:

# at the top
from libc.stdlib cimport malloc, free
cdef class Solver_p:
# ....
def __cinit__(self,...):
self.pptr = <problem*>malloc(sizeof(problem))
self.sptr = <solution*>malloc(sizeof(solution))
# ...
def __dealloc__(self):
free(self.pptr)
free(self.sptr)
# ...

您需要小心以确保您分配的所有内存都已适当释放。


我的建议是,如果你不明白如何在C中正确使用指针,那么你不应该在Cython中使用它们。

最新更新