访问2D阵列时到底发生了什么

时间:2015-06-18 00:00:32

标签: c++ c

我已经编写了很长一段时间,但只是意识到我对一个非常基本的操作的混淆可能会混淆。

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 ++,有增强和嵌套向量,但它们甚至比上面更重,所以我会尽可能避免使用它们。

更新

老实说,我在这里要做的就是编译我的上面的例子,并对汇编器输出有所了解。那将很快回答我的所有问题。我的误解是我假设所有n-dim数组访问都是通过数学运算来完成的,类似于编译时已知数组的完成方式。我没有意识到[] []正在执行**array的双重任务,并且在动态分配的每个取消引用之间进行适当的索引,而为编译时已知类型执行[i * dim + j]。动态2+维度阵列不是我曾经做过的事情。

2 个答案:

答案 0 :(得分:5)

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 *的数组,可用于索引到mem,就像之前一样(这个数组是静态分配的,但是如果你想从这个函数返回它你想要分配它是动态的。)

答案 1 :(得分:0)

因为类型完全不同。

在第一种情况下,具有两个维度的数组中的类型以及每个维度的长度是已知的,因此可以直接计算地址,并引用一次。

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

编译器知道的所有内容是array是指向某个未知大小的指向int指针的数组的指针。 array

int **array;

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

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

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

这份工作属于你,程序员。编译器不知道任何事情。您可以为每个这样的指针正确地将每个指针分配给int到正确数量的int s,然后将正确数量的指针分配给int s首先。编译器不知道每个中有多少个,为了编写无错误的代码,以某种形式或方式跟踪它是你的工作。

欢迎使用C ++。