在 C++ 中嵌入 python:奇怪的分段错误

     2023-02-23     156

关键词:

【中文标题】在 C++ 中嵌入 python:奇怪的分段错误【英文标题】:Embedding python in C++ : strange segmentation faults 【发布时间】:2015-02-03 15:32:09 【问题描述】:

(抱歉标题含糊,但它表明我对这个问题感到多么目瞪口呆)。

所以我正在从 C++ 程序运行 Python 代码,遵循此处描述的方法:https://docs.python.org/2/extending/embedding.html。

这是 C++ 代码:

#include <Python.h>
#include <iostream>

int main(int argc, char *argv[])

    PyObject *pName, *pModule, *pDict, *pFunc;
    PyObject *pArgs, *pValue;
    int i;

    if (argc < 3) 
        fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
        return 1;
    

    Py_SetProgramName(argv[0]);
    Py_Initialize();
    PySys_SetArgv(argc, argv); 
    PyObject *sys = PyImport_ImportModule("sys");
    PyObject *path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyString_FromString("."));
    pName = PyString_FromString((char*)argv[1]);
    /* Error checking of pName left out */

    pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    if (pModule != NULL) 
        pFunc = PyObject_GetAttrString(pModule, argv[2]);
        /* pFunc is a new reference */

        if (pFunc && PyCallable_Check(pFunc))    
            PyObject *pArgs = PyList_New(4);
            PyList_SetItem(pArgs,0,PyString_FromString("H-SAMPLE1-OH"));
            PyList_SetItem(pArgs,1,PyInt_FromLong(2));
            PyList_SetItem(pArgs,2,PyString_FromString("H-SAMPLE2-OH"));
            PyList_SetItem(pArgs,3,PyInt_FromLong(3));
            PyObject *arglist = Py_BuildValue("(O)", pArgs);
            Py_DECREF(pArgs);

            for(int run = 0; run < 2; run++)
             
                std::cout << "begin" << std::endl;

                pValue = PyObject_CallObject(pFunc, arglist);
                //Py_DECREF(arglist);
                if (pValue != NULL) 
                
                    int py_list_size = PyList_Size(pValue);
                    printf("list size = %d\n",py_list_size);
                    int sub_list_size = 0;
                    for(Py_ssize_t i = 0; i < py_list_size; ++i)
                    
                        PyObject *pList = PyList_GetItem(pValue, i);
                        sub_list_size = PyList_Size(pList);  
                        if(PyList_Check(pList))
                        
                            for(Py_ssize_t j = 0; j < sub_list_size; ++j)
                            
                                PyObject *pListItem = PyList_GetItem(pList, j);
                                double pyNumber = PyFloat_AsDouble(pListItem);
                                std::cout << "pynumber ok" << std::endl;
                                Py_DECREF(pListItem);
                                printf("Result of call: %f\n", pyNumber);
                            
                        
                        else
                        
                            printf("Not list!\n");
                          
                        Py_DECREF(pList);
                    
                    Py_DECREF(pValue);
                
                else 
                    std::cout << "Else" << std::endl;
                    Py_DECREF(pFunc);
                    Py_DECREF(pModule);
                    PyErr_Print();
                    fprintf(stderr,"Call failed\n");
                    return 1;
                
            
        
        else 
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
        
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    
    else 
        PyErr_Print();
        fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
        return 1;
    
    Py_Finalize();
    return 0;

这里是玩具 Python 代码:

def test(a):
    print a
    return [[1.2,2.6],[4.7,5.6]]

注意 C++ 代码中的主循环,迭代变量“run”。当循环内的代码只执行一次时,它就像一个魅力。如果我尝试更多地运行它,例如只运行两次,它就会出错,我会遇到分段错误。显然,错误发生在第 61 行,尝试执行时

double pyNumber = PyFloat_AsDouble(pListItem);

我觉得这很奇怪。它在第一次执行期间工作正常,然后突然如果无法从 pListItem 正确获取某些东西(尽管它确实收到了它识别为大小为 2 的列表并且似乎正确处理所有其他 pyObject 指针)。知道发生了什么吗?

重现:

我编译如下:

g++ -L/usr/lib/python2.7/config-x86_64-linux-gnu -L/usr/lib -I/usr/include/python2.7 -o ms2pip ms2pip.c -lpthread -ldl  -lutil -lm  -lpython2.7 -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions

然后执行如下:

$ ./ms2pip python_code test
(so ./executable < python_file_without_py_extension > < function_name >)

【问题讨论】:

【参考方案1】:

我认为您的问题是 PyList_GetItem() 返回借用的引用。所以问题在于用pListpListItem 调用Py_DECREF()

