首页 > 解决方案 > 当 C 函数返回动态数组时,Python ctypes 行为异常

问题描述

我正在为 Matlab 的动态库开发 Python 包装类,以在 Python 中读取 Matlab MAT 文件,我遇到了一个我无法从 ctypes 接口解释的奇怪行为。

C 函数签名如下所示:

    const mwSize *mxGetDimensions(const mxArray *);

在这里,mwSize是一个重命名的size_t,并且mxArray*是一个不透明的指针。该函数返回 Matlab 数组的“形状”。返回的指针指向 size_t 数组,该数组内部存储在 mxArray 对象中,并且以null 结尾(其大小通过另一个函数获得)。

要从 Python 调用此函数,我将库设置如下:

    libmx = ctypes.cdll.LoadLibrary('libmx.dll')
    libmx.mxGetDimensions.restype = ctypes.POINTER(ctypes.c_size_t)
    libmx.mxGetDimensions.argtypes = [ctypes.c_void_p]

在获得mxArray*VAR,我打电话给:

    dims = libmx.mxGetDimensions(VAR)
    print(dims[0],dims[1])

VAR已知是 2-D 并且具有(1, 13)(使用 C 程序验证)的形状,但我的 Python 代码(55834574849 0)在 c_ulonglong 中返回...结果在测试 MAT 文件中存储的所有变量中始终是垃圾。

我究竟做错了什么?其他使用 VAR 的库调用似乎工作正常,因此 VAR 指向有效对象。如上所述,在 C 程序中调用的 mxGetDimensions() 按预期工作。

任何输入将不胜感激!谢谢

标签: pythonctypes

解决方案


@Neitsa 在 OP 下的评论中解决了我的直接问题,对 libmx.dll 的进一步调查解决了 Python 和 C 版本之间的剩余差异。

由于 Matlab 的 libmx.dll 可以追溯到很长时间,因为它最初是在 32 位时代编写的,所以 DLL 包含其函数的多个版本以实现向后兼容性。事实证明,这const mwSize *mxGetDimensions(const mxArray *);是该函数的最旧版本,并且关联的 C 头文件 (matrix.h) 具有#define mxGetDimensions mxGetDimensions_800用其最新版本覆盖该函数的行。显然,Python 的 ctypes 不检查 C 头文件;所以,留给我筛选头文件以确定要使用哪个版本的函数。

最后,POINTER(c_size_t)当我将代码更改为:

    libmx = ctypes.cdll.LoadLibrary('libmx.dll')
    libmx.mxGetDimensions_800.restype = ctypes.POINTER(ctypes.c_size_t)
    libmx.mxGetDimensions_800.argtypes = [ctypes.c_void_p]

    dims = libmx.mxGetDimensions_800(VAR)

所以,你有它:如果你正在包装一个 3rd-party 动态/共享库,请彻底研究相关的头文件。


推荐阅读