【问题标题】:C/C++: Is this undefined behavior? (2D arrays)C/C++:这是未定义的行为吗? (二维数组)
【发布时间】:2011-05-16 09:00:37
【问题描述】:

如果我以以下方式遍历二维数组的元素,是否会出现未定义的行为?

int v[5][5], i;

for (i = 0; i < 5*5; ++i) {
     v[i] = i;
}

然后,它甚至可以编译吗? (我现在不能尝试,我不在家。)如果没有,那么想象我以某种方式获得了指向第一个元素的指针并使用 taht 而不是v[i]

【问题讨论】:

  • v[i] = i 甚至不应该编译,IMO。也许你的意思是v[0][i] = i
  • 您的代码应显示此错误“错误 C2440: '=' : cannot convert from 'int' to 'int [5]'”
  • 使用www.ideone.com,自己编译看看!
  • (I can't try it right now, I'm not at home.) - 也许你回家后应该试试这个!我们应该为你编译代码?
  • @Nawaz:“编译并查看”绝不是对有关 UB 的问题的有效答案

标签: c++ c arrays undefined-behavior


【解决方案1】:

从指向第一个元素的指针访问多维数组的元素对于不属于第一个数组的元素来说是未定义行为 (UB)。

鉴于T array[n]array[i] 对于所有 i >= n 来说是直接前往 UB-land。即使TU[m]。即使是通过指针。正如其他人所提到的,确实对数组有严格的要求(例如sizeof(int[N]) == N*sizeof(int)),但没有明确例外,因此无能为力。

我没有官方参考资料,因为据我所知,C++ 标准将细节留给了 C89 标准,而且我对 C89 或 C99 标准都不熟悉。相反,我引用了comp.lang.c FAQ

[...] 根据官方解释,访问 (&array[0][0])[x] 的行为没有为 x >= NCOLUMNS 定义。

【讨论】:

  • C99 明确声明它是 UB 的示例,甚至可以执行指针添加。
  • C 常见问题解答没有帮助,它提供了对 6.5.5.2 的不相关参考,该部分仅涉及多维数组的下标语法,而不是如何访问它们。这就是为什么你应该只引用标准。
  • 澄清:以这种方式访问​​多数组可能不是有效的语法,但通过 &array[0][0])[x] 访问它可能是也可能不是有效的语法。
  • @Lundin 我的回答与问题中出现的确切代码无关,而是描述的情况:“想象一下,我以某种方式获得了指向第一个元素的指针并使用 taht [...] "。
【解决方案2】:

它不会编译。

等价的多,少的等价

int v[5][5], *vv, i;

vv = &v[0][0];
for (i = 0; i < 5*5; ++i) {
     vv[i] = i;
}

int v[5][5], i;

for (i = 0; i < 5*5; ++i) {
     v[0][i] = i;
}

将编译。我不确定它们是否是 UB(实际上 C90、C99 和 C++ 之间可能有所不同;别名是一个棘手的领域)。我会尝试以一种或另一种方式找到参考。

【讨论】:

  • 我认为这完全没问题,因为所有多维数组在内存中都是连续的(这是有保证的),并且对数组使用operator[] 只是指针运算。
  • 我不会对定义我的第一个示例和第二个 UB 感到惊讶:您将离开边界对象。在大多数当前目标上它不会产生影响,但在 x86 巨大模型(16 位 int,64 K 段)上 int[32000][32000] 的情况下,可能会检测到 int[0] 在一个段中并且不更新计算 int[0][i] 时的段部分,因此 i > 32765 的值失败(显然,您需要无符号或长索引才能在该平台上获得它)。
  • 在 C++ 中是 UB。但如果您使用vv++; 并通过*vv 访问,那很好。每次只能迈出第一步。
  • @Kiril 哪里有保证?您能否引用 C/C++ 标准的相关部分?
  • @Lundin - 我没有标准的副本,但发现了一些东西 - 在 ISO/IEC 14882:2003(E) 中(不知道“(E)”是什么意思): 8.3.4 数组:“一个数组类型的对象包含一个连续分配的非空的N个T类型子对象的集合”
【解决方案3】:

确实很难在标准中找到任何明确说明这是未定义行为的参考。当然,标准明确规定(C99 6.5.6 §8-9)如果你在数组之外进行指针算术,它就是 UB。那么问题来了,数组的定义是什么

如果把多维数组看成是数组对象的数组,那么就是UB。但是如果把它看成是一个多维数组,那代码就完美了。

在标准的附件 J 中有另一个未定义行为的有趣注释:

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

暗示访问超出第一维范围的多维数组是未定义的行为。但是,附件不是规范性文本,6.5.6 比较笼统。

或许有人可以明确定义数组对象和多维数组的区别?在那之前,我不相信这是 UB。

编辑:忘了提到 v[i] 肯定不是有效的 C 语法。根据 6.5.2.1,v[i] 等价于 *(v+i),它是一个数组指针,而不是一个数组元素。我不确定以v[0][too_large_value] 访问它是否是UB。

【讨论】:

    【解决方案4】:

    这里v[i]代表5个元素的整数数组。 一个整数数组被一个地址位置引用,这取决于你的'c'编译器可能是16位,32位......

    所以v[i] = i 可能会在某些编译器中编译......但它肯定不会产生你正在寻找的结果。

    sharptooth 的回答是正确的 v[i][j] = i... 是最简单易读的解决方案之一..

    其他可能

    int *ptr;
    ptr = v;
    

    现在你可以遍历这个ptr来分配值

    for (i = 0; i < 5*5; i++, ptr++) {
         *ptr = i;
    }
    

    【讨论】:

      【解决方案5】:

      这不会编译。

      您将收到以下错误:

      v[i] = i;
      

      错误:将“int”分配给“int [5]”时类型不兼容

      从类似的问题中得到答案:

      http://www.velocityreviews.com/forums/t318379-incompatible-types-in-assignment.html

      v 是一个二维数组。由于您只引用一个维度,因此您最终得到的是指向底层数组的 char 指针,因此该语句试图将 char 常量分配给 char 指针。 您可以使用双引号将常量更改为 C 风格的字符串,也可以显式引用 v[i][0],这就是我假设您的意图。

      【讨论】:

        猜你喜欢
        • 2012-05-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多