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

从Python调用GMP C函数遇段错误与内存泄漏问题求助

问题描述

今日工作清闲,团队安排了自我开发任务,我决定尝试从Python调用C语言函数处理问题。此前已用Rust为Python提速,但处理超出u128类型范围的大整数时屡屡受阻,因此打算借助知名的C语言GMP库解决这一问题。

已编写极简C程序,单独运行正常,功能符合预期,代码如下:

#include <stdio.h>
#include <gmp.h>

#define BASE 10

void _factorial(int n, mpz_t result) {
    int factor;
    mpz_t factor_mpz;

    mpz_init(result);
    mpz_init_set_ui(result, 1);

    for (factor = 1; factor <= n; factor++) {
        mpz_init(factor_mpz);
        mpz_init_set_ui(factor_mpz, factor);
        mpz_mul(result, result, factor_mpz);
        mpz_clear(factor_mpz);
    }
}

char *factorial(int n) {
    char *result;
    mpz_t result_mpz;

    _factorial(n, result_mpz);
    mpz_get_str(result, BASE, result_mpz);
    mpz_clear(result_mpz);

    return result;
}

int main(void) { // This runs without any apparent issues.
    char *result = factorial(100);

    printf("%s\n", result);

    return 0;
}

随后尝试用Python调用该函数,代码如下:

from ctypes import CDLL, c_void_p, c_char_p, c_int32, cast

CLIB = CDLL("./shared.so")
cfunc = CLIB.factorial
cfunc.argtypes = [c_int32]
cfunc.restype = c_char_p
raw_pointer = cfunc(100)
result = raw_pointer.decode()

print(result)

编译命令:

gcc main.c -lgmp -fpic -shared -o shared.so

运行Python脚本遇到两个问题:

  • 虽能执行print()语句并输出正确结果,但随后触发段错误;
  • 担心从C向Python传递任意长度字符串时存在内存泄漏。

解决方案

1. 修复段错误

段错误由两个核心问题导致:

  • 未初始化mpz_t变量:factorial函数中result_mpz未通过mpz_init初始化就传入_factorial,属于非法内存访问;
  • 内存管理器不兼容:mpz_get_str在C堆上分配的字符串,Python会默认用自身内存管理器释放,导致内存访问冲突。

修复后的C代码:

#include <stdio.h>
#include <gmp.h>
#include <stdlib.h>

#define BASE 10

void _factorial(int n, mpz_t result) {
    int factor;
    mpz_t factor_mpz;

    mpz_set_ui(result, 1); // 初始化交给调用者负责,内部仅重置值
    for (factor = 1; factor <= n; factor++) {
        mpz_init(factor_mpz);
        mpz_set_ui(factor_mpz, factor);
        mpz_mul(result, result, factor_mpz);
        mpz_clear(factor_mpz);
    }
}

char *factorial(int n) {
    char *result;
    mpz_t result_mpz;

    mpz_init(result_mpz); // 必须先初始化mpz_t变量
    _factorial(n, result_mpz);
    result = mpz_get_str(NULL, BASE, result_mpz); // 传入NULL让GMP自动分配内存
    mpz_clear(result_mpz);

    return result;
}

// 新增函数,让Python可以调用释放C分配的内存
void free_c_string(char *ptr) {
    free(ptr);
}

int main(void) {
    char *result = factorial(100);
    printf("%s\n", result);
    free(result); // 单独运行时手动释放
    return 0;
}

2. 解决内存泄漏

Python无法直接管理C堆内存,必须通过新增的C函数主动释放mpz_get_str分配的内存。修改后的Python代码:

from ctypes import CDLL, c_char_p, c_int32

CLIB = CDLL("./shared.so")
# 声明函数类型
c_factorial = CLIB.factorial
c_factorial.argtypes = [c_int32]
c_factorial.restype = c_char_p

c_free = CLIB.free_c_string
c_free.argtypes = [c_char_p]

try:
    raw_pointer = c_factorial(100)
    result = raw_pointer.decode()
    print(result)
finally:
    # 确保内存必被释放
    c_free(raw_pointer)

编译命令保持不变

gcc main.c -lgmp -fpic -shared -o shared.so

内容的提问来源于stack exchange,提问作者Tom Hosker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 15:45:03