我在我的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/

10-13 04:26