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

Python调用C素数函数返回异常,如何获取int*指针长度?

解决ctypes调用C素数函数的段错误问题及获取返回数组大小的方法

你的核心问题是C函数只返回了素数数组的指针,但Python无法知道这个数组的元素个数,遍历的时候会越界访问不属于数组的内存,最终触发段错误。下面给出几种可行的解决方案:

方案1:修改C函数,返回包含数组指针和长度的结构体

通过定义结构体同时存储素数数组指针和素数个数,Python用ctypes解析结构体获取完整信息。

修改后的C代码(primes.c)

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

// 定义存储结果的结构体
typedef struct {
    int* primes;
    int count;
} PrimeResult;

PrimeResult primes(int n)
{
    int i, j;
    int *sieve = NULL;
    int *prime_numbers = NULL;
    int num_primes = 0;
    PrimeResult result = {NULL, 0};
    
    if (n < 3) {
        printf("n must be >= 3, you input n: %d\n", n);
        return result;
    }

    sieve = (int *)calloc(n, sizeof(int));
    if (sieve == NULL) {
        printf("Memory allocation failed\n");
        return result;
    }

    for (i = 0; i < n; i++) {
        sieve[i] = 1;
    }
    sieve[0] = 0;
    sieve[1] = 0;
    
    for (i = 2; i < n; i++) {
        if (sieve[i]) {
            for (j = i*2; j < n; j += i) {
                sieve[j] = 0;
            }
            num_primes++;
        }
    }

    prime_numbers = (int *)calloc(num_primes, sizeof(int));
    if (prime_numbers == NULL) {
        printf("Memory allocation failed\n");
        free(sieve);
        return result;
    }

    j = 0;
    for (i = 0; i < n; i++) {
        if (sieve[i]) {
            prime_numbers[j] = i;
            j++;
        }
    }
    free(sieve);
    
    result.primes = prime_numbers;
    result.count = num_primes;
    return result;
}

修改后的Python调用代码

import ctypes
from time import perf_counter

# 定义对应C的结构体
class PrimeResult(ctypes.Structure):
    _fields_ = [("primes", ctypes.POINTER(ctypes.c_int)),
                ("count", ctypes.c_int)]

library = ctypes.CDLL('./primes.so')
library.primes.argtypes = [ctypes.c_int]
library.primes.restype = PrimeResult
libc = ctypes.CDLL("libc.so.6")

def primes_c(n: int) -> tuple[list[int], ctypes.POINTER(ctypes.c_int)]:
    assert isinstance(n, int), "n must be an integer."
    assert (n >= 3), "n must be >= 3."
    result = library.primes(n)
    # 根据count把指针转成Python列表
    prime_list = [result.primes[i] for i in range(result.count)]
    return prime_list, result.primes

def main():
    n: int = 10
    print(f"获取{n}以内的素数:")

    print("C实现:")
    start = perf_counter()
    prime_numbers_c, ptr = primes_c(n)
    end = perf_counter()
    print(f"耗时 {end - start:.2f} 秒.")

    for num in prime_numbers_c:
        print(num)

    # 释放C分配的内存
    libc.free(ptr)
    

if __name__ == '__main__':
    main()

方案2:让C函数返回素数个数,数组指针通过输出参数传递

无需定义结构体,用指针参数传递数组地址,返回值表示素数数量。

修改后的C代码(primes.c)

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

int primes(int n, int** out_primes)
{
    int i, j;
    int *sieve = NULL;
    int *prime_numbers = NULL;
    int num_primes = 0;
    
    if (n < 3) {
        printf("n must be >= 3, you input n: %d\n", n);
        return 0;
    }
    if (out_primes == NULL) {
        printf("Invalid output pointer\n");
        return 0;
    }

    sieve = (int *)calloc(n, sizeof(int));
    if (sieve == NULL) {
        printf("Memory allocation failed\n");
        return 0;
    }

    for (i = 0; i < n; i++) {
        sieve[i] = 1;
    }
    sieve[0] = 0;
    sieve[1] = 0;
    
    for (i = 2; i < n; i++) {
        if (sieve[i]) {
            for (j = i*2; j < n; j += i) {
                sieve[j] = 0;
            }
            num_primes++;
        }
    }

    prime_numbers = (int *)calloc(num_primes, sizeof(int));
    if (prime_numbers == NULL) {
        printf("Memory allocation failed\n");
        free(sieve);
        return 0;
    }

    j = 0;
    for (i = 0; i < n; i++) {
        if (sieve[i]) {
            prime_numbers[j] = i;
            j++;
        }
    }
    free(sieve);
    
    *out_primes = prime_numbers;
    return num_primes;
}

修改后的Python调用代码

import ctypes
from time import perf_counter

library = ctypes.CDLL('./primes.so')
# 第二个参数是int**类型,用POINTER(POINTER(c_int))
library.primes.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.POINTER(ctypes.c_int))]
library.primes.restype = ctypes.c_int
libc = ctypes.CDLL("libc.so.6")

def primes_c(n: int) -> tuple[list[int], ctypes.POINTER(ctypes.c_int)]:
    assert isinstance(n, int), "n must be an integer."
    assert (n >= 3), "n must be >= 3."
    ptr = ctypes.POINTER(ctypes.c_int)()
    count = library.primes(n, ctypes.byref(ptr))
    prime_list = [ptr[i] for i in range(count)]
    return prime_list, ptr

def main():
    n: int = 10
    print(f"获取{n}以内的素数:")

    print("C实现:")
    start = perf_counter()
    prime_numbers_c, ptr = primes_c(n)
    end = perf_counter()
    print(f"耗时 {end - start:.2f} 秒.")

    for num in prime_numbers_c:
        print(num)

    libc.free(ptr)
    

if __name__ == '__main__':
    main()

方案3:在数组开头存储素数个数(简易实现)

让C函数分配数组时多留一个位置,第一个元素存素数个数,后续存储素数,Python先读取长度再遍历。

修改后的C代码关键片段

// 分配数组时多一个int空间存个数
prime_numbers = (int *)calloc(num_primes + 1, sizeof(int));
if (prime_numbers == NULL) {
    printf("Memory allocation failed\n");
    free(sieve);
    return NULL;
}
// 第一个元素存素数数量
prime_numbers[0] = num_primes;
j = 1; // 从索引1开始存素数
for (i = 0; i < n; i++) {
    if (sieve[i]) {
        prime_numbers[j] = i;
        j++;
    }
}

Python调用代码修改

def primes_c(n: int) -> tuple[list[int], ctypes.POINTER(ctypes.c_int)]:
    assert isinstance(n, int), "n must be an integer."
    assert (n >= 3), "n must be >= 3."
    ptr = library.primes(n)
    if not ptr:
        return [], None
    count = ptr[0]
    prime_list = [ptr[i] for i in range(1, count+1)]
    return prime_list, ptr

原代码段错误原因

原代码中Python拿到int*指针后直接遍历,会从指针位置开始持续读取内存,直到访问到非法区域。而C函数分配的数组只有num_primes个元素,超出范围的内存属于未定义区域,最终触发段错误。

内容的提问来源于stack exchange,提问作者Wihan Uys

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 19:48:08