PyObject *pList = PyList_GetItem(pValue, i);
// ...
if(PyList_Check(pList))

    for(Py_ssize_t j = 0; j < sub_list_size; ++j)
    
        PyObject *pListItem = PyList_GetItem(pList, j);
        double pyNumber = PyFloat_AsDouble(pListItem); // <-- Segfault in second iteration after released from first iteration.
        // ...
        Py_DECREF(pListItem); // <-- Bad, released in first iteration.
        // ...
    

//...
Py_DECREF(pList); // <-- Bad, released in first iteration.

pList 是借用的参考资料,您不负责与Py_DECREF() 一起发布。此外,pListItem 也是借用的参考。所以在第一次迭代中,你发布了pList 以及每个pListItem,这很糟糕。在第二次迭代中,您获取已发布的pList 和每个pListItem,并将它们视为仍然稳定,但事实并非如此。因为您正在访问已释放的对象,所以程序可能在涉及它们的任何函数调用中真正失败或给出错误结果(例如,PyList_Size(pList)PyList_GetItem(pList, j)PyFloat_AsDouble(pListItem)Py_DECREF(pListItem)Py_DECREF(pList))。

【讨论】:

我注释掉了一些 Py_DECREF 语句,它现在似乎工作正常!但是可以将它们注释掉吗?他们在那里不是有原因的吗?我必须承认我没有完全理解他们的角色(这段代码广泛地基于我链接的页面上可以找到的那个)但我希望如果你不使用它们,你会保留内存资源和可能会遇到内存问题,不是吗? @jerorx 阅读Objects, Types and Reference Counts(尤其是关于引用计数的部分)。总之,当从任何Py* 函数返回PyObject*(或后代)时,文档将指出它是借用引用,还是拥有(或新)引用我>。 借用 引用意味着您不拥有该对象,并且只会临时使用它。如果你想保留对它的引用,你必须Py_INCREF()它,以便你以后可以Py_DECREF()它。 @jerorx 使用 owned(或 new)引用,您拥有它,并且在完成后必须调用 Py_DECREF(),否则它内存会泄漏。 @jerorx 关于借用引用可以使用多长时间的一般规则是,它仅在包含对象未被修改且仅用于以下范围时才有效您获得了它(例如,来自PyList_GetItem() 的引用仅在再次调用PyList_* 之前有效)。 PyList_SetItem() 等一些函数会窃取你的引用。因此,如果您要 PyList_GetItem() 一个项目(借用参考),然后 PyList_SetItem() 该项目(窃取参考),你将不得不Py_INCREF()之间的项目以确保正确的引用计数。

Omnet ++简单模块的C ++代码中python嵌入代码中的分段错误错误

】Omnet++简单模块的C++代码中python嵌入代码中的分段错误错误【英文标题】:SegmentationfaulterrorinpythonembeddedcodeinC++codeofOmnet++simplemodule【发布时间】:2019-05-2913:33:35【问题描述】:我想从OMNeT++简单模块中的C++代码调用Python函数。我... 查看详情

C++:奇怪的分段错误

