带有clang的C大数组中的奇怪分段错误

Posted

技术标签:

【中文标题】带有clang的C大数组中的奇怪分段错误【英文标题】:Weird segmentation fault in C big array with clang 【发布时间】:2018-11-07 18:37:32 【问题描述】:

在这里,我想找到所有低于 200 万的素数的总和。 我使用 Eratosthenes 筛子,所以我需要一个 200 万个数组。

起初,我只将数组声明为全局变量,但 zsh 给了我一个分段错误。所以我尝试了 malloc,但错误仍然存​​在。 我已经测试了小数组,程序运行良好。

另外,我用的是clang-1000.10.44.2,加上-O2,程序可以工作,但答案不正确。代码如下。

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

#define MAXN 2000000

unsigned long long sum;
void erat(int maxn, char *flag)

    flag[0] = 0;
    flag[1] = 0;
    for(int i = 2; i < maxn; i++)
        if(flag[i])
            for(int j = i * i; j < maxn; j+=i)
                flag[j] = 0;


int main()

    int i;
    char *flag = (char*) malloc(MAXN * sizeof(char));
    for(i = 0; i < MAXN; i++)
        flag[i] = 1;
    erat(MAXN, flag);
    for(i = 0; i < MAXN; i++)
        if(flag[i]) sum+=i;
    printf("%llu\n", sum);
    return 0;

希望有人可以帮助我,感谢您的宝贵时间!

【问题讨论】:

OT:当调用任何堆分配函数时:malloccallocrealloc 1) 始终检查 (!=NULL) 返回值以确保操作成功。 2) 在 C 中,返回值的类型为void*,可以分配给任何指针。强制转换只会使代码混乱,使其更难以理解、调试等 谢谢你!特别是对于规则 2 :) 【参考方案1】:

i46349i*i2148229801 时,这超过了一个整数,所以溢出到-2146737495,因为它小于maxn,执行flag[j] = 0超出数组范围,因此崩溃。

j 更改为long long 修复了该错误:

for (long long j = (long long)i * i; j < maxn; j += i)

【讨论】:

我认为说它“溢出到”并不好,因为它是未定义的行为。 @Osiris 是的,根据标准它是未定义的,但在这种实际情况下,这是在调试器中在 PC 上运行时获得的值,并解释了观察到的程序其余部分的行为 但是 undefined 不就意味着你不能依赖结果吗?你可能会得到这个结果,但你也可能不会。就像你写越界时不会总是遇到分段错误一样。 是的,理论上你不能依赖结果,但实际上据我所知,在任何 x86 处理器上,如果你将 46349 乘以 46349,你总是会得到 -2146737495。该行为在标准中未定义,以便不对编译器实现者施加不必要的限制,这些限制可能无法在目标 CPU 中实现。 即使有符号溢出是明确定义的,超出flag 边界的大型负数组索引也肯定不是。【参考方案2】:

你在这里溢出了int

for (int j = i * i; j < maxn; j+=i)

由于i上升到2000000,i*i可以大于231,导致溢出,undefined behavior。

您可以在外循环中解决这个问题:

for(int i = 2; i < maxn; i++)

你只需要检查到maxn的平方根,所以把它改成:

for(int i = 2; i * i < maxn; i++)

【讨论】:

以上是关于带有clang的C大数组中的奇怪分段错误的主要内容,如果未能解决你的问题,请参考以下文章

带有非类型参数的奇怪模板实例化错误

大数组大小的分段错误

由于 C 中的内存不足导致的分段错误

“vtable”链接器错误(涉及带有“=default”的虚拟析构函数)- Clang 3.1 中的潜在错误?

尝试声明大数组时出现分段错误和核心转储[重复]

初始化数组时出现段错误