在C/Python程序中使用无符号长long时的不稳定行为

在C/Python程序中使用无符号长long时的不稳定行为,python,c,Python,C,我在我的C程序中发现了一个bug,它接受numpy数组(作为PyObject*),并在其中搜索超过阈值的给定值。特别是,如果数组由64位整数组成,则搜索结果不正确,并导致代码中出现未定义的行为。下面是我的代码的简化版本(它支持各种数组类型和通过宏进行的相等性测试): 数组已抽象为起始地址和结束地址,以及在内存中前进的步长。对于较短的整数,此代码的版本可以正常工作,但此版本从未找到合适的值(即使它存在于数组中),并且始终返回NULL 调试也很困难,因为我不知道如何打印这些长整数。如果我为searc

我在我的C程序中发现了一个bug,它接受numpy数组(作为PyObject*),并在其中搜索超过阈值的给定值。特别是,如果数组由64位整数组成,则搜索结果不正确,并导致代码中出现未定义的行为。下面是我的代码的简化版本(它支持各种数组类型和通过宏进行的相等性测试):

数组已抽象为起始地址和结束地址,以及在内存中前进的步长。对于较短的整数,此代码的版本可以正常工作,但此版本从未找到合适的值(即使它存在于数组中),并且始终返回NULL

调试也很困难,因为我不知道如何打印这些长整数。如果我为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表示中解包无符号long-long-int的过程中,似乎出现了一些问题。我注意到在中,PyLong_AsUnsignedLongLong似乎返回了一个带有无符号PY_LONG_LONG类型的值,但使用它时,我得到了相同的结果,除了搜索“查找”(错误地)数组的第一个元素,而不是什么都没有找到。有人能指出我做错了什么吗

编辑:步幅计算如下:

//arr is the PyArrayObject* passed in from Python via PyArg_ParseTuple
int elsize = arr->descr->elsize;
int stride = arr->strides[0] / elsize;
编辑2:程序崩溃的错误消息如下(某些名称已修改):

回溯(最近一次呼叫最后一次):
文件“Parser.py”,第1893行,在
main()
文件“Parser.py”,第1864行,在main中
p、 Parse()
Parse中第1411行的文件“Parser.py”
ResultDisct=self.ParseField(名称、数组、信号掩码、请求)
ParseField中第1554行的文件“Parser.py”
arrays=Result.CalcAggStat(stat、名称、数组、时间、标志、*args)
CalcAggStat中的文件“C:\Users\dpitch40\Documents\Local Sandbox\main\branchs\PARSER3\tools\integrated\Parser\DFiles\Result.py”,第1503行
对于zip中的名称、数组、t、标志(名称、数组、时间、标志):
SystemError:..\Objects\longobject.c:980:内部函数的参数错误
我已经在崩溃的部分玩过了。在失败行中压缩在一起的每个列表都有一个元素。因此,正在运行的循环运行了一次迭代(其中运行了上面给出的C搜索代码),然后当它返回到带有for的行时,由于上述错误而崩溃。longobject.c中的行号是某种错误处理函数的一部分,因此消息似乎基本上没有用处。

更改

for (i = start_addr; i != end_addr; i+=stride) {

回想一下,1+void*是列表中的下一个元素,或者键入cast更好:

for (i = start_addr; i != end_addr; ((uint8_t*)i)+=stride) {


18446744073709551615=-1或FFFFFFFFFFFFFFFF

在Claris建议查找可能发生的错误后,我首先尝试调用perror(),它显示“无错误”。然后我检查是否抛出了任何Python异常,发现我得到的错误消息(如上所示)源自我发布的搜索代码,但由于某种原因,直到for行才显示出来。因此,longobject.c中的“对内部函数的错误调用”发生在我处理无符号长整数的一些代码中,这是有道理的

然后我尝试将这些检查添加到我的程序中:

printf("Is int: %d\n", PyInt_Check(searchval));
printf("Is EXACTLY int: %d\n", PyInt_CheckExact(searchval));
printf("Is long: %d\n", PyLong_Check(searchval));
其中打印了以下输出:

Is int: 1
Is EXACTLY int: 1
Is long: 0

因此,虽然我搜索的数组的值是long-long整数,但我从Python提供的搜索值不是,这在尝试将其转换为C无符号long-long时导致了错误。(我认为Python整数和long更容易互换,但显然不是这样)因此我在Python包装器中为我的C代码添加了一个数组类型检查,如果它包含8字节整数,搜索值将转换为Python long。这似乎解决了问题。感谢您的帮助和探索性问题。

猜测:
stride
以字节为单位,但
i+=stride
增量
stride*sizeof(*i)
。更改
stride
计算或
i+=stride
。我会选择
stride/=sizeof(*I)
或类似的东西。需要看更多的代码以获得最佳方向。我已经用较小的整数大小测试过它,然后它就可以工作了。我认为这个问题会影响具有2字节和4字节元素的数组。请参见上面我的步幅计算;这不是问题所在。对不起,我应该包括计算步幅的代码,其中考虑了这一点。添加到我原来的帖子中。正如我所说,问题似乎是当我试图将搜索的值从PyObject*转换为无符号长整型时。你知道我在这方面做错了什么吗?你很可能会得到一个错误(基于返回代码)。佩罗尔提供了有用的信息吗?对不起,你能详细说明一下吗?来自哪个函数的返回码?你的意思是在Python崩溃之后吗?不,在尝试进行转换之后,得到-1。佩罗尔(“”)有什么表现吗?此外,您可能会发现scipy.weave更易于使用,或者至少在调试其中一些方面有所帮助。例如,在执行相同的操作时,检查scipy.weave生成的可执行文件。哦,我明白了,我在检查该数字的二进制形式时犯了一个错误,所以我忽略了这一点。不过,使用perror打印“无错误”。然后我意识到它可能为Python设置了一条错误消息。因此,我尝试让顶级C函数返回NULL(表示发生了错误),并仅从C搜索函数的执行中得到相同的错误消息。似乎这个错误一直都在发生,只是因为某种原因直到for行才打印出来。所以我想这一定是因为我在处理长整数时出错了。
for (i = start_addr; i != end_addr; ((uint8_t*)i)+=stride) {
printf("Is int: %d\n", PyInt_Check(searchval));
printf("Is EXACTLY int: %d\n", PyInt_CheckExact(searchval));
printf("Is long: %d\n", PyLong_Check(searchval));
Is int: 1
Is EXACTLY int: 1
Is long: 0