带有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:当调用任何堆分配函数时:malloc
calloc
realloc
1) 始终检查 (!=NULL) 返回值以确保操作成功。 2) 在 C 中,返回值的类型为void*
,可以分配给任何指针。强制转换只会使代码混乱,使其更难以理解、调试等
谢谢你!特别是对于规则 2 :)
【参考方案1】:
当i
是46349
,i*i
是2148229801
时,这超过了一个整数,所以溢出到-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大数组中的奇怪分段错误的主要内容,如果未能解决你的问题,请参考以下文章