为什么这不会产生分段错误

Why does this not produce a segmentation fault

本文关键字:分段 错误 为什么      更新时间:2023-10-16

这是c++中的for循环。我不明白为什么在执行时没有给出分段错误。

int main() 
{
   int arr[5];
   for (int x = 0; x <= 5; x++)
       {
           arr[x] = x;
       }
   return 0; 
}

这是未定义行为。未定义的行为意味着任何都可能发生,包括:

  • 段错误
  • 没有错误
  • 输出不一致
  • 硬盘格式化
  • …(无论)

更正式一点,c++ 11标准是这样定义未定义行为的:

本国际标准对

行为没有要求[:当本国际标准省略了对…的明确定义时,可能会出现未定义的行为行为或当程序使用错误的结构或错误的数据时。允许的未定义行为从完全忽视导致不可预测结果的情况,到在翻译或翻译过程中的行为以环境特征的记录方式执行程序(有或没有发出(诊断消息),到终止翻译或执行(发出诊断消息)。许多错误的程序构造不会产生未定义的行为;他们需要被诊断出来。-end note]

之所以做x[5]确实是未定义行为,是因为x[5]等价于*(x + 5)(见第8.3.4/6段),而关于*一元算子的第5.3.1/1段规定:

一元*操作符是间接执行的:它所作用的表达式必须是指向对象的指针对象类型或指向函数类型的指针,而的结果是指向对象或函数的左值表达式指向的。如果表达式的类型是"指向T的指针",则结果的类型是"T。"[…]

但是由于x + 5没有指向任何对象,并且上面的段落没有指定这样的指针解引用的结果应该是什么,因此前面引用的句子适用:

[…当本国际标准省略了对行为的任何明确定义时,可能会出现未定义的行为[…]

表示x[5]为未定义行为

当用户程序试图执行下列操作之一时,会发生分段错误:

  • 访问不允许访问的部分内存,如系统内存
  • 访问不存在的部分内存(即超出边界)

所以你意识到你在数组中超出了界限是正确的,在最后一次循环迭代中,你正在访问程序分配的内存之外的东西。它只是碰巧那块内存不是系统内存,它存在,所以它允许你读取它。

如果你运行这段代码的次数足够多,你最终会得到一个分段错误,因为它会恰好被放置在系统内存或内存的末尾。

我认为Andy Prowl已经回答得最好了,他说这是未定义的行为。

但是如果你对为什么它不会崩溃的细节感兴趣,至少在我的编译器上,变量x被分配在紧跟在数组后面的堆栈位置。当你将x赋值给arr[5]时,你实际上只是将x赋值给它自己。

显然,这在不同的编译器之间可能是不同的。我想你可能有兴趣知道至少一个特定的编译器在做什么。