C/Python程序中使用无符号long long时的异常行为

2024-04-25 04:41:59 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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。在

调试也很困难,因为我不知道如何打印这些长整型数。如果Python代码为3000000,则运行以下代码:

^{pr2}$

我得到输出

3000000
18446744073709551615
3000000

因此,在从PyObject表示中解压无符号long long int的过程中似乎出了问题。我注意到在Python/C API documentation中,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:程序崩溃的错误消息如下(某些名称已修改):

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的行时,它会崩溃,并出现上述错误。c中的行号是某种错误处理函数的一部分,因此消息似乎大部分是无用的。在


Tags: 代码inpy版本parser错误line数组
2条回答

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

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

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));

打印了以下输出:

^{pr2}$

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

改变

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

^{pr2}$

回想一下,1+void*是列表中的下一个元素,或者类型转换更好:

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

还有

18446744073709551615=-1或FFFFFFFFFFFFFF

相关问题 更多 >