为什么我在使用 Python/C API 时会出现这个段错误

Why am I getting this segfault when using the Python/C API?

本文关键字:段错误 错误 Python API 为什么      更新时间:2023-10-16

使用 Python/C API 在我的C++代码中引用PyObject*时,我遇到了分段错误,我不知道为什么。 我正在使用C++和Python 2.7。 我正在使用新样式的类来实现未来的 Python 3 兼容性。

我的目标是创建一个C++类MyClass,作为 Python 模块中定义的类的包装器。 在MyClass构造函数中,我传入 Python 模块的名称,导入模块,找到类(始终具有预定义的名称 PyClass ),然后调用该类来创建它的实例。 然后,我将生成的PyObject*存储在MyClass中以备将来使用。 在 MyClass 析构函数中,我取消了存储PyObject*以避免内存泄漏。

我已经验证了就查找类并创建其实例而言,一切正常。 我什至验证了我可以在其他MyClass方法中使用存储的PyObject*,例如,访问PyClass中的方法。 但是,当析构函数执行 decref 时,它会导致段错误。

这是我的代码示例。 我还在适当的时候在其他地方调用Py_Initialize()Py_Finalize(),为了简洁起见,我省略了一些错误检查代码:

MyPythonModule.py

class PyClass:
    pass

MyClass.h

class MyClass {
public:
    MyClass(const char* modulename);
    ~MyClass();
private:
    void* _StoredPtr;
};

MyClass.cpp

#include <Python.h>
#include <iostream>
#include "MyClass.h"
MyClass::MyClass(const char* modulename) {
    _StoredPtr = NULL;
    PyObject *pName = NULL, *pModule = NULL, *pAttr = NULL;
    // Import the Python module.
    pName = PyString_FromString(modulename);
    if (pName == NULL) {goto error;}
    pModule = PyImport_Import(pName);
    if (pModule == NULL) {goto error;}
    // Create a PyClass instance and store a pointer to it.
    pAttr = PyObject_GetAttrString(pModule, "PyClass");
    if (pAttr == NULL) {goto error;}
    _StoredPtr = (void*) PyObject_CallObject(pAttr, NULL);
    Py_DECREF(pAttr);
    if (_StoredPtr == NULL) {goto error;}
error:
    if (PyErr_Occurred()) {PyErr_Print();}
    Py_XDECREF(pName);
    Py_XDECREF(pModule);
    return;
}
MyClass::~MyClass() {
    std::cout << "Starting destructor..."  << std::endl;
    Py_XDECREF((PyObject*)(_StoredPtr));
    std::cout << "Destructor complete."  << std::endl;
}

我知道我可以通过在析构函数中省略Py_XDECREF()来避免段错误,但我害怕导致内存泄漏,因为我不明白为什么会发生这种情况。 特别奇怪的是,我可以在其他MyClass方法中成功地使用_StoredPtr,但我无法拒绝它。

我还尝试将导入模块的PyObject*存储在 MyClass 中并保留它直到 _StoredPtr 被取消后,但 _StoredPtr decref 仍然存在段错误。 我尝试注释掉Py_DECREF(pAttr);行,但这无济于事。

正如我所提到的,我可以使用_StoredPtr检索PyClass中的方法,并且我还尝试将它们存储在MyClass中并在析构函数中取消引用。 当我这样做时,我可以 decref _StoredPtr ,但是当我尝试 decref 方法的PyObject*时,它会出现段错误。 如果我使用多种方法执行此操作,则无论我将它们按什么顺序排列,导致段错误的始终是最后一个 decref。

关于这里发生的事情有什么见解吗?

这对

我有用

#include <Python.h>
#include <iostream>
#include "MyClass.h"
MyClass::MyClass(const char* modulename) {
_StoredPtr = NULL;
PyObject *pName = NULL, *pModule = NULL, *pAttr = NULL;
// Import the Python module.
pName = PyString_FromString(modulename);
if (pName == NULL) {goto error;}
pModule = PyImport_Import(pName);
if (pModule == NULL) {goto error;}
// Create a PyClass instance and store a pointer to it.
pAttr = PyObject_GetAttrString(pModule, "PyClass");
if (pAttr == NULL) {goto error;}
_StoredPtr = (void*) PyObject_CallObject(pAttr, NULL);
Py_DECREF(pAttr);
if (_StoredPtr == NULL) {goto error;}
else{
// do something with _StoredPtr
Py_XDECREF((*PyObject)_StoredPtr)
}
error:
    if (PyErr_Occurred()) {PyErr_Print();}
    Py_XDECREF(pName);
    Py_XDECREF(pModule);
    return;
}
MyClass::~MyClass() {}

我基本上将 XDECREF 移到析构函数之外的函数中使用 PyObject 中。