【问题标题】:What exactly happens when accessing a 2D array访问二维数组时究竟发生了什么
【发布时间】:2015-06-18 00:00:31
【问题描述】:

我已经编写了很长时间的代码,但刚刚意识到我对一个非常基本的操作的交互可能会感到困惑。

int array[10][10];

array[1][1] = 0;

这是足够的样本 - 数组地址 + (10 * 1 + 1) * sizeof(int) 分配为 0。

但是在做动态数组的时候呢?

int **array;

// malloc/new the base array of pointers - say 10 as above.
array = malloc(10 * sizeof(int *));

// through pointers and alloc each with ints - again 10 per row as above.
for(int i=0; i<10; i++) 
    array[i] = malloc(10 * sizeof(int);

array[1][1] = 0;

在这个例子中,编译器必须在数组的第一层引用一个数组来获取第二个指针以获取内存。同样,这个过程很简单。

我的问题是:编译器如何知道内存是连续的,因此它可以只做简单的数学运算而不是第一个示例中的引用,而不是第二个示例?

如果答案是:因为编译器事先知道数组的大小,就这样吧,但是有没有办法告诉编译器动态数组是一个单一的分配,因此它可以做更简单的数学运算而不是额外的 deref 和额外的内存碎片分配?或者这是否需要 VLA 支持才能按照this post 正确处理,如下所示:

double (*A)[n] = malloc(sizeof(double[n][n]));

另一个网站也列出了这种可能性:

int r=3, c=4;
int **arr;

arr  = (int **)malloc(sizeof(int *) * r);
arr[0] = (int *)malloc(sizeof(int) * c * r);

这真的让我想知道编译器是如何解决问题的,或者这是否只是使用 VLA 支持的另一种方式。

对于 C++,有 boost 和嵌套向量,但它们比上面的还要重,所以如果可能的话,我会避免使用这些。


更新

老实说,我在这里应该做的只是编译我上面的例子,然后看看汇编器的输出。这将很快回答我所有的问题。我的误解是,我假设所有 n-dim 数组访问都是通过类似于编译时已知数组的数学运算完成的。我没有意识到 [][] 作为**array 正在执行双重职责,在动态分配的每个取消引用之间进行适当的索引,同时对编译时已知类型执行 [i*dim+j]。动态 2+ 维数组不是我必须做的事情。

【问题讨论】:

  • 一个词:types。这就是我们有类型的原因。特别注意int**int[10][10] 的不同之处,后者不能 衰减为前者(它会衰减为int(*)[10] 或类似的东西)。我不真的明白你在问什么,因为你的整个问题(根据定义)是基于一个基本的误解。不幸的是,我们还不确切地知道这种误解是什么。 :) 这可能会在聊天室中得到更好的解决。

标签: c++ c


【解决方案1】:

int **int[10][10] 的类型不一样。

int ** 类型不包含有关元素数量的任何信息,因为它不是数组类型,而是指针类型(特别是指向 int * 的指针)。 int[10][10] 包含编译器需要知道有多少元素的所有信息。

如果您尝试在指针与数组上使用sizeof,您会注意到这一点。下面的程序演示了这一点:

#include <stdio.h>                                                             

int main ( int argc, char * argv[] ) { 

    printf( "Size of int[10][10]: %lu\n", sizeof(int[10][10]) );
    printf( "Size of int**: %lu\n", sizeof(int**) );

    return 0;
}

动态内存不是在编译时分配的,它是在运行时编译的,所以编译器改变动态内存的创建方式,最终可能会修改程序的行为,(IE。通过分配超过在一个连续的块中可用,并且 malloc 返回 null)。

你当然可以自己获得一块连续的内存,为100 ints分配足够的内存,然后自己算术:

int * mem = malloc( 10 * 10 * sizeof( int ) );

现在 mem 是 100 个整数的连续块

int * array[10];
for ( int i = 0; i < 10; i++ )
    array[i] = &mem[10*i];

现在你有一个包含 10 个 int *s 的数组,可以用来索引 mem,就像以前一样(这个数组是静态分配的,但是如果你想从这个函数中返回它,你会想要动态分配它而是)。

【讨论】:

  • 这是答案的一半。另一半是int** 类型允许编译器知道第二级不是由数组组成,而是由指针组成(在那个阶段,这些指针是否指向更多数组是不相关,因为它们是否指向动态分配的内存)。当您在每一步都考虑 types 时,扩展 [] 表示法所发生的所有操作都会顺利地自行排序,但我真的不会费心去遍历在两个过程中发生的每一件事各种二维数组访问!
  • @Paulpro 因此,让我们回过头来看看是否有一种方法可以在单个动态分配的内存块上使用 array[][] 表示法,就像使用堆栈一样。我的最后两个示例似乎是通过让编译器做出假设来做到这一点的。那么,这是如何在 C 中完成的?
  • @MichaelDorgan 我不确定你是否看到了我的编辑,因为我是在你发表评论前几分钟做的。我演示了一种分配连续内存块的方法,然后创建一个指针数组以像二维数组一样对其进行索引。
【解决方案2】:

因为类型完全不同。

第一种情况,二维数组中的类型,每个维度的长度是已知的,所以可以直接计算地址,引用一次。

第二种情况,类型完全不同,类型是双指针。指向某个类型的指针数组的指针,在您的情况下,是 int

编译器只知道array 是一个指向数组的指针,大小未知,指向int 的指针。 array 是一个

int **array;

array[x] 是一个指向 int 的指针。请注意,它只是指向int 的指针。它没有说明它指向多少ints。

它可以只是一个int,也可以不是。每个指向int 的指针都可以指向不同数量的int

例如,array[0] 可能指向十个整数。 array[1] 可能指向二十个ints。没有什么要求 array[x] 指向与 array[y] 相同数量的ints。

那份工作属于你,程序员。编译器什么都不知道。对于每个这样的指针,您可以将每个指针正确分配给int,分配给正确数量的ints,然后首先将正确数量的指针分配给ints。编译器不知道每个有多少,你的工作就是以某种形式或方式跟踪它,以便编写无错误的代码。

欢迎使用 C++。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-25
    • 2010-10-04
    相关资源
    最近更新 更多