How are multi-dimensional arrays formatted in memory?
在C中,我知道可以使用以下代码在堆上动态分配二维数组:
1 2 3 4 5 |
显然,这实际上创建了一个指向一组单独的一维整数数组的一维指针数组,"系统"可以在我要求时理解我的意思:
1 | someNumbers[4][2]; |
但是当我静态地声明一个二维数组时,如下面的一行…:
1 | int someNumbers[ARRAY_ROWS][ARRAY_COLUMNS]; |
…类似的结构是在堆栈上创建的,还是完全是另一种形式?(即,它是一个一维指针数组吗?如果没有,它是什么?如何找到它的引用?)
另外,当我说,"系统",什么才是真正的责任来解决这个问题?内核?还是C编译器在编译时对其进行排序?
一个静态的二维数组看起来像一个数组数组-它只是在内存中连续排列。数组与指针不是一回事,但由于您经常可以互换使用它们,因此有时会混淆。不过,编译器保持正确的跟踪,这使得所有的东西都很好地排列起来。您必须像前面提到的那样小心处理静态的二维数组,因为如果您试图将一个数组传递给一个接受
1 | int array1[3][2] = {{0, 1}, {2, 3}, {4, 5}}; |
内存如下:
1 | 0 1 2 3 4 5 |
完全相同:
1 | int array2[6] = { 0, 1, 2, 3, 4, 5 }; |
但如果您试图将
1 | void function1(int **a); |
您将收到警告(应用程序将无法正确访问阵列):
1 | warning: passing argument 1 of ‘function1’ from incompatible pointer type |
因为二维数组与
1 | void function2(int a[][2]); |
或
1 | void function2(int a[3][2]); |
让一切都快乐。
同样的概念也适用于N维数组。不过,在应用程序中利用这种有趣的业务通常只会使您更难理解。所以在外面要小心。
答案是基于这样一个想法:C没有真正的二维数组——它有数组。当你申报时:
1 | int someNumbers[4][2]; |
您要求
另一个难题是数组总是在内存中连续排列。如果你要求:
1 | sometype_t array[4]; |
那么,这看起来总是这样:
1 | | sometype_t | sometype_t | sometype_t | sometype_t | |
(4个
1 | | int [2] | int [2] | int [2] | int [2] | |
每个
1 | | int | int | |
所以总的来说,你会得到:
1 | | int | int | int | int | int | int | int | int | |
1 | unsigned char MultiArray[5][2]={{0,1},{2,3},{4,5},{6,7},{8,9}}; |
内存等于:
1 | unsigned char SingleArray[10]={0,1,2,3,4,5,6,7,8,9}; |
同时回答您的问题:两者都是,尽管编译器正在做大部分的繁重工作。
对于静态分配的数组,"系统"将是编译器。它将像为任何堆栈变量那样保留内存。
对于malloc的数组,"系统"将是malloc的实现者(通常是内核)。编译器将分配的所有内容都是基指针。
编译器总是按照声明的方式来处理类型,除非在Carl给出的示例中,它可以找出可互换的用法。这就是为什么如果您将一个[[]传递给一个函数,它必须假定它是一个静态分配的平面,其中**假定为指向指针的指针。
要访问特定的二维数组,请考虑数组声明的内存映射,如下代码所示:
1 2 3 | 0 1 a[0]0 1 a[1]2 3 |
要访问每个元素,只需将感兴趣的数组作为参数传递给函数即可。然后对列使用偏移量来单独访问每个元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |