14

考虑以下代码:

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

4 回答 4

15

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

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

于 2010-01-10T04:48:38.423 回答
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有效。如果这看起来很奇怪,那就是,但是根据您从标准中发布的报价,您只能计算一个位于数组内部或最多超过数组的指针(不取消引用“过去”指针) .

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

也请参阅此帖子comp.lang.c

于 2010-01-10T05:22:00.770 回答
3

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

但是,如果您想线性地遍历“二维”内存,则可以针对一维数组构造一个二维访问器,它会正常工作,而 memset 之类的东西将对一维数组起作用。

于 2010-01-10T04:51:07.523 回答
0

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

于 2010-01-10T05:36:26.410 回答