2017-06-13 24 views
0

我是一名C語言和一些python經驗的知識的一位新手。目前我正在嘗試瞭解擴展類型,但我無法理解以下示例中指針值發生了什麼(代碼位於解釋下方)。指針作爲擴展類型的屬性cython

作爲練習,我正在實施虛擬解算器。問題由兩個數字'a'和'b'表示,解決方案由's'表示,其中s = a * b。

我定義了兩個相應的C結構,problemsolution。問題結構有兩個int成員,'a'和'b';解決方案結構有一個成員'。有一個函數可以初始化問題結構,init_problem(problem *p,int a, int b)。此外,還有一個函數可以指向結構並返回一個解決方案,solution solve(problem *p)。最後,另外兩個函數打印值(void dump_problem(problem *p)void dump_solution(solution *s))。所有這些使用cdef聲明。

接着我用三種方式來暴露所述C代碼來蟒:一個def功能do_things(int a,int b)它包裝的C函數,以及兩個cdef class,一個使用結構作爲屬性,並使用指針結構作爲屬性(Solver_sSolver_p另一個,分別),包括打印方法來打印問題和解決方案。 Solver_s類按預期工作;然而,當使用Solver_p時,指針似乎不會被初始化,返回不正確的值(參見test_pointers和output部分)。

我想我錯過了一個關於指針和它們的範圍的細節,但我不明白髮生了什麼。任何幫助獲得這一點,不勝感激。我在OS X 10.11.6(El Capitan)中使用python 3.5.3和cython 0.25.2

P.S:第一次在SO中詢問,所以如果我不清楚,我會很樂意澄清!

pointers.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 = %d\n",p.a,p.b) 

cdef void dump_solution(solution *s): 
    printf("Solution dump: s= %d\n",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 

回答

2

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使用它們。