Python 当 C 函数 returns 动态数组时 ctypes 行为异常

Python ctypes misbehaves when a C function returns a dynamic array

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

C 函数签名如下所示:

    const mwSize *mxGetDimensions(const mxArray *);

这里,mwSize是重命名的size_tmxArray*是不透明的指针。这个函数returns Matlab 数组的"shape"。返回的指针指向 size_t 数组,该数组存储在 mxArray 对象内部并且 不是 空终止(它的大小是通过另一个函数获得的)。

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

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

VAR得到mxArray*后,我调用:

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

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

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

如有任何意见,我们将不胜感激!谢谢

@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)

所以,你知道了:如果你要包装第 3 方 dynamic/shared 库,请彻底研究相关的头文件。