繁体   English   中英

使用 ctypes 为 C 函数传递和获取数组的问题

[英]Problems with passing and getting arrays for a C function using ctypes

我正在尝试使用 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 - Python 的外部函数库

您的代码存在许多问题。 以下是一些:

  • 我不知道像double *Thomas(int dimension, double MatrizTridiagonal[dimension][dimension], double vec_b[dimension])这样的函数头是如何编译的(因为维度)。 但是,我没有用gcc测试过
  • 您的CPython函数标头(返回值)不同: double* vs. ctypes.POINTER(ctypes.c_double * 5)
  • 你永远不会释放返回的数组,导致内存泄漏
  • 代码风格(包括命名)可以大大改进

当将数组(尤其是多维 - 因为维度需要在编译时知道)作为函数参数处理时,这意味着它们是从外部传递的,有两种处理方法:

  1. 对尺寸使用最大常数值。 限制很明显
  2. 改用指针。 缺点是函数头不是那么清楚,一般来说,人们通常倾向于避开指针,但特别是如果他们有不止一级的间接( 2 星指针:))

但是,我选择了后一种方法。 我创建了一个虚拟.dll ,其中包含一个计算两个数组的乘积的函数(将一维数组视为只有一列的二维数组)。

dll0.c :

#include <stdlib.h>

#if defined(_WIN32)
#  define DLL0_EXPORT_API __declspec(dllexport)
#else
#  define DLL0_EXPORT_API
#endif


DLL0_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;
}


DLL0_EXPORT_API void dealloc(double *ptr) {
    free(ptr);
}

脚本0.py

#!/usr/bin/env python3

import sys
import ctypes


DLL_NAME = "./dll0.dll"


def main():
    dim = 5
    DoubleArr = ctypes.c_double * dim
    DoubleArrArr = DoubleArr * dim

    DoublePtr = ctypes.POINTER(ctypes.c_double)
    DoublePtrPtr = ctypes.POINTER(DoublePtr)

    DoublePtrArr = DoublePtr * dim

    dll0 = ctypes.CDLL(DLL_NAME)

    dll0Func0 = dll0.dll0Func0
    dll0Func0.argtypes = [ctypes.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, ctypes.cast(DoublePtrArr(*(ctypes.cast(row, DoublePtr) for row in mat)), DoublePtrPtr), ctypes.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 {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main()
    print("\nDone.")

这里唯一棘手的是DoublePtrArr 转换,因为2D数组不能直接转换为 double( ** ,而不是类型)指针(我的意思是可以,但是 2 个内存布局不同,所以它会生成Undefined行为,并且最有可能的程序将段错误访问冲突)),所以每个内阵列在中间对象,这将在随后转换为双(**)指针单独铸造(即该函数需要)。

输出

 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 ********************************************************************** ** Visual Studio 2017 Developer Command Prompt v15.9.14 ** Copyright (c) 2017 Microsoft Corporation ********************************************************************** [vcvarsall.bat] Environment initialized for: 'x64' [prompt]> dir /b dll0.c script0.py thomas.c Thomas.h [prompt]> cl /nologo /DDLL dll0.c /link /NOLOGO /DLL /OUT:dll0.dll dll0.c Creating library dll0.lib and object dll0.exp [prompt]> dir /b *.dll dll0.dll [prompt]> "e:\\Work\\Dev\\VEnvs\\py_064_03.07.03_test0\\Scripts\\python.exe" script0.py Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit 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.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM