n维(n≥2)数组在内存中是如何表示的?

4
3个回答

4
在C语言中,二维数组就是数组的数组。三维数组就是数组的数组的数组,以此类推。
相关的C99标准中,与数组下标有关的部分在第6.5.2.1节:"数组下标"。
连续的下标操作符用于指定多维数组对象中的一个元素。如果E是一个n维数组(n ≥ 2),其维度为i × j × . . . × k,则E(作为非左值使用)会被转换为一个指向(n −1)维数组,其维度为j × . . . × k。如果对这个指针显式地或隐式地应用一元*运算符,或者通过下标运算来实现,结果就是指向(n −1)维数组的指针,如果不作为左值使用,则该指针本身会被转换为一个指针。由此可知,数组按行主序存储(最后一个下标变化最快)。
一些混淆是由于索引运算符是基于指针算术定义的。这并不意味着数组实际上是“指针” - 实际上它们绝对不是。声明数组对象根本不会创建任何指针对象(除非当然它是指针的数组)。但是,引用数组的表达式通常(但并不总是)“衰减”为指向数组的第一个元素的指针(那是指针值,而不是指针对象)。
现在,简单的数组对象,无论有多少维,都非常不灵活。在C99之前,所有数组对象都必须是在编译时确定大小的固定大小。C99引入了可变长度数组(VLA),但即使在C99标准发布12年后,并不是所有编译器都支持VLA。
如果您需要更灵活的东西,常见的方法是声明指向元素类型的指针,然后使用malloc()分配数组,并使指针指向数组的第一个元素:
int *ptr = malloc(N * sizeof *ptr);
if (ptr == NULL) /* handle allocation failure */

这样你就可以使用与已声明的固定大小数组对象相同的语法引用堆分配数组的元素,但在`arr [i]`中,表达式`arr`会衰减为指针,而在`ptr [i]`中,`ptr`已经是一个指针。
可以将同样的方法扩展到更高的维度。您可以分配一个指针数组,然后将每个指针初始化为指向已分配的任何内容数组的开头。
这使您获得了非常类似于二维(或更多)数组的东西,但是您必须自己管理内存;这是灵活性更高的代价。
严格来说,这不是一个二维数组。正如我上面所说,一个二维数组只是一个数组的数组。认为它是一个2D数组可能并非完全不合理,但这与C标准中的用法冲突;这类似于将链表称为1D数组。

comp.lang.c FAQ是一个很好的资源;其中涵盖数组和指针的第6部分尤其出色。


2
一个二维数组实际上是一个指向数组的指针数组。一个由整数组成的二维数组 a[i][j] 占用空间大小为 i*sizeof(int*) 用于存储指针数组,以及 i*j*sizeof(int) 用于存储最终的数组。
一个三维数组 a[i1][i2][i3] 是一个指向指针数组的指针数组。第一层包含 i1 个指针,第二层包含 i1*i2 个指针,第三层包含 i1*i2*i3 个整数。
通常情况下,一个 N 维数组,大小分别为 i1..iN,将具有 N-1 层指针数组和 1 层整数数组。第 N 层的数组长度为 iN,在该层中有 i1..iN-1 的乘积个数组。
因此,一个五维数组:
1 array, length i1, of pointers
i1 arrays, length i2, of pointers
i1*i2 arrays, length i3, of pointers
i1*i2*i3 arrays, length i4, of pointers
i1*i2*i3*i4 arrays, length i5, of ints

希望这可以帮到您(同时我也希望我理解的索引是正确的)。
您发的维基百科链接指的是一种/不同类型的多维数组/。在C语言中,多维数组默认是我刚才描述的方式。您也可以将它们抽象为单维数组。这样可以节省内存并使整个数组连续,但访问元素会更加复杂。以5-D数组为例:
// WARNING I AM CHANGING NOTATION. N1..N5 are the lengths in each direction.
// i1..i5 are the indicies.
int* bigarray = malloc(sizeof(int)*N1*N2*N3*N4*N5);
// now instead of bigarray[i1][i2][i3][i4][i5], write this:
*(bigarray + i1*N2*N3*N4*N5 + i2*N3*N4*N5 + i3*N4*N5 + i4*N5 + i5);

每个术语都有一个偏移量,乘以我们需要偏移的元素数量。例如,要递增一级第一维度,我们需要遍历剩下的四个维度一次来“环绕”,如果您愿意的话。

1
多维数组存储在内存中作为指向指针的形式是高度不可能的。更有可能的情况是它以连续的内存块的形式存储,并且编译器使用指针算术运算来访问元素。 - Some programmer dude
1
一个二维数组实际上是一个指向数组的指针数组。不,它不是,一个二维数组只是一个数组的数组。指针数组可以用来实现类似于二维数组的东西(具有更高的灵活性和开销),但它实际上并不是一个二维数组。 - Keith Thompson
例如,int arr[10][10]; 声明了一个 10 行 10 列的二维 int 数组。看不到任何指针对象。 - Keith Thompson

1

1
“C语言中数组在内存中的存储方式并没有标准化,就我所知。” 是的,实际上是有标准的。 - Keith Thompson

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