You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 05:05:38