今天工作很安静,因此团队被指示进行一些"self 发展".我决定从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;
}

然后我try 从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()个陈述并打印正确的结果,但它随后会达到segmentation fault.
  2. 我担心,在将任意长度的字符串从C传递到Python时,可能会出现一些内存泄漏.

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

推荐答案

  1. 尽管它达到了print()个陈述并打印正确的结果,但它随后会达到segmentation fault.

您的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()为您提供了两种存储结果的 Select :

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

  2. 它可以为结果动态分配足够的空间. 这是通过传递空指针作为第一个参数来触发的.

无论哪种方式,它都会返回指向结果第一个字节的指针.

您正在传递一个野生指针作为第一个参数,并导致未定义的行为. 从您对行为的描述中,您会看到一些任意程序数据被数字字符串输出覆盖,这样程序就成功打印了该数据,但很快就会失败,因为一些重要数据已被损坏.

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

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

    _factorial(n, result_mpz);
    result = mpz_get_str(NULL, BASE, result_mpz);  // <=== notice this
    mpz_clear(result_mpz);

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

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

如果您按照我上面描述的那样进行,那么您确实必须安排释放返回值所指向的内存. 我不确定Python ctypes是否对此有具体的规定,但至少,您应该能够使用ctypes将从factorial()获得的指针传递到标准库的free()函数.

Python相关问答推荐

如何在where或过滤器方法中使用SQLAlchemy hybrid_Property?

Python-Polars:如何用两个值的平均值填充NA?

重命名变量并使用载体中的字符串存储 Select 该变量

如何对行使用分段/部分.diff()或.pct_change()?

如何编写一个正规表达式来查找序列中具有2个或更多相同辅音的所有单词

code _tkinter. Tcl错误:窗口路径名称错误.!按钮4"

不允许AMBIMA API请求方法

如何使用矩阵在sklearn中同时对每个列执行matthews_corrcoef?

Python Hashicorp Vault库hvac创建新的秘密版本,但从先前版本中删除了密钥

如果条件为真,则Groupby.mean()

我在使用fill_between()将最大和最小带应用到我的图表中时遇到问题

重新匹配{ }中包含的文本,其中文本可能包含{{var}

通过Selenium从页面获取所有H2元素

在Python中管理打开对话框

如何在python xsModel库中定义一个可选[December]字段,以产生受约束的SON模式

如何在Polars中从列表中的所有 struct 中 Select 字段?

根据列值添加时区

使用BeautifulSoup抓取所有链接

如何在达到end_time时自动将状态字段从1更改为0

Python—压缩叶 map html作为邮箱附件并通过sendgrid发送