从 Python 调用 GMP C 函数时出现分段错误和内存泄漏

问题描述 投票:0回答:1

今天工作比较安静,所以团队被指示做一些“自我开发”。我决定从 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)

我使用以下命令将 C 代码编译为 .so 文件:

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

然后我运行了上面的Python脚本,但不幸的是遇到了两个问题:

  1. 虽然它到达了
    print()
    语句并打印了正确的结果,但它随后遇到了 分段错误
  2. 我担心,在将任意长度的字符串从 C 传递到 Python 时,可能会出现一些内存泄漏。

有谁知道我如何克服分段错误,如果确实存在内存泄漏,我该如何堵住它?

python c gmp
1个回答
0
投票
  1. 虽然它到达了
    print()
    语句并打印了正确的结果,但它随后遇到了 分段错误

您的

factorial
函数错误地使用了
mpz_get_str()
。考虑:

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;
}

mpz_get_str()
为您提供两种存储结果的选择:

  1. 它将结果写入调用者指定的足够大的空间中。这是通过传递非空指针(到目标空间)作为第一个参数来触发的。

  2. 它为结果本身动态分配足够的空间。

您传递一个野指针作为第一个参数,从而导致未定义的行为。根据您对行为的描述,您将获得一些被数字字符串输出覆盖的任意程序数据,以便程序成功打印该数据,但很快就会失败,因为一些基本数据已损坏。

您最好的选择可能是让 GMP 为您分配空间:

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

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

    return result;
}
  1. 我担心,在将任意长度的字符串从 C 传递到 Python 时,可能会出现一些内存泄漏。

C 没有任意长度的字符串。它确实具有动态分配的对象,这些对象可以是内容为 C 字符串的字符数组。避免内存泄漏是确保动态分配的对象也被释放的一项练习。

如果您按照我上面描述的方式进行操作,那么您必须安排释放返回值指向的内存。我不确定Python的

ctypes
是否对此有具体规定,但至少,您应该能够使用
ctypes
将从
factorial()
获得的指针传递给标准库的
free()
函数。

© www.soinside.com 2019 - 2024. All rights reserved.