我在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;
}
然而,在运行时没有发生任何不好的事情(显示“Ok,goodbye”)。
这个代码实际上是否正常,尽管访问(在PyList_Check(pythonList);
中)已被减少为零的PyObject?
或者,这段代码是错误的,这只是运气问题,这里没有发生分段错误(为什么?)?
答案 0 :(得分:1)
代码错误 - 对Py_DECREF()
的调用释放了对象,这意味着pythonList
是一个悬空指针,所以当PyList_Check()
时尝试推断指针,未定义的行为被调用。
至于为什么不会导致分段错误,正式答案是不需要未定义的行为来导致任何特定的可观察结果(例如分段错误)。未定义的行为可能导致程序执行literally anything,并且程序员有责任避免调用它。
实际上,有一个更令人满意的解释:在大多数流行的系统中,当程序试图访问未映射到任何物理内存时代的虚拟内存页面或映射到a时,会导致分段错误不允许程序访问的物理页面。因此,如果您将pythonList
设置为指向某个随机/无效的内存位置,然后尝试取消引用它,则可能会出现分段错误。但是,pythonList
并未指向随机内存位置,它指向有效Python列表对象所在的内存位置(直到刚才,{{1}释放它)。 &#34;释放&#34;该存储器仅仅意味着进程的堆数据结构现在在其自由存储器列表中包括该部分存储器&#34;作为可以在下次进程的其他部分想要分配内存时重用的内存。它并不涉及告诉MMU该内存位置现在无法访问(一般来说,它不能,因为MMU检查内存页面的有效性,而不是单个字节的有效性并且,拥有一个包含有效对象和释放内存区域的内存页面非常常见。因此,MMU /分段错误检查系统不会捕获您对释放内存的读取(valgrind可能会捕获它,但代价是程序运行速度比正常慢10-100倍)。