多维数组如何在内存中格式化?

Chr*_*per 165 c memory arrays stack-memory data-structures

在C中,我知道我可以使用以下代码在堆上动态分配二维数组:

int** someNumbers = malloc(arrayRows*sizeof(int*));

for (i = 0; i < arrayRows; i++) {
    someNumbers[i] = malloc(arrayColumns*sizeof(int));
}
Run Code Online (Sandbox Code Playgroud)

显然,这实际上会创建一个指向一堆独立的一维整数数组的指针的一维数组,而"系统"可以在我要求时找出我的意思:

someNumbers[4][2];
Run Code Online (Sandbox Code Playgroud)

但是,当我静态声明一个2D数组时,如下一行...:

int someNumbers[ARRAY_ROWS][ARRAY_COLUMNS];
Run Code Online (Sandbox Code Playgroud)

...是否在堆栈上创建了类似的结构,还是完全是另一种形式?(即它是指针的一维数组吗?如果没有,它是什么,以及如何计算它的引用?)

另外,当我说"系统"时,究竟是什么负责解决这个问题呢?内核?或者C编译器在编译时对其进行排序?

Car*_*rum 129

静态二维数组看起来像一个数组数组 - 它只是在内存中连续布局.数组与指针不同,但因为你可以经常互换地使用它们,有时会让人感到困惑.然而,编译器会正确地跟踪,这使得一切都很好地排列.你必须要小心你所提到的静态二维数组,因为如果你试图将一个数组传递给一个带int **参数的函数,那么坏事就会发生.这是一个简单的例子:

int array1[3][2] = {{0, 1}, {2, 3}, {4, 5}};
Run Code Online (Sandbox Code Playgroud)

在内存中看起来像这样:

0 1 2 3 4 5
Run Code Online (Sandbox Code Playgroud)

完全相同:

int array2[6] = { 0, 1, 2, 3, 4, 5 };
Run Code Online (Sandbox Code Playgroud)

但是如果你试图传递array1给这个函数:

void function1(int **a);
Run Code Online (Sandbox Code Playgroud)

你会得到一个警告(并且应用程序无法正确访问数组):

warning: passing argument 1 of ‘function1’ from incompatible pointer type
Run Code Online (Sandbox Code Playgroud)

因为2D数组不一样int **.将数组自动衰减为指针只会"深入一层",可以这么说.您需要将函数声明为:

void function2(int a[][2]);
Run Code Online (Sandbox Code Playgroud)

要么

void function2(int a[3][2]);
Run Code Online (Sandbox Code Playgroud)

让一切快乐.

同样的概念扩展到n维数组.但是,在您的应用程序中利用这种有趣的业务通常只会让人更难理解.所以要小心.

  • @JasonK. - 不.数组不是指针.在某些情况下,数组"衰减"成指针,但绝对不是*相同. (3认同)
  • 需要明确的是:是的,克里斯“将数组的长度作为单独的参数传递仍然是一个好习惯”,否则使用 std::array 或 std::vector (这是 C++ 而不是旧的 C)。我认为我们同意 @CarlNorum 对新用户的概念和实践,引用 Quora 上的 Anders Kaseorg 的话:“学习 C 的第一步是理解指针和数组是同一回事。第二步是理解指针和数组是不同的。” (2认同)
  • @JasonK。“学习 C 的第一步是理解指针和数组是一回事。” - 这句话是非常错误和误导!这确实是理解它们**不**相同的最重要的一步,但是对于大多数运算符来说,数组被**转换**为指向**第一个元素**的指针!`sizeof(int[100]) != sizeof(int *)`(除非你找到一个带有 `100 * sizeof(int)` bytes/`int` 的平台,但那是另一回事。 (2认同)

caf*_*caf 79

答案是基于C不具备 2D数组的想法- 它具有数组数组.当你声明这个时:

int someNumbers[4][2];
Run Code Online (Sandbox Code Playgroud)

您要求someNumbers是一个包含4个元素的数组,其中该数组的每个元素都是类型int [2](它本身就是一个2 int的数组).

这个难题的另一部分是数组总是在内存中连续排列.如果你要求:

sometype_t array[4];
Run Code Online (Sandbox Code Playgroud)

那总是这样:

| sometype_t | sometype_t | sometype_t | sometype_t |
Run Code Online (Sandbox Code Playgroud)

(4个sometype_t物体彼此相邻放置,中间没有空格).所以在你的someNumbers数组数组中,它看起来像这样:

| int [2]    | int [2]    | int [2]    | int [2]    |
Run Code Online (Sandbox Code Playgroud)

每个int [2]元素本身就是一个数组,如下所示:

| int        | int        |
Run Code Online (Sandbox Code Playgroud)

总的来说,你得到这个:

| int | int  | int | int  | int | int  | int | int  |
Run Code Online (Sandbox Code Playgroud)

  • 看看最终的布局让我觉得 int a[][] 可以作为 int * 访问......对吧? (2认同)
  • @user3238855:类型不兼容,但如果你得到一个指向数组数组中第一个 `int` 的指针(例如,通过评估 `a[0]` 或 `&amp;a[0][0]`)然后是,您可以抵消它以顺序访问每个`int`)。 (2认同)

小智 26

unsigned char MultiArray[5][2]={{0,1},{2,3},{4,5},{6,7},{8,9}};
Run Code Online (Sandbox Code Playgroud)

在内存中等于:

unsigned char SingleArray[10]={0,1,2,3,4,5,6,7,8,9};
Run Code Online (Sandbox Code Playgroud)


Jon*_*n L 5

在回答你的同时:两者,虽然编译器正在做大部分繁重的工作.

在静态分配的数组的情况下,"系统"将是编译器.它将像任何堆栈变量一样保留内存.

对于malloc'd数组,"The System"将是malloc的实现者(通常是内核).所有编译器将分配的是基指针.

编译器总是将处理类型作为它们声明的类型,除了在Carl给出的示例中它可以找出可互换的用法.这就是为什么如果你将一个[] []传递给一个函数,它必须假定它是一个静态分配的平面,其中**被假定为指向指针的指针.