我在我的C程序中发现了一个bug,它接受一个numpy数组(作为PyObject*)并在其中搜索超过阈值的给定值。特别是,如果数组由64位整数组成,则搜索会给出错误的结果,并导致代码中的未定义行为。下面是我的代码的简化版本(通过宏支持多种数组类型和相等性测试):
static void* array_find_ulonglong(PyObject* searchval,
void* start_addr,
void* end_addr,
int stride) {
unsigned long long value = PyLong_AsUnsignedLongLong(searchval);
unsigned long long int* i;
for (i = start_addr; i != end_addr; i+=stride) {
if (*i >= value) {
return (void*)i;
}
}
return NULL;
}
数组被抽象成一个起始地址和结束地址,以及一个在内存中前进的步长。该代码用于更短的int类型的工作很好,但是这个版本永远找不到合适的值(即使它存在于数组中)并且总是返回null。
这也很难调试,因为我不知道如何打印这些长int。如果我为searchval提供3000000的Python整数,则运行以下代码:
printf("%s\n", PyString_AsString(PyObject_Str(searchval)));
unsigned long long value = PyLong_AsUnsignedLongLong(searchval);
printf("%I64u\n", value);
printf("%I64u\n", 3000000ull);
我得到输出
3000000
18446744073709551615
3000000
因此,在将无符号长整型整型整型整型从其PyObject表示中解包的过程中,似乎出现了一些问题。我注意到在Python/C API documentation中,PY LONG_AsUnsignedLongLong似乎返回了一个无符号PY_longlong类型的值,但是在使用此类型时,我得到了相同的结果,除了搜索“查找”(错误地)数组的第一个元素而不是什么都找不到。有人能指出我做错了什么吗?
编辑:步幅计算如下:
//arr is the PyArrayObject* passed in from Python via PyArg_ParseTuple
int elsize = arr->descr->elsize;
int stride = arr->strides[0] / elsize;
编辑2:程序崩溃的错误消息如下(某些名称已修改):
Traceback (most recent call last):
File "Parser.py", line 1893, in <module>
main()
File "Parser.py", line 1864, in main
p.Parse()
File "Parser.py", line 1411, in Parse
resultDict = self.ParseField(names, arrays, ignoreMasks, requests)
File "Parser.py", line 1554, in ParseField
arrays = Result.CalcAggStat(stat, names, arrays, times, flags, *args)
File "C:\Users\dpitch40\Documents\Local Sandbox\main\BRANCHES\PARSER3\tools\integrated\Parser\DFiles\Result.py", line 1503, in CalcAggStat
for name, array, t, flag in zip(names, arrays, times, flags):
SystemError: ..\Objects\longobject.c:980: bad argument to internal function
我一直在玩撞车的部分。在失败行中压缩在一起的每个列表都有一个元素。因此,正在运行的循环运行一次迭代(其中运行了上面给出的C搜索代码),然后当它返回到带有for的行时,它会因上述错误而崩溃。longobject.c中的行号是某种错误处理函数的一部分,因此消息看起来基本上是无用的。
最佳答案
改变
for (i = start_addr; i != end_addr; i+=stride) {
到
for (i = start_addr; i != end_addr; i+=1) {
回想一下,1+void*是列表中的下一个元素,或者类型转换更好:
for (i = start_addr; i != end_addr; ((uint8_t*)i)+=stride) {
所以
18446744073709551615=-1或ffffffffffffffffffffffffffffff
关于python - 在C/Python程序中使用无符号长整型时的行为不稳定,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21172550/