多维数组在内存中的表现

3

我想知道C++标准是否保证多维数组(非动态分配)被展开为完全相同大小的一维数组。例如,如果我有:

char x[100];
char y[10][10];

这两者是否等价?我知道大多数编译器会展开y,但这是否确实是有保证的呢?在阅读C++标准的11.3.4数组章节时,我并没有找到任何保证这一点的地方。
C++标准保证y[i]紧随在y[i-1]后面。由于y[i-1]长10个字符,那么从逻辑上讲,y[i]应该在内存中比y[i-1]晚10个字符; 但是,一个编译器是否会填充y[i-1]与额外的字符以使y[i]对齐呢?

你究竟想用这个做什么?访问 y[0][10] 将会是未定义行为。 - 1201ProgramAlarm
1
听起来你真正想问的是是否可以像迭代一维数组一样迭代二维数组。答案是法律上不行,但大多数/所有实现都允许这样做,因为这是唯一合理的行为。非常相关/重复:https://dev59.com/ymw05IYBdhLWcg3wZA4C - NathanOliver
@ChrisMM 请看:https://timsong-cpp.github.io/cppwp/dcl.array#6 - NathanOliver
@NathanOliver-ReinstateMonica 我不是要刁难,但我认为这并不能保证数组中没有填充。这只是指定了y[i-1]y[i]之间没有填充,但如果 y[i-1] 占用 sizeof(char[10])+2 字节,则仍然不存在附加的数组元素之间的填充,这符合标准规范。 如果我有 char x[5]; int y[5] ,那么编译器可以在第一个数组后添加填充,那么为什么它不能对多维数组进行相同的操作呢? - ChrisMM
@ChrisMM,我已经添加了一篇答案,希望能够澄清这一切。 - NathanOliver
显示剩余4条评论
1个回答

5

你要找的内容在[dcl.array]/6中找到。

类型为“N个U的数组”(array of N U)的对象包含一组连续分配的非空子对象,即数组的元素,编号为0到N-1。

这意味着如果你有一个像int arr[10]的数组,那么就会有10个连续的int在内存中。这个定义可以递归使用,所以如果你有:

int arr[5][10]

那么你拥有的是一个包含5个int[10]数组的数组。如果我们应用上面的定义,我们就知道这5个int[10]数组是连续的,而int[10]本身也是连续的,因此所有50个int都是连续的。所以,是的,在内存中,2D数组看起来就像1D数组,因为它们实际上就是这样。
这并不意味着你可以获取arr[0][0]的指针并迭代到arr[4][9]。根据[expr.add]/4
当整型表达式J被加到或从指针类型表达式P中减去时,结果的类型为P。
如果P计算出一个空指针值并且J计算出0,则结果是一个空指针值。
否则,如果P指向具有n个元素([dcl.array])的数组对象x的数组元素i,则表达式P + J和J + P(其中J的值为j)指向x的(可能是虚拟的)数组元素i+j,如果0≤i+j≤n,则表达式P-J指向x的(可能是虚拟的)数组元素i-j。
否则,行为未定义。
这意味着,如果您有一个指向数组的指针,则可以添加的有效索引为[0,array_size]。所以如果您执行以下操作:
int * it = &arr[0][0]

那么it指向的是第一个数组的第一个元素,这意味着您只能合法地将it增加到it + 10,因为这是第一个数组的结束元素之前。即使它们是连续的,进入第二个数组也会导致未定义行为。

抱歉,我还有一个问题:int[10]是否保证它是sizeof(int) * 10,并且绝对没有填充在末尾?填充规则仅适用于不同的变量吗? - ChrisMM
@ChrisMM 是的。数组不允许有填充。它们被定义为具有“包含N个子对象的连续分配的非空集合”。 - NathanOliver
@ChrisMM 如果您使用std::array,则可能会存在填充(padding),因为std::array是一个类类型,这些类型允许有填充(padding)。 - NathanOliver
好的,我认为我感到困惑是因为 char x[5]; int a; 变量之间可以有填充,对我来说这个填充属于 x,因此占用的空间比 sizeof( char ) * 5 更多。如果这样说您能理解吗 :) - ChrisMM
1
@ChrisMM 那个填充属于这两个对象中的任何一个。基本上,那个填充属于实现本身。 - NathanOliver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接