如何将二维数组从 Python 传递给 C? [英] How to pass a 2d array from Python to C?

查看:69
本文介绍了如何将二维数组从 Python 传递给 C?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 ctypes 将一个二维数组从 Python 传递到 C.数组数据类型是 uint16.我写了一个简单的代码只是为了理解它是如何工作的:

C:

#include __declspec(dllexport) uint16_t 测试(uint16_t **arr){返回 (arr[5][5]);}

蟒蛇:

将 numpy 导入为 np从 ctypes 导入 cdll导入 ctypes从 numpy.ctypeslib 导入 ndpointer_p = ndpointer(dtype=np.uint16, ndim=2, shape=(10, 10), flags='C')mydll = cdll.LoadLibrary("mydll.dll")_func = mydll.Test_func.argtypes = [_p]_func.restypes = ctypes.c_uint16数据 = np.empty([10, 10], dtype=np.uint16)数据[5, 5] = 5打印(_func(数据))

我收到 OSError:访问冲突读取 0xFFFFFFFFFFFFFFFFFFFFFFFF我做错了什么,我该如何解决?

解决方案

Listing [SciPy.Docs]:C 类型外部函数接口 (numpy.ctypeslib)(和 [Python 3.Docs]:ctypes - Python 的外部函数库 以防万一.

这就像 [SO]:在 Python 中通过 ctypes 调用 C 函数,但函数返回不正确的值(@CristiFati 的答案)(重复),它恰好也涉及 NumPy.
换句话说,你有未定义行为,因为argtypes必须是CTypes类型(不是 NumPy).

以下是您的代码的修改版本,有效.

dll00.c:

#include #如果已定义(_WIN32)# 定义 DLL00_EXPORT_API __declspec(dllexport)#别的# 定义 DLL00_EXPORT_API#万一#if 已定义(__cplusplus)外部C"{#万一DLL00_EXPORT_API uint16_t dll00Func00(uint16_t **ppArr);#if 已定义(__cplusplus)}#万一uint16_t dll00Func00(uint16_t **ppArr){返回 ppArr[5][5];}

code00.py:

#!/usr/bin/env python3导入系统将 ctypes 导入为 ct将 numpy 导入为 npDLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")def main(*argv):UI16Ptr = ct.POINTER(ct.c_uint16)UI16PtrPtr = ct.POINTER(UI16Ptr)dll00 = ct.CDLL(DLL_NAME)dll00Func00 = dll00.dll00Func00dll00Func00.argtypes = [UI16PtrPtr]dll00Func00.restype = ct.c_uint16昏暗0 = 10昏暗1 = 10np_arr_2d = np.empty([dim0, dim1], dtype=np.uint16)np_arr_2d[5][5] = 5打印(np_arr_2d)#魔法"发生在以下代码行中ct_arr = np.ctypeslib.as_ctypes(np_arr_2d)UI16PtrArr = UI16Ptr * ct_arr._length_ct_ptr = ct.cast(UI16PtrArr(*(ct.cast(row, UI16Ptr) for row in ct_arr)), UI16PtrPtr)res = dll00Func00(ct_ptr)打印(\n{0:s} 返回:{1:d}".format(dll00Func00.__name__, res))如果 __name__ == __main__":print("Python {:s} {:03d}bit on {:s}\n".format("".join(item.strip() for item in sys.version.split("\n");)),64 如果 sys.maxsize >0x100000000 否则 32, sys.platform))打印(NumPy:{0:s}\n".format(np.version.version))rc = main(*sys.argv[1:])打印(\n完成.")sys.exit(rc)

输出:

<块引用>

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q058727931]>蝙蝠### 设置更短的提示以更好地适合粘贴到 StackOverflow(或其他)页面中###[提示]>c:\Install\x86\Microsoft\Visual Studio Community\2017\VC\Auxiliary\Build\vcvarsall.bat"x64>nul[提示]>目录/b代码00.pydll00.c[提示]>cl/nologo/DDLL dll00.c/link/NOLOGO/DLL/OUT:dll00.dlldll00.c创建库 dll00.lib 和对象 dll00.exp[提示]>目录/b代码00.pydll00.cdll00.dlldll00.expdll00.libdll00.obj[提示]>e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe"代码00.pyPython 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 位 (AMD64)] 064 位在 win32NumPy:1.16.2[[19760 5277 632 0 32464 5280 632 0 111 114][ 107 92 68 101 118 92 86 69 110 118][ 115 92 112 121 95 48 54 52 95 48][ 51 46 48 55 46 48 51 95 116 101][ 115 116 48 92 108 105 98 92 115 105][ 116 101 45 112 97 5 107 97 103 101][ 115 92 110 117 109 112 121 92 116 101][ 115 116 105 110 103 92 95 112 114 105][ 118 97 116 101 92 110 111 115 101 116][ 101 115 116 101 114 46 112 121 0 0]]dll00Func00 返回:5完毕.

