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

C调用Python模块时PyImport_ImportModule二次调用崩溃求助

问题分析与解决方案

核心问题原因

  1. 重复初始化/终结Python导致扩展状态异常:你每次调用run_python()都执行Py_Initialize()和Py_Finalize(),但requests这类依赖底层网络C扩展的模块,在Py_Finalize()后无法完全重置全局状态,二次初始化时会触发崩溃——这也是无网络时正常(未触发完整扩展逻辑)、有网络时崩溃的直接原因。
  2. 内存管理错误:PyUnicode_AsUTF8()返回的指针指向Python对象管理的内存,在你调用Py_DecRef(resultPy)和Py_Finalize()后,该内存已被释放,返回的指针属于野指针,会引发未定义行为。

修复方案

关键修改思路

  • 全局仅初始化一次Python,程序退出时再终结,避免重复初始化导致的扩展状态问题。
  • 将Python返回的字符串复制到C自主管理的内存中,避免依赖Python对象的生命周期。
  • 完善错误检查逻辑,避免空指针访问。

修复后的C代码

#include <stdio.h>
#include <Python.h>
#include <string.h>
#include <stdlib.h>

// 全局初始化Python,仅执行一次
void init_python_env() {
    Py_Initialize();
    // 可选:重定向stderr到stdout,方便调试
    PyRun_SimpleString("import sys\nsys.stderr = sys.stdout\n");
}

// 全局清理Python环境,程序退出时执行
void cleanup_python_env() {
    Py_Finalize();
}

const char* fetch_currency() {
    // 导入Python模块
    PyObject* pModule = PyImport_ImportModule("geo");
    if (!pModule) {
        PyErr_Print();
        return strdup("EUR");
    }

    // 获取目标函数
    PyObject* get_currency_func = PyObject_GetAttrString(pModule, "get_currency");
    if (!get_currency_func || !PyCallable_Check(get_currency_func)) {
        PyErr_Print();
        Py_DecRef(pModule);
        return strdup("EUR");
    }

    // 调用函数获取结果
    PyObject* currency_obj = PyObject_CallObject(get_currency_func, NULL);
    if (!currency_obj) {
        PyErr_Print();
        Py_DecRef(get_currency_func);
        Py_DecRef(pModule);
        return strdup("EUR");
    }

    // 将Python字符串复制到C堆内存,脱离Python内存管理
    const char* raw_str = PyUnicode_AsUTF8(currency_obj);
    char* result = strdup(raw_str ? raw_str : "EUR");

    // 清理Python对象引用
    Py_DecRef(currency_obj);
    Py_DecRef(get_currency_func);
    Py_DecRef(pModule);

    return result;
}

int main() {
    init_python_env();

    // 第一次调用
    const char* curr1 = fetch_currency();
    printf("First currency: %s\n", curr1);
    free((void*)curr1); // 释放自主管理的内存

    // 第二次调用
    const char* curr2 = fetch_currency();
    printf("Second currency: %s\n", curr2);
    free((void*)curr2);

    cleanup_python_env();
    return 0;
}

补充注意事项

  • 确保geo.py位于程序运行的工作目录,或通过sys.path.append()添加模块所在路径。
  • 若因特殊需求必须多次初始化/终结Python(不推荐),需确认所有依赖的第三方模块都支持重复初始化,目前requests等网络模块无法保证这一点。
  • 编译命令保持原配置即可,确保链接正确的Python库版本。

内容的提问来源于stack exchange,提问作者il.giaco

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 10:16:02