引用计数为零,仍然没有分段错误

Zero reference count and still no segmentation fault

本文关键字:分段 错误 引用      更新时间:2023-10-16

我在 Python/C API 和内存分配方面有开发技能,并且预计以下 Python 嵌入式C++代码会有问题并导致分段错误之类的东西:

#include <Python.h> 
#include <iostream>
int main(){
Py_Initialize();
PyObject* pythonList = Py_BuildValue("[i i]",1,2);
Py_DECREF(pythonList); // I checked with Py_REFCNT(pythonList) that reference count is now 0
PyList_Check(pythonList); // hence, I was expecting here something like a segmentation fault, but this does not happen...
std::cout << "Ok, goodbye" << std::endl;
return 0;
}

但是,运行时不会发生任何不良情况(并且显示"好的,再见"(。

  • 尽管访问(PyList_Check(pythonList);(已归零的 PyObject ,但这段代码实际上还可以吗?

  • 或者,这段代码是错误的,这里没有发生分段错误只是运气问题(为什么?

代码是错误的- 对Py_DECREF()的调用释放了对象,这意味着pythonList是一个悬空指针,因此当PyList_Check()尝试尊重该指针时,将调用未定义的行为。

至于为什么这不会导致分割错误,正式的答案是,不需要未定义的行为来导致任何特定的可观察后果(例如分段错误(。 未定义的行为可能导致程序执行任何操作,程序员有责任避免调用它。

不过,实际上有一个更令人满意的解释:在大多数流行的系统上,当程序尝试访问未映射到任何物理内存年龄的虚拟内存页面或映射到程序不允许访问的物理页面时,会导致分段错误。 因此,如果您将pythonList设置为指向某个随机/无效的内存位置,然后尝试取消引用它,则可能会遇到分段错误。 但是,pythonList不是指向随机内存位置,而是指向有效 Python List 对象所在的内存位置(直到片刻前,Py_DECREF()释放它(。 该内存的"释放"仅意味着进程的堆数据结构现在将这部分内存包含在其"可用内存列表"中,作为内存,下次进程的其他部分想要分配内存时可以重用该内存。 它不涉及告诉 MMU 该内存位置现在无法访问(一般来说,它不能,因为 MMU 检查内存页的有效性,而不是单个字节的有效性,并且包含有效对象和释放内存区域的内存混合在一起是很常见的(。 因此,MMU/分段错误健全性检查系统不会捕获您对释放内存的读取(valgrind 可能会捕获它,但代价是您的程序运行速度比正常情况慢 10-100 倍(。