[英]Strange memory behaviour when using Python C API
我正在嘗試通過 C++ 庫使用 Python C API 實現 Python 包裝器。 我需要實現轉換,以便我可以在 Python 和 C++ 中使用對象。 我過去已經這樣做了,但我有一個錯誤,我真的很難解決。
我有一個非常基本的測試功能:
PyObject* convert_to_python() {
std::cout << "Convert to PyObject" << std::endl;
long int a = 20;
PyObject* py_a = PyInt_FromLong(a);
std::cout << "Convert to PyObject ok" << std::endl;
return py_a;
}
我在 GoogleTest 宏中調用此函數:
TEST(Wrapper, ConvertTest) {
PyObject *py_m = convert_to_python();
}
我的輸出是:
Convert to PyObject
Segmentation fault (core dumped)
我還在它上面運行了 valgrind:
valgrind --tool=memcheck --track-origins=yes --leak-check=full ./my_convert
但它沒有給我太多關於它的信息:
Invalid read of size 8
==19030== at 0x4F70A7B: PyInt_FromLong (in /usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0)
==19030== by 0x541E6BF: _object* pysmud_from<float>(smu::Matrix<float, 0, 0>&) (smu_type_conversions.cpp:308)
==19030== by 0x43A144: (anonymous namespace)::Wrapper_ConvertMatrix_Test::Body() (test_wrapper.cpp:12)
==19030== by 0x43A0C6: (anonymous namespace)::Wrapper_ConvertMatrix_Test::TestBody() (test_wrapper.cpp:10)
==19030== by 0x465B4D: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (gtest.cc:2078)
==19030== by 0x460684: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (gtest.cc:2114)
==19030== by 0x444C05: testing::Test::Run() (gtest.cc:2151)
==19030== by 0x4454C9: testing::TestInfo::Run() (gtest.cc:2326)
==19030== by 0x445BEA: testing::TestCase::Run() (gtest.cc:2444)
==19030== by 0x44CF41: testing::internal::UnitTestImpl::RunAllTests() (gtest.cc:4315)
==19030== by 0x46712C: bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (gtest.cc:2078)
==19030== by 0x461532: bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (gtest.cc:2114)
==19030== Address 0x0 is not stack'd, malloc'd or (recently) free'd
我認為這段代碼應該可以工作,但我不明白我寫的東西有什么問題。 我是否錯誤地包含或鏈接了 Python 文件和庫?
編輯:沒有錯誤
#include <Python.h>
PyObject* convert_long_int(long int a) {
PyObject *ret = PyInt_FromLong(a);
return ret;
}
int main(void) {
long int a = 65454984;
PyObject *pya = convert_long_int(a);
return 0;
}
如果用gcc -o wraptest -I/usr/include/python2.7 wraptest.c -L/usr/lib/x86_64-linux-gnu/ -lpython2.7
初始化有什么作用?
如果省略初始化,我可以在 Ubuntu 16.04 和 Python 2.7 上確認分段錯誤。
#include <Python.h>
int
main(int argc, char *argv[])
{
Py_SetProgramName(argv[0]); /* optional but recommended */
Py_Initialize();
PyRun_SimpleString("from time import time,ctime\n"
"print 'Today is',ctime(time())\n");
Py_Finalize();
return 0;
}
所以當我做一個等效的最小主
int main()
{
Py_Initialize();
PyObject *p = convert_to_python();
Py_Finalize();
return 0;
}
它可以正常工作而不會崩潰。
這兩個例子的區別是
long int a = 20;
和
long int a = 65454984;
我想,它與PyInt_FromLong(long ival) 有關
當前的實現為 -5 到 256 之間的所有整數保留了一個整數對象數組,當您在該范圍內創建一個 int 時,您實際上只是返回對現有對象的引用。
也許 Python 試圖在沒有初始化的情況下訪問未初始化的指針或內存范圍。
當我使用a = 256
更改示例時,它崩潰了。 使用a = 257
,它沒有。
查看cpython/Objects/intobject.c:79 ,可以看到一個指針數組
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
在PyInt_FromLong(long ival) 的正下方訪問
v = small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
但沒有從_PyInt_Init(void)初始化
for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++) {
if (!free_list && (free_list = fill_free_list()) == NULL)
return 0;
/* PyObject_New is inlined */
v = free_list;
free_list = (PyIntObject *)Py_TYPE(v);
(void)PyObject_INIT(v, &PyInt_Type);
v->ob_ival = ival;
small_ints[ival + NSMALLNEGINTS] = v;
}
這些指針都是NULL
,導致崩潰。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.