如何将Python函数指针传入C并执行?解决段错误问题
问题描述
我希望在Python中创建一个函数,将其函数指针传入C代码并在C中执行。以下是我的Python代码:
import ctypes import example def tester_print(): print("Hello") my_function_ptr = ctypes.CFUNCTYPE(None)(tester_print) example.pass_func(my_function_ptr)
我的C代码如下:
typedef void (*MyFunctionType)(void); PyObject* pass_func(PyObject *self, PyObject* args) { PyObject* callable_object; if (!PyArg_ParseTuple(args, "O", &callable_object)) return NULL; if (!PyCallable_Check(callable_object)) { PyErr_SetString(PyExc_TypeError, "The object is not a callable function."); return NULL; } PyObject* function_pointer = PyCapsule_New(callable_object, "my_function_capsule", NULL); if (function_pointer == NULL) return NULL; MyFunctionType my_function = (MyFunctionType) PyCapsule_GetPointer(function_pointer, "my_function_capsule"); if (my_function == NULL) return NULL; my_function(); // Or (*my_function)() Both same result. // PyCapsule_Free(function_pointer); Py_RETURN_NONE; }
执行上述代码时,调用my_function()会触发段错误,请问该如何解决?
解决方案
段错误的核心原因是你把ctypes生成的Python封装对象直接当成原生C函数指针处理,PyCapsule的用法完全错误——ctypes的CFUNCTYPE对象本身不是原生函数指针,它是一个Python对象,需要用正确方式提取指针,或者换一种更安全的调用方式。
方法一:正确提取ctypes函数的原生指针
ctypes生成的CFUNCTYPE实例有_as_parameter_属性,存储的才是真正的C函数指针。修改代码如下:
Python代码修改版
import ctypes import example def tester_print(): print("Hello") # 生成ctypes函数封装 my_function_ptr = ctypes.CFUNCTYPE(None)(tester_print) # 传入原生指针而非封装对象 example.pass_func(my_function_ptr._as_parameter_)
C代码修改版
#include <Python.h> #include <stdint.h> typedef void (*MyFunctionType)(void); PyObject* pass_func(PyObject *self, PyObject* args) { uintptr_t func_addr; // 用"K"格式符解析指针地址(对应uintptr_t类型) if (!PyArg_ParseTuple(args, "K", &func_addr)) return NULL; MyFunctionType my_function = (MyFunctionType)func_addr; // 直接调用原生函数指针 my_function(); Py_RETURN_NONE; }
方法二:直接用Python C API调用可调用对象
如果不需要原生C函数指针,只是想在C中执行Python函数,完全可以跳过ctypes,直接用Python C API调用传入的函数对象,这种方式更安全:
Python代码修改版(无需ctypes)
import example def tester_print(): print("Hello") # 直接传入Python函数对象 example.pass_func(tester_print)
C代码修改版
#include <Python.h> PyObject* pass_func(PyObject *self, PyObject* args) { PyObject* callable; if (!PyArg_ParseTuple(args, "O", &callable)) return NULL; if (!PyCallable_Check(callable)) { PyErr_SetString(PyExc_TypeError, "参数必须是可调用对象"); return NULL; } // 调用Python函数,无参数无返回值 PyObject* result = PyObject_CallObject(callable, NULL); if (result == NULL) return NULL; // 释放返回值对象(我们不需要它) Py_DECREF(result); Py_RETURN_NONE; }
重要注意事项
- 方法一中,要确保
my_function_ptr在C调用期间不会被Python垃圾回收——可以将其赋值给全局变量,或者通过其他方式延长生命周期,避免指针失效。 - 方法二更适合大多数场景,全程用Python C API处理,无需手动管理指针,也不会出现类型不匹配的问题。
内容的提问来源于stack exchange,提问作者Turgut
相关产品推荐
相关产品推荐

