2013-07-17 44 views
5

Tôi đang cố tạo một trình bao bọc Python cho một chương trình C cũ có đầu vào làm con trỏ. Tại thời điểm này, tôi có thể chạy chương trình nhưng không thể tìm ra cách lấy lại các giá trị tại các con trỏ được chỉ định.Truy cập dữ liệu tại địa chỉ bộ nhớ với ctypes

Đây là một C kịch bản đơn giản:

#include <stdlib.h> 

void cprogram(double *wts, double *res, int *kks, int n, double *ex) { 
    int m; 
    m=n+1; 
    res[0]=1.0; 
    kks[0]=1.0;} 

Và đây là mã Python đơn giản của tôi:

from ctypes import * 
import sys 

libc = CDLL("src/program.so") 

class CONTEXT(Structure): 
    _fields_ = [ 
       ("wts", POINTER(c_double)), //tried just to see if it would work 
       ("res", c_double), 
       ("kks", c_int), 
       ("n", c_int), 
       ("ex", c_double)] 

def runner(): 
    kk = (1,2,3) 
    n = 3 
    mm = n + 1 
    wts = (c_double * n)(1, 1, 1) 
    res = (c_double * mm)(0) 
    kks = (c_int * len(kk))(*kk) 
    n = c_int(n) 
    ex = c_double(0) 

    libc.cprogram.restype = POINTER(CONTEXT) 

    tmp = libc.cprogram(wts, res, kks, n, ex) 

runner() 

Tôi đã thử lệnh như print tmp[1].wts[1]print tmp[2] nhưng điều này chỉ in địa chỉ bộ nhớ và không giá trị (hoặc các giá trị cực kỳ nhỏ không chính xác như 2.15880221124e-314). Tôi muốn có thể trả về một danh sách các giá trị của wts.

Trả lời

3

Hàm C của bạn trả về void, không phải là CONTEXT *. Vì vậy, mã của bạn chỉ là tạo bộ nhớ ngẫu nhiên chưa được khởi tạo cho một CONTEXT * và sau đó cố gắng bỏ qua nó.

Ngày đầu đó, ngay cả khi nó đã làm trở lại một đối tượng CONTEXT bằng cách tham khảo, tmp[1] sẽ cố gắng deref bộ nhớ sau rằng đối tượng, vì vậy nó sẽ vẫn được rác.

Khi bạn cố gắng giải thích bộ nhớ ngẫu nhiên dưới dạng gấp đôi, bạn sẽ nhận được giá trị hoặc giá trị như 2.15880221124e-314 nếu bạn may mắn - nếu bạn không may mắn, bạn sẽ nhận được các giá trị chính xác nhưng vẫn ngẫu nhiên 0.0.


Trong khi đó, vì hàm C của bạn sửa đổi các đối số của nó tại chỗ, bạn không cần phải làm gì ở đây. Chỉ cần sử dụng các biến bạn thông qua vào

Vì vậy:.

def runner(): 
    kk = (1,2,3) 
    n = 3 
    mm = n + 1 
    wts = (c_double * n)(1, 1, 1) 
    res = (c_double * mm)(0) 
    kks = (c_int * len(kk))(*kk) 
    n = c_int(n) 
    ex = c_double(0) 

    libc.cprogram.restype = None 

    libc.cprogram(wts, res, kks, n, ex) 

    print wts[1] 

này hoạt động, và in ra 1.0.


Và nếu chức năng C đã trở lại một mảng của CONTEXT cấu trúc phù hợp với ctypes khai của bạn, điều này sẽ tất cả làm việc tốt. Ví dụ:

#include <stdlib.h> 

typedef struct { 
    double *wts; 
    double res; 
    int kks; 
    int n; 
    double ex; 
} CONTEXT; 

CONTEXT *cprogram(double *wts, double *res, int *kks, int n, double *ex) { 
    int m; 
    m=n+1; 
    res[0]=1.0; 
    kks[0]=1.0; 

    CONTEXT *contexts = malloc(sizeof(CONTEXT) * 4); 
    for (int i=0; i!=4; ++i) { 
    double *wtsses = malloc(sizeof(double) * 5); 
    for (int j=0; j!=4; ++j) { 
     wtsses[j] = i + j; 
    } 
    CONTEXT context = { wtsses, *res, *kks, m, *ex }; 
    contexts[i] = context; 
    } 
    return contexts; 
} 

Biên dịch này, chạy script Python hiện tại của bạn với thêm một print tmp[1].wts[1], và nó sẽ in ra 2.0.


Cuối cùng, cho followup bạn, trước tiên chúng ta hãy thay đổi mã C để có int *n, nơi *n là đầu vào:

void cprogram(double *wts, double *res, int *kks, int *n, double *ex) { 
    int m; 
    m=*n+1; 
    res[0]=1.0; 
    kks[0]=1.0;} 

Bây giờ, để gọi đây từ Python, bạn phải tạo một c_int và chuyển một con trỏ tới nó.Vì bạn đã làm nửa đầu (mà không phải là cần thiết trước khi-chỉ cần đặt argtypes thay ... nhưng đó là cần thiết bây giờ, đó là thuận tiện), nó chỉ là một sự thay đổi một dòng:

libc.cprogram(wts, res, kks, pointer(n), ex) 

Điều này thậm chí công trình nếu n là thông số không tham gia.


Nhưng thực sự, bạn không cần phải nhìn thấy đối tượng con trỏ nào cả từ Python; điều duy nhất bạn đang làm với nó là tạo ra nó để chuyển cho hàm, sau đó cho phép nó được thu thập. Để vượt qua một con trỏ C mà không cần tạo một đối tượng con trỏ ctypes (mà một lần nữa làm việc ngay cả khi n là một tham số trong-out), sử dụng byref:

libc.cprogram(wts, res, kks, byref(n), ex) 
+0

Cảm ơn bạn đã đặt trong thời gian và công sức để giải thích và sửa chữa các lỗi của tôi ! Điều duy nhất tôi tìm thấy là 'c_void' dường như không tồn tại nhưng' Không có' làm việc ở chỗ của nó. Tôi không biết nhiều C nên tôi đang cố gắng tránh thay đổi mã đó. – brebs

+0

Quick followup: Làm thế nào tôi sẽ đi về làm cho nó để chức năng C đã n trong như một con trỏ '* n' và sau đó hoạt động trên nó bằng cách làm' m = * n + 1'? Mã hiện tại của tôi không thành công. Cụ thể, có sửa chữa nhanh trong mã Python để thực hiện việc này không? – brebs

+0

@brebs: Phải, xin lỗi, 'void *' là 'c_void_p', nhưng' void' chỉ là 'None'. Dù sao, để theo dõi, tôi sẽ chỉnh sửa câu trả lời. – abarnert