用Python+ctypes封装C++动态数组,段错误

4 投票
1 回答
1596 浏览
提问于 2025-04-16 00:19

我想用ctypes来封装一段小的C++代码,用来分配一个数组,但在把地址存储到c_void_p对象时遇到了一些问题。

(注意:这里的指针故意转换成void*类型,因为我以后也想用同样的方法来处理C++对象的数组分配。)

需要封装的C(++)函数如下:

void* test_alloc()
{
    const int size = 100000000;
    int* ptr = new int[size];
    std::cout << "Allocated " << size * sizeof(int) << " bytes @ " <<
                 ptr << std::endl;
    return static_cast<void*>(ptr);
}

void test_dealloc(void* ptr)
{
    int* iptr = static_cast<int*>(ptr);
    std::cout << "Trying to free array @ " << iptr << std::endl;
    delete[] iptr;
}

这是Python的封装代码(假设之前的函数已经用ctypes导入):

class TestAlloc(object):
    def __init__(self):
        self.pointer = ctypes.c_void_p(test_alloc())
        print "self.pointer points to ", hex(self.pointer.value)

    def __del__(self):
        test_dealloc(self.pointer)

对于小数组(比如大小为10),看起来是没问题的:

In [5]: t = TestAlloc()
Allocated 40 bytes @ 0x1f20ef0
self.pointer points to  0x1f20ef0

In [6]: del t
Trying to free array @ 0x1f20ef0

但如果我想分配一个大的数组(大小为100,000,000),就会出现问题:

In [2]: t = TestAlloc()
Allocated 400000000 bytes @ 0x7faec3b71010 
self.pointer points to  0xffffffffc3b71010L

In [3]: del t
Trying to free array @ 0xffffffffc3b71010
Segmentation fault

存储在ctypes.c_void_p中的地址显然是错误的,前面4个字节是无效的。某种程度上,32位和64位的地址混在了一起,而在分配大数组时,内存管理器(在这种情况下)被迫返回一个在32位上无法表示的地址(感谢TonJ)。

有没有人能提供一个解决方法呢?

这段代码是用g++ 4.4.3编译的,运行在Ubuntu Linux 10.04 x86_64上,内存为4G。Python版本是2.6.5。

非常感谢!

更新:

我解决了这个问题。我忘记为test_alloc()指定返回值类型(restype)。restype的默认值是ctypes.c_int,而64位的地址放不下。通过在调用test_alloc()之前添加test_alloc.restype = ctypes.c_void_p,问题就解决了。

1 个回答

1

从表面上看,问题似乎不在于小数组和大数组的分配,而是在于32位和64位地址的混用。在你的例子中,小数组的地址可以用32位来表示,但大数组的地址就不行了。

撰写回答