我已经编写了很长一段时间,但只是意识到我对一个非常基本的操作的混淆可能会混淆.
int array[10][10];
array[1][1] = 0;
Run Code Online (Sandbox Code Playgroud)
这样就足够了 - 数组地址+(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;
Run Code Online (Sandbox Code Playgroud)
在此示例中,编译器必须引用数组第一层上的数组以获取第二个指针以获取内存.同样,这个过程是直截了当的.
我的问题是:编译器如何知道内存是连续的,因此在第一个例子中它只能做简单的数学而不是引用,而不是第二个?
如果答案是:因为编译器事先知道数组的大小,所以就是这样,但有没有办法告诉编译器动态数组是单个分配,因此它可以做更简单的数学而不是额外的deref和额外的内存碎片分配?或者这是否需要VLA支持才能按照此帖发布,如下所示:
double (*A)[n] = malloc(sizeof(double[n][n]));
Run Code Online (Sandbox Code Playgroud)
另一个网站也列出了这种可能性:
int r=3, c=4;
int **arr;
arr = (int **)malloc(sizeof(int *) * r);
arr[0] = (int *)malloc(sizeof(int) * c * r);
Run Code Online (Sandbox Code Playgroud)
这真让我想知道编译器如何解决问题,或者这只是使用VLA支持的另一种方式.
对于C++,有增强和嵌套向量,但它们甚至比上面更重,所以我会尽可能避免使用它们.
更新
老实说,我在这里要做的就是编译我的上面的例子,并对汇编器输出做一些看法.那将很快回答我的所有问题.我的误解是我假设所有n-dim数组访问都是通过数学运算来完成的,类似于编译时已知数组的完成方式.我没有意识到[] []正在执行双重任务,因为**array在动态分配的每个解除引用之间进行适当的索引,而对于编译时已知类型执行[i*dim + j].动态2+维度阵列不是我曾经做过的事情.
类型int **和int[10][10]不一样.
该类型int **不包含有关有多少元素的任何信息,因为它不是数组类型,它是指针类型(特别是指向a的指针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;
}
Run Code Online (Sandbox Code Playgroud)
动态内存不是在编译时分配的,它是在运行时编译的,因此编译器改变动态内存的创建方式,最终可能会修改程序的行为(IE.通过分配超过可用的内容)一个连续的块,并且malloc返回null).
当然,你可以通过为内存分配足够的内存来获得连续的内存块100 ints,然后自己进行算术运算:
int * mem = malloc( 10 * 10 * sizeof( int ) );
Run Code Online (Sandbox Code Playgroud)
现在mem是一个100个整数的连续块
int * array[10];
for ( int i = 0; i < 10; i++ )
array[i] = &mem[10*i];
Run Code Online (Sandbox Code Playgroud)
现在你有一个10 int *秒的数组,可以用来索引到mem,就像之前一样(这个数组是静态分配的,但如果你想从这个函数返回它,你会想要动态地分配它).