如何使用Ctypes将此numpy数组传递给C?

3

我正在学习C类型。我的目标是在python中生成一个从0到4*pi分为500步的numpy数组A。该数组将传递给计算这些值的C代码的正切值。C代码还将把这些值传递回python的另一个numpy数组B。

昨天我尝试了简单地将一个值从python转换为C,并成功了一次(经过一些帮助)。今天,我要尝试传递整个数组,而不是一个值。

我认为向C库添加另一个处理数组的函数是一个好主意。新函数应该循环将A的每个值传递给tan1()函数,并将该值存储在数组B中。

我有两个问题:

  • 编写处理numpy数组A的函数
  • 在python和C代码之间传递numpy数组。

我阅读了以下信息:

这些都很有帮助,但我仍然不知道如何解决我的问题。

C代码(仅相关部分):

double tan1(f) double f;
{
    return sin1(f)/cos1(f); 
}


void loop(double A, int n);
{
    double *B;
    B = (double*) malloc(n * sizeof(double));
    for(i=0; i<= n, i++)
    {
        B[i] = tan1(A[i])
    }
}

Python 代码:

import numpy as np
import ctypes


A = np.array(np.linspace(0,4*np.pi,500), dtype=np.float64)

testlib = ctypes.CDLL('./testlib.so')
testlib.loop.argtypes = ctypes.c_double,
testlib.loop.restype = ctypes.c_double


#print(testlib.tan1(3))
    

我知道在这种情况下使用ctypes.c_double是错误的,但这是我在只有1个值的版本中使用的,我还不知道该用什么来替换它。

请问我该如何实现这个目标?


1
你没有从C中使用NumPy(只使用了基本数据类型),因此我认为在Python中也没有使用它的理由。这只会为你的目标(学习CTypes)增加额外的复杂性层。 - CristiFati
@CristiFati 这是作业的一部分,要求使用numpy完成。你说得对,我还没有使用numpy,但我想将A传递给C库。 - Tim
这个回答解决了你的问题吗?如何使用NumPy数组和ctypes? - ead
重复的答案回答了你的问题(请参见你的问题标题),但并没有解决你的问题,因为你的第一个问题是你的C代码无法编译,并且没有任何明显的效果(如果不考虑引入的内存泄漏)。 - ead
1个回答

4
你需要归还动态分配的内存,例如将你的C代码更改为以下内容:
#include <math.h>
#include <stdlib.h>
#include <stdio.h>

double tan1(double f) {
    return sin(f)/cos(f);
}

double *loop(double *arr, int n) {
    double *b = malloc(n * sizeof(double));
    for(int i = 0; i < n; i++) {
        b[i] = tan(arr[i]);
    }
    return b;
}

void freeArray(double *b) {
    free(b);
}

在Python中,您需要声明参数和返回类型。正如其他评论中提到的那样,您还应该释放动态分配的内存。请注意,在C语言中,数组总是衰减为指针。因此,您需要一个额外的参数来告诉您数组中元素的数量。

此外,如果要将指向double的指针返回给Python页面,您必须指定数组的大小。使用np.frombuffer,您可以在不进行复制的情况下处理数据。

import numpy as np
from ctypes import *

testlib = ctypes.CDLL('./testlib.so')

n = 500
dtype = np.float64
input_array = np.array(np.linspace(0, 4 * np.pi, n), dtype=dtype)
input_ptr = input_array.ctypes.data_as(POINTER(c_double))

testlib.loop.argtypes = (POINTER(c_double), c_int)
testlib.loop.restype = POINTER(c_double * n)
testlib.freeArray.argtypes = POINTER(c_double * n),

result_ptr = testlib.loop(input_ptr, n)
result_array = np.frombuffer(result_ptr.contents)

# ...do some processing
for value in result_array:
    print(value)

# free buffer
testlib.freeArray(result_ptr)

testlib.freeArray.argtypes = POINTER(c_double * n), 是不正确的(至少它不反映C)。 - CristiFati
result_ptr(通过malloc在C端分配)是指向500个double的指针。同样的指针被传递给freeArray。在C中,数组会自动转换为指针。因此,类型从一个包含500个double的数组隐式转换为指向double的指针,函数参数的值是第一个double值的地址。 - Stephan Schlecht

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接