】C++:奇怪的分段错误【英文标题】:C++:WeirdSegmentationfault【发布时间】:2010-06-1011:42:12【问题描述】:我正在尝试使用C++打印一些东西。但是,我遇到了一个让我一无所知的奇怪错误,我使用以下代码:PRINTDLGpd;ZeroMemory(&pd,s... 查看详情

对象创建引起的 C++ 奇怪的分段错误

】对象创建引起的C++奇怪的分段错误【英文标题】:C++strangesegmentationfaultbyobjectcreation【发布时间】:2010-11-0513:04:01【问题描述】:通过启动类对象,我遇到了一个奇怪的问题。这个问题很奇怪,也不容易重现。但是,我将尝试... 查看详情

无法在嵌入 C++ 的 Python 代码中修改 time.time() 返回的值

】无法在嵌入C++的Python代码中修改time.time()返回的值【英文标题】:Can\'tmodifyvaluereturnedbytime.time()inPythoncodeembeddedinC++【发布时间】:2010-02-2512:16:19【问题描述】:我遇到了一个非常奇怪的问题。以下代码:importtimetarget_time=time.time... 查看详情

在 C++ 中嵌入 python

】在C++中嵌入python【英文标题】:embeddingpythoninc++【发布时间】:2011-10-0918:08:32【问题描述】:我用C++创建了一个VCL应用程序,borland。在我的项目中有一个文件,我在其中定义的方法中实现了嵌入式python(我的应用程序包含一个... 查看详情

在嵌入 Python 解释器的应用程序中调试 tk85.dll 中的问题

】在嵌入Python解释器的应用程序中调试tk85.dll中的问题【英文标题】:Debugaproblemintk85.dllinanapplicationthatembedsthePythoninterpreter【发布时间】:2011-01-3113:55:10【问题描述】:我的C++应用程序嵌入了Python解释器,但在关闭时似乎遇到了... 查看详情

在 C++ 中使用向量的分段错误?

】在C++中使用向量的分段错误?【英文标题】:SegmentationfaultusingvectorsinC++?【发布时间】:2019-07-1015:39:32【问题描述】:我正在寻找已编入代码的分段错误。当我的函数parseRecord(string)生成一个向量并且我想打印出其中一个元素时... 查看详情

为啥在此 C++ 代码中出现分段错误?

】为啥在此C++代码中出现分段错误?【英文标题】:WhydoIgetasegmentationfaultinthisC++code?为什么在此C++代码中出现分段错误?【发布时间】:2019-08-1306:13:26【问题描述】:我正在解决HackerRank上的问题,到目前为止,我已经能够轻松地... 查看详情

在命名管道中获取分段错误(核心转储)错误

...】:2018-09-0310:23:11【问题描述】:尝试在Ubuntu上使用C++和python中的命名管道实现反向字符串,当我尝试接受用户输入时出现分段错误(核心转储)错误。预定义字符串时,程序可以完美运行。以下是写入文件的C++编写程序:#inclu... 查看详情

在嵌入python解释器的应用程序中调试tk85.dll中的问题(代码片段)

我的C++应用程序嵌入了Python解释器,但在关闭时似乎遇到了一些麻烦。在主窗口关闭后,我得到一个分段错误(这是Windows,但无论如何我们称之为分段错误)。堆栈跟踪如下:#0102AD580tk85!Tk_MainWindow()(C:Users...1.3inDebuglib k85.dll:??)#... 查看详情

在 C++ 中使用向量时出现分段错误

】在C++中使用向量时出现分段错误【英文标题】:Segmentationfaultwhenusingavectorinc++【发布时间】:2014-04-1121:40:54【问题描述】:使用向量时出现分段错误似乎是一个常见问题,但我似乎仍然无法解决我的问题。我不清楚为什么getArmP... 查看详情

Pthread_create 在 C++ 中导致分段错误

...在尝试接受一个整数值,并在程序中创建该数量的线程。奇怪的是,只能创建第一个线程。经过一些跟踪,它显示pthread_create是导致核心转储的行。#include<iostream>#include< 查看详情

在 C++ 中操作向量时出现分段错误

】在C++中操作向量时出现分段错误【英文标题】:Segmentationfaultwhenoperatingavectorinc++【发布时间】:2013-10-3019:40:41【问题描述】:我正在尝试学习c++,我想用一个简单的程序将X实例的向量初始化为类成员,但是我遇到了分段错误..... 查看详情

在 C++ 中使用向量时出现分段错误?

】在C++中使用向量时出现分段错误?【英文标题】:SegmentationfaultwhileusingvectorinC++?【发布时间】:2017-05-1513:30:28【问题描述】:为什么我在以下代码中遇到分段错误?此代码以第一个元素为0的数组s开始。然后是元素与s互补的数... 查看详情

带有clang的C大数组中的奇怪分段错误

】带有clang的C大数组中的奇怪分段错误【英文标题】:WeirdsegmentationfaultinCbigarraywithclang【发布时间】:2018-11-0718:37:32【问题描述】:在这里,我想找到所有低于200万的素数的总和。我使用Eratosthenes筛子,所以我需要一个200万个数... 查看详情

在 C++ 中捕获分段错误

】在C++中捕获分段错误【英文标题】:CatchSegmentationfaultinc++【发布时间】:2012-10-1007:16:41【问题描述】:try-catch块是否捕获分段错误错误?我正在使用下面给出的函数读取文本文件,但有时文件为空并且程序崩溃。我希望程序继... 查看详情

使用 Python 的析构函数中的分段错误

】使用Python的析构函数中的分段错误【英文标题】:SegmentationfaultindestructorwithPython【发布时间】:2021-08-1507:10:33【问题描述】:我已经创建了一个类来表示我的LED灯带,并且我想在停止灯带时关闭灯带(也就是当程序停止并且对... 查看详情

使用 ctypes 时出现分段错误

...布时间】:2018-10-2309:01:31【问题描述】:当我使用ctypes从python调用c++时,我不断收到分段错误。我已将gdb附加到c++代码并确保C++代码运行良好。在c++代码具有正确的返回值之后并在返回到python代码之前引发了分段错误。我检查了... 查看详情