循环时出现分段故障

Segfault while looping

本文关键字:分段 故障 循环      更新时间:2023-10-16

处理一个简单的问题,但由于程序的写入超过了数组的末尾而导致分段错误。

#include <iostream>
#include <stdio.h>
static const int N = 46350;
int main()
{
    int* intarray = new int[N];
    for (int i = 2; i < N; ++i)
    {
        intarray[i] = 1;
    }
    for (int i = 2; i < N; ++i)
    {
        if (intarray[i])
        {
            for (int j = i; j*i < N; ++j)
            {
                printf("before i: %i j: %i ", i, j);
                std::cout << "a: " << intarray + i*j << std::endl;
                intarray[i*j] = 0;
                printf("after  i: %i j: %i ", i, j);
                std::cout << "a: " << intarray + i*j << std::endl;
            }
        }
    }
    delete [] intarray;
    return 0;
}

控制台输出:

before i: 211 j: 219 array: 0x21dd24c
after  i: 211 j: 219 array: 0x21dd24c
before i: 46349 j: 46349 array: 0x2488aec

对于N=46349,这种情况不会发生。不确定发生了什么。

整数溢出导致了这种情况。ij的乘积溢出了类型int的范围并产生负值,这显然被比作"小于N"。稍后,您尝试在负索引i * j处修改内存,这将导致不可预测的结果。事实上,溢出本身已经在产生未定义的行为。

对于N的这个值,您可以使用unsigned int类型而不是int类型。前者的正范围是前者的两倍。但在一般情况下,你必须记住,两个int值的乘积不一定适合int类型的范围。

在您的情况下,i可能会变得与46349一样大,并且46349*46349=21848322500,这大于有符号2补32位整数2147483647的典型上限。

N=463549的版本也以同样的方式被正式破解,只是你很幸运有了那个版本。if (intarray[i])检查可防止内部循环在可能导致溢出的情况下运行。

在您的案例中,最大整数(int(是2^31-1,即2147483647。这个数字的平方根是46340.95...,所以当你乘以i * j时,你得到了一个整数溢出,结果看起来是负的,它看起来是< N,并且在循环中被允许通过。所以你有错。所以说真的,任何高于46340的东西,你都可能会遇到一些问题。