您如何包装一个C函数,该函数使用ctypes返回指向malloc数组的指针?

时间:2020-08-22 19:01:24

标签: python c ctypes

我有一个C函数,该函数读取二进制文件并返回动态大小的无符号整数数组(大小基于二进制文件中的元数据):

//example.c
#include <stdio.h>
#include <stdlib.h>

__declspec(dllexport)unsigned int *read_data(char *filename, size_t* array_size){
  FILE *f = fopen(filename, "rb");
  fread(array_size, sizeof(size_t), 1, f);
  unsigned int *array = (unsigned int *)malloc(*array_size * sizeof(unsigned int));
  fread(array, sizeof(unsigned int), *array_size, f);
  fclose(f);

  return array;
}

answer似乎是在说将创建的数组从C传递到Python的正确方法是这样的:

# example_wrap.py
from ctypes import *
import os

os.add_dll_directory(os.getcwd())
indexer_dll = CDLL("example.dll")

def read_data(filename):
    filename = bytes(filename, 'utf-8')
    size = c_size_t()
    ptr = indexer_dll.read_data(filename, byref(size))
    return ptr[:size]

但是,当我运行python包装程序时,代码在ptr[:size]处静默失败,就好像我试图越界访问数组一样,也许是,但是传递此内容的正确方法是什么?动态调整数组大小?

1 个答案:

答案 0 :(得分:2)

一些注意事项:

首先,您需要正确设置C函数的原型,以便ctypes可以在C和Python类型之间正确转换。

第二,由于size实际上是ctypes.c_size_t对象,因此实际上您需要使用size.value来访问数组大小的数值。

第三,由于ptr[:size.value]实际上将数组内容复制到Python列表中,因此您将要确保也free()分配的C数组,因为您将不再使用它。

(在这里将数组复制到Python列表可能不理想,但我认为在这里还可以,因为否则在Python中处理C数组会更加复杂。)

这应该有效:

from ctypes import *
import os

os.add_dll_directory(os.getcwd())
indexer_dll = CDLL("example.dll")
indexer_dll.read_data.argtypes = [c_char_p, POINTER(c_size_t)
indexer_dll.read_data.restype = POINTER(c_int)
libc = cdll.msvcrt

def read_data(filename):
    filename = bytes(filename, 'utf-8')
    size = c_size_t()
    ptr = indexer_dll.read_data(filename, byref(size))
    result = ptr[:size.value]
    libc.free(ptr)
    return result