【问题标题】:Access array beyond the limit in C and C++ [duplicate]访问数组超出 C 和 C++ 的限制 [重复]
【发布时间】:2013-09-14 15:23:40
【问题描述】:
int data[8];
data[9] = 1;

C++ 标准对此有何评论?这是未定义的行为吗?

至少 C 编译器 (gcc -std=c99 -pedantic -W -Wall) 没有说明任何内容。

【问题讨论】:

  • 事实上,即使data[8] 也是未定义的行为。
  • 附带说明:data[8] = anything 也是未定义的行为,因为您的数组将以 data[7] 结束。
  • 您对这个问题的答案有任何疑问吗?

标签: c++ c arrays undefined-behavior


【解决方案1】:

在数组边界之外访问是未定义的行为,来自c99 draft standard 部分Annex J.2 J.2 未定义的行为包括以下几点:

一个数组下标超出范围,即使一个对象显然可以用 给定下标(如左值表达式 a[1][7] 给定声明 int a[4][5]) (6.5.6)。

5.7 部分中的draft C++ standard 加法运算符 段落5 说:

当一个整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型。如果指针操作数指向数组对象的一个​​元素,并且数组足够大,则结果指向一个元素,从原始元素偏移,使得结果和原始的下标的差异数组元素等于整数表达式。 [...] 如果指针操作数和 result 都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则评估不应产生溢出;否则,行为未定义。

为了完整起见,5.2.1Subscripting 段落 1 部分说:

[...]表达式 E1[E2] 与 *((E1)+(E2)) 相同(根据定义) [ 注意:详见 5.3 和 5.7 * 和 + 的 以及 8.3.4 中的数组详细信息。 ——尾注]

重要的是要注意编译器不需要为未定义的行为产生警告(诊断),1.4 部分中的 C++ 标准草案 实现合规性1 段说:

可诊断规则集由本国际标准中的所有句法和语义规则组成除了那些包含“不需要诊断”的明确表示法或被描述为导致“未定义行为”的规则。

【讨论】:

    【解决方案2】:

    是的,这是未定义的行为。

    即使编译器能够检测到未定义的行为,它也可能会或可能不会警告您。

    【讨论】:

      【解决方案3】:

      这被认为是未定义的行为。如果您尝试编译将导致未定义行为的代码,编译器不需要发出警告,尽管这样做很好。

      【讨论】:

      • 我认为很多行为都被标记为“未定义”因为编译器很难找到这些错误。
      【解决方案4】:

      未定义。 它可能是也可能不是无效的内存,这使得它很危险。 您可以使用 valgrind 之类的工具来检测此类错误访问。

      【讨论】:

        【解决方案5】:

        是的,这是未定义的行为。一切都可能发生,它可能工作或不工作,它可能工作 2 年然后停止工作。这是三个中最危险的:

        • 未定义的行为
        • 未指定的行为
        • 实现定义的行为

        您可以查看它来认识它的其他亲戚: What are all the common undefined behaviour that a C++ programmer should know about?

        Undefined, unspecified and implementation-defined behavior

        【讨论】:

          【解决方案6】:

          C 和 C++ 不检查边界。您试图达到的价值几乎可以是任何东西。它在您的编译器上似乎可以工作,但它不是合法的 C 或 C++,并且不能保证在您下次运行程序时它仍然可以工作。

          根据 ISO C 标准,访问边界外的数组会导致

          未定义的行为:在使用不可移植或错误程序结构或错误数据时的行为,本国际标准对此没有任何要求

          当您尝试取消引用指向您的程序不允许访问的内存的指针时,会发生分段错误,而只是越过数组的末尾可能不会导致这种情况。但它最有可能会给你一些不好的价值。

          【讨论】:

            【解决方案7】:

            是的,这是未定义的行为,一些编译器会对此发出警告,而其他编译器则不会,但让我们看看,您的代码会这样做。

            查看运算符[] 内联实现。 a[b] 实际上是 *(a + b)。所以回到你的代码。

            int data[8];
            data[9] = 1;
            

            首先,您分配堆栈的一部分并创建指向第一个元素的指针。然后你重写了一些数据,这些数据就在你的数组之后,所以你破坏了一些数据。

            再举一个例子:

            int data[8];
            int data2[8] = {};
            data[9] = 1;
            

            很可能,编译器生成的代码分配一次并创建两个指针作为数组。所以data[9] = 1; 可以将data2 的第二个值设置为1,但不能保证这一点。

            【讨论】:

              猜你喜欢
              • 2011-04-09
              • 2018-09-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-01-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多