代码的有效性

时间:2010-01-10 04:41:23

标签: c++ undefined-behavior

请考虑以下代码:

void populate(int *arr)
{
   for(int j=0;j<4;++j)
       arr[j]=0;
}

int main()
{
   int array[2][2];
   populate(&array[0][0]);
}

在当地社区有一个关于代码是否有效的讨论(我应该提到它的名字吗?)。一个人说它调用UB因为它违反了

C ++标准($ 5.7 / 5 [expr.add])

  

“如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义< /强>“。

但是我没有看到代码有什么问题,代码对我来说完全没问题。

所以,我只是想知道这段代码是否有效?我错过了什么吗?

4 个答案:

答案 0 :(得分:15)

您的array是两个int[2]数组,而您的函数populate()将其视为int[4]的单个数组。根据编译器决定如何对齐array元素的确切方式,这可能不是一个有效的假设。

具体来说,当j为2并且您尝试访问arr[2]时,这超出了main array[0]的范围,因此无效。

答案 1 :(得分:9)

你有一个数组数组。 array[1]跟在内存中array[0],因为数组是连续的。如果p == array[0],则p[1]跟随p[0],因为数组是连续的。所以,你是对的:array的所有内存都是连续的。

在图片中,array看起来像这样。

+-----------------+-----------------+
|      [0]        |      [1]        |
+-----------------+-----------------+

现在,让我们分解array[0]array[1],它们分别是这样的:

+--------+--------+
|  [0]   |  [1]   |        
+--------+--------+

所以,最后的图片是:

+--------+--------+--------+--------+
| [0][0] | [0][1] | [1][0] | [1][1] |
+--------+--------+--------+--------+

现在,问题是,你能否按照自己的方式访问这个连续的内存。答案是,标准无法保证。数组是连续的,但标准不允许以您的方式编制索引。换句话说:

&array[0][0]+2 == &array[1][0],但(&a[0][0] + 2) + 1未定义,而&a[1][0] + 1有效。如果这看起来很奇怪,那么,根据您从标准中发布的引用,您只能计算一个指针,该指针位于数组内部或最多一个超过数组的指针(不解除引用“一个过去”指针)

在实践中,我怀疑这会在任何地方失败,但至少根据标准,你的代码因未定义的行为而无效。

同样请参阅this post on comp.lang.c

答案 2 :(得分:3)

这不会一直有效。 C具有数组阵列,而不是2D数组。子数组并不总是在内存中指定为连续的(静态数组可能是,检查C / C ++标准)在这个特定的例子中,我怀疑它是否正常工作。但是,如果你动态分配了传入的内存,你很可能会失败,因为malloc(或new)可能使子数组相距很远。

但是,如果你想线性地向下走“2d”内存,你可以构建一个针对1D数组的2D访问器,它可以正常工作,像memset这样的东西可以对抗1D数组。

答案 3 :(得分:0)

C中,所有内容都存储在线性内存段中。您传递的a[0][0]地址与a[0]的地址相同 a[i][j]a[i*ColSize+j]相同,因为所有内容都是线性存储的。但是如果你动态分配内存,它将会失败,因为那时所有行可能都不会存储在连续的位置。然后a[i][j]将为*(&a[i]+j)