可以在 [SO]:C++ &Python:将二维双指针数组从 python 传递并返回到 C++(@CristiFati 的答案)(以及引用的 [SO]:使用 ctypes 传递和获取 C 函数数组的问题(@CristiFati 的回答))..>

I'm trying to pass a 2d array from Python to C, using ctypes. The array dtype is uint16. I wrote a simple code just to understand how it works:

C:

#include <stdint.h>

__declspec(dllexport) uint16_t Test(uint16_t **arr)
{
     return (arr[5][5]);
}

Python:

import numpy as np
from ctypes import cdll
import ctypes
from numpy.ctypeslib import ndpointer

_p = ndpointer(dtype=np.uint16, ndim=2, shape=(10, 10), flags='C')
mydll = cdll.LoadLibrary("mydll.dll")
_func = mydll.Test
_func.argtypes = [_p]
_func.restypes = ctypes.c_uint16

data = np.empty([10, 10], dtype=np.uint16)
data[5, 5] = 5
print(_func(data))

I get OSError: access violation reading 0xFFFFFFFFFFFFFFFFFFFFFFF what am I doing wrong and how do I fix it?

解决方案

Listing [SciPy.Docs]: C-Types Foreign Function Interface (numpy.ctypeslib) (and [Python 3.Docs]: ctypes - A foreign function library for Python just in case).

This is a exactly like [SO]: Calling C function in Python through ctypes, but function returns incorrect value (@CristiFati's answer) (a duplicate), it just happens to also involve NumPy.
In other words, you have Undefined Behavior, as argtypes must be CTypes types (not NumPy).

Below is a modified version of the your code, that works.

dll00.c:

#include <stdint.h>

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


#if defined(__cplusplus)
extern "C" {
#endif

DLL00_EXPORT_API uint16_t dll00Func00(uint16_t **ppArr);

#if defined(__cplusplus)
}
#endif


uint16_t dll00Func00(uint16_t **ppArr)
{
    return ppArr[5][5];
}

code00.py:

#!/usr/bin/env python3

import sys
import ctypes as ct
import numpy as np


DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")


def main(*argv):
    UI16Ptr = ct.POINTER(ct.c_uint16)
    UI16PtrPtr = ct.POINTER(UI16Ptr)

    dll00 = ct.CDLL(DLL_NAME)
    dll00Func00 = dll00.dll00Func00
    dll00Func00.argtypes = [UI16PtrPtr]
    dll00Func00.restype = ct.c_uint16


    dim0 = 10
    dim1 = 10
    np_arr_2d = np.empty([dim0, dim1], dtype=np.uint16)

    np_arr_2d[5][5] = 5
    print(np_arr_2d)

    # The "magic" happens in the following lines of code
    ct_arr = np.ctypeslib.as_ctypes(np_arr_2d)
    UI16PtrArr = UI16Ptr * ct_arr._length_
    ct_ptr = ct.cast(UI16PtrArr(*(ct.cast(row, UI16Ptr) for row in ct_arr)), UI16PtrPtr)
    res = dll00Func00(ct_ptr)

    print("\n{0:s} returned: {1:d}".format(dll00Func00.__name__, 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))
    print("NumPy: {0:s}\n".format(np.version.version))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

Output:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q058727931]> 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
code00.py
dll00.c

[prompt]> cl /nologo /DDLL dll00.c  /link /NOLOGO /DLL /OUT:dll00.dll
dll00.c
   Creating library dll00.lib and object dll00.exp

[prompt]> dir /b
code00.py
dll00.c
dll00.dll
dll00.exp
dll00.lib
dll00.obj

[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

NumPy: 1.16.2

[[19760  5277   632     0 32464  5280   632     0   111   114]
 [  107    92    68   101   118    92    86    69   110   118]
 [  115    92   112   121    95    48    54    52    95    48]
 [   51    46    48    55    46    48    51    95   116   101]
 [  115   116    48    92   108   105    98    92   115   105]
 [  116   101    45   112    97     5   107    97   103   101]
 [  115    92   110   117   109   112   121    92   116   101]
 [  115   116   105   110   103    92    95   112   114   105]
 [  118    97   116   101    92   110   111   115   101   116]
 [  101   115   116   101   114    46   112   121     0     0]]

dll00Func00 returned: 5

Done.

The explanation for all those funky conversions can be found at [SO]: C++ & Python: Pass and return a 2D double pointer array from python to c++ (@CristiFati's answer) (and the referenced [SO]: Problems with passing and getting arrays for a C function using ctypes (@CristiFati's answer)).

这篇关于如何将二维数组从 Python 传递给 C?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