【问题标题】:Why does this not produce a segmentation fault [duplicate]为什么这不会产生分段错误[重复]
【发布时间】:2013-05-01 22:46:41
【问题描述】:

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

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

【问题讨论】:

  • 这来自使用 Java、Javascript 和 C# 编码的人...为什么会导致错误?
  • for (int x = 0; x &lt; 5; ++x)。从零开始。
  • @Renan 该数组有 5 个元素,索引为 0 到 4。正在访问索引 5。
  • @sftrabbit 谢谢。我希望在 C++ 中,这只会导致程序尝试读取内存中接下来的 4 个(或在某些机器中为 2 个或 8 个)字节,紧挨着分配给第 5 个位置的那些字节。但话说回来,C++ 不是我的强项。
  • @Renan 是的,可能。这就是为什么它是未定义的行为。

标签: c++


【解决方案1】:

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

  • 分段错误
  • 完全没有错误
  • 输出不一致
  • 硬盘格式化
  • ...(随便)

为了更正式一点,这是 C++11 标准定义未定义行为的方式:

本国际标准没有要求的行为 [ 注意:当本国际标准省略任何明确的定义时,可能会出现未定义的行为 行为或程序使用错误构造或错误数据时。 允许的未定义行为 范围从完全无视情况导致不可预测的结果,到在翻译或 以环境特征的书面方式执行程序(有或没有发布 诊断消息),终止翻译或执行(发出诊断消息)。 许多错误的程序结构不会产生未定义的行为;他们需要被诊断出来。 ——尾注]

关于x[5] 确实是未定义行为的原因,那是因为x[5] 等效于*(x + 5)(参见第8.3.4/6 段),以及关于一元运算符@987654324 的第5.3.1/1 段@指定:

一元 * 运算符执行间接:应用它的表达式应该是指向 对象类型,或指向函数类型的指针,结果是引用对象或函数的左值 表达式所指向的。如果表达式的类型是“指向 T 的指针”,则结果的类型是 “T。” [...]

但是由于x + 5不指向任何对象,而且上一段也没有说明解引用这样一个指针的结果应该是什么,所以前面引用的句子适用:

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

这意味着x[5] 是未定义的行为。

【讨论】:

  • 为什么是未定义的行为?
  • @someguy,因为它允许编译器执行某些优化。
  • 试图设置arr[5] 的值是未定义的部分,对吗?
  • @psib​​ar:不太可能; ) 但从形式上看,是的
  • 需要有一个 C++ 编译器来检测未定义的行为,当它可以并且实际格式化硬盘时。危险的编码。
【解决方案2】:

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

  • 访问不允许访问的内存的一部分,例如系统内存
  • 访问不存在的内存部分(也就是越界)

因此,您正确地意识到您超出了数组的范围,并且在最后一次循环迭代中,您正在访问程序分配内存之外的内容。恰好这块内存不是系统内存,而是存在的,所以它可以让你读取它。

如果您将这段代码运行足够多次,您最终应该会遇到分段错误,因为它恰好被放置在系统内存或内存末尾。

【讨论】:

  • 我质疑最后一部分。在我所知道的具有虚拟内存的系统上,它不能以这种方式工作。每个程序都有自己的地址空间,不会干扰除共享内存之外的其他应用程序地址空间。
  • 在那个 arr[5] 的顶部是在堆栈上。
【解决方案3】:

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

但是,如果您对它不崩溃的具体原因感兴趣,至少在我的编译器上,变量x 被分配在堆栈上紧跟数组的位置。当您将x 分配给arr[5] 时,您实际上只是将x 分配回自身。

显然,这可能因编译器而异。只是想你可能有兴趣至少知道一个特定的编译器在做什么。

【讨论】:

    猜你喜欢
    • 2011-01-27
    • 1970-01-01
    • 1970-01-01
    • 2017-12-04
    • 2016-02-08
    • 1970-01-01
    • 2016-09-24
    相关资源
    最近更新 更多