我正在尝试使用 ctypes 将二维数组和一维数组从我的 python 代码传递到 C 函数,然后该函数将一维数组返回到我的 python 终端。
1-我需要从 python 传递数组,正如您将在下面看到的(我是如何尝试做的)。 2-可能我没有正确设置参数类型和返回类型。我一直在搜索,但没有解决我的问题。
我的 C 代码名为 Thomas_PYTHON_DLL.c:
#include"stdio.h"
#include"stdlib.h"
#include"Thomas.h"
EXPORT double* Thomas(int dimension, double MatrizTridiagonal[dimension]
[dimension],double vec_b[dimension]){
double* Thomas(int dimension, double MatrizTridiagonal[dimension]
[dimension],double vec_b[dimension]){
double a[dimension];
double b[dimension];
double c[dimension];
double resp[dimension];
double *solution;
solution=(double *) malloc(dimension*sizeof(double));
for(int i=0;i<dimension;i++){resp[i]=vec_b[i];}
for(int i=0;i<dimension;i++){
if(i==0){a[i]=0.0;}
else{
a[i]=MatrizTridiagonal[i][i-1];
}
}
for(int i=0;i<dimension;i++){
b[i]=MatrizTridiagonal[i][i];
}
for(int i=0;i<dimension;i++){
if(i==dimension-1){c[dimension-1]=0.0;}
else{
c[i]=MatrizTridiagonal[i][i+1];
}
}
for(int i=0;i<dimension;i++){
if(i==0){
c[i]=c[i]/b[i];
resp[i]=resp[i]/b[i];
}
else{
c[i]=c[i]/(b[i]-c[i-1]*a[i]);
resp[i]=(resp[i]-a[i]*resp[i-1])/(b[i]-a[i]*c[i-1]);
}
}
for(int i=dimension-1;i>=0;i--){
if(i==dimension-1){
solution[i]=resp[i];
}
else{
solution[i]=resp[i]-c[i]*solution[i+1];
}
}
for(int i=0;i<dimension;i++){printf("x%d=|%0.2f| \n",i,solution[i]);}
return solution;
//free(solution);
}
}
我的 C 代码名为 Thomas.h:
#define EXPORT __declspec(dllexport)
EXPORT double* Thomas(int dimension, double MatrizTridiagonal[dimension]
[dimension],double vec_b[dimension]);
最后是我的 Python 代码,名为 Thomas_Python.py:
from ctypes import *
x=(c_double*5)
Tridiagonal = cdll.LoadLibrary('Thomas_dll.dll')
Tridiagonal.Thomas.restype=POINTER(x)
Tridiagonal.Thomas.argtypes=[c_int,((c_double*5)*5),(c_double*5)]
#arrays that i want to pass to C code
a=((c_double*5)*5)((2,-1,0,0,0),(-1,2,-1,0,0),(0,-1,2,-1,0),(0,0,-1,2,-1),
(0,0,0,-1,2))
b=(c_double*5)(4,2,2,2,4)
r=Tridiagonal.Thomas(5,a,b)
print(r[2])
在上面的代码中,我希望在位置“2”处打印数组 r 的值,但打印结果显示:
<__main__.c_double_Array_5 object at 0x03A77350>
除了知道如何读取数组值,将整个数组作为列表获取之外,还有一个很大的帮助。非常感谢您的帮助和时间,我为我的英语道歉。
最佳答案
提到[Python.Docs]: ctypes - A foreign function library for Python .
您的代码存在许多问题。这里有一些:
我不知道像这样的函数头
测试它double *Thomas(int dimension, double MatrizTridiagonal[dimension][dimension], double vec_b[dimension])
是如何编译的(因为维度)。但是,我没有用 gcc您的 C 和 Python 函数头(返回值)不同:
double*
vs.ctypes.POINTER(ctypes.c_double * 5)
您从不释放返回的数组,导致内存泄漏
代码风格(包括命名)可以大大改进
当处理数组(尤其是多维——因为维度需要在编译时知道)作为函数参数时,这意味着它们是从外部传递的,有两种处理方法:
为维度使用最大常数值。局限性非常明显
改用指针。缺点是函数头不是那么清晰,一般来说人们倾向于避开指针,尤其是当它们有不止一层的间接寻址时(2 星指针 :))
不过,我选择了后一种方式。我创建了一个虚拟 .dll,其中包含一个计算 2 个数组乘积的函数(将 1D 数组视为 2D 数组只有一列)。
dll00.c:
#include <stdlib.h>
#if defined(_WIN32)
# define DLL00_EXPORT_API __declspec(dllexport)
#else
# define DLL00_EXPORT_API
#endif
DLL00_EXPORT_API double *dll0Func0(int dimension, double **arr2D, double *arr1D)
{
double *solution = (double*)calloc(dimension, sizeof(double));
for (int i = 0; i < dimension; i++) {
for (int j = 0; j < dimension; j++) {
solution[i] += arr2D[i][j] * arr1D[j];
}
}
return solution;
}
DLL00_EXPORT_API void dealloc(double *ptr)
{
free(ptr);
}
code00.py:
#!/usr/bin/env python3
import sys
import ctypes as ct
DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")
def main(*argv):
dim = 5
DoubleArr = ct.c_double * dim
DoubleArrArr = DoubleArr * dim
DoublePtr = ct.POINTER(ct.c_double)
DoublePtrPtr = ct.POINTER(DoublePtr)
DoublePtrArr = DoublePtr * dim
dll0 = ct.CDLL(DLL_NAME)
dll0Func0 = dll0.dll0Func0
dll0Func0.argtypes = (ct.c_int, DoublePtrPtr, DoublePtr)
dll0Func0.restype = DoublePtr
dealloc = dll0.dealloc
dealloc.argtypes = (DoublePtr,)
mat = DoubleArrArr(
(2, -1, 0, 0, 0),
(-1, 2, -1, 0, 0),
(0, -1, 2, -1, 0),
(0, 0, -1, 2, -1),
(0, 0, 0, -1, 2),
)
vec = DoubleArr(4, 2, 2, 2, 4)
res = dll0Func0(dim, ct.cast(DoublePtrArr(*(ct.cast(row, DoublePtr) for row in mat)), DoublePtrPtr), ct.cast(vec, DoublePtr))
print("{0:s} returned {1:}".format(dll0Func0.__name__, res))
for i in range(dim):
print("{0:d} - {1:.3f}".format(i, res[i]))
dealloc(res)
if __name__ == "__main__":
print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32, sys.platform))
main(*sys.argv[1:])
print("\nDone.")
sys.exit(rc)
这里唯一棘手的事情是 DoublePtrArr 转换,因为 2D 数组不能转换为 double (**,不是类型)直接指针(我的意思是它可以,但是 2 个内存布局不同,所以它会生成未定义的行为,并且很可能该程序将段错误(访问冲突)),因此每个内部数组都在中间对象中单独转换,然后转换为 double (**) 指针(函数所期望的)。
输出:
cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q057295045]> sopr.bat ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [prompt]> "c:\Install\x86\Microsoft\Visual Studio Community\2017\VC\Auxiliary\Build\vcvarsall.bat" x64>nul [prompt]> dir /b dll00.c code00.py thomas.c Thomas.h [prompt]> cl /nologo /DDLL dll00.c /link /NOLOGO /DLL /OUT:dll00.dll dll0.c Creating library dll00.lib and object dll00.exp [prompt]> dir /b *.dll dll00.dll [prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 064bit on win32 dll0Func0 returned <__main__.LP_c_double object at 0x0000026CD4BEC4C8> 0 - 6.000 1 - -2.000 2 - 0.000 3 - -2.000 4 - 6.000 Done.
您还可以查看 [SO]: How to pass a 2d array from Python to C? (@CristiFati's answer)了解更多详情。
关于python - 使用 ctypes 为 C 函数传递和获取数组的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57295045/