从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
相关产品推荐
相关产品推荐

