请考虑以下代码:
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])
“如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义< /强>“。
但是我没有看到代码有什么问题,代码对我来说完全没问题。
所以,我只是想知道这段代码是否有效?我错过了什么吗?
答案 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)
。