严格来说,double **arr 和 double arr[3][3] 是两种真正不同的类型。
double **arr 不是数组,它只是一个指向double 的指针。你有
没有更多信息。您无法事先知道 arr 是一个双精度数组。因此,arr[1][2] 将意味着“跳过第一个 double 指针并取消引用第二个指针作为采用第三个元素(索引 2)的数组”。
所以,x = arr[1][2] 可以翻译为以下伪代码:
tmp = memory[<arr_address> + sizeof(double *)]
x = memory[<tmp_address> + 2*sizeof(double)]
double arr[3][3],相反,是一个固定大小的数组,可以做更多的假设。它保证在内存中是连续的,并且 arr[1][2] 意味着“跳过第一个数组 3 double,然后跳过第二个数组的两个 double 和看第三个(索引 2)”。
所以,x = arr[1][2] 可以翻译为以下伪代码:
x = memory[<arr_address> + (3 + 2)*sizeof(double)]
作为一个实际示例,考虑以下程序:
int func1(int** v1) {
return v1[1][2];
}
int func2(int v2[3][3]) {
return v2[1][2];
}
int main(void) {
int const_arr[3][3]; // Values are not important.
int **double_ptr; // We don't really run this code!
func1(const_arr);
func2(double_ptr);
}
编译后,它会给出以下(非常相关的)警告:
arrayparam.c: In function ‘main’:
arrayparam.c:13:8: warning: passing argument 1 of ‘func1’ from incompatible pointer type [-Wincompatible-pointer-types]
func1(const_arr);
^~~~~~~~~
arrayparam.c:1:5: note: expected ‘int **’ but argument is of type ‘int (*)[3]’
int func1(int** v1) {
^~~~~
arrayparam.c:14:8: warning: passing argument 1 of ‘func2’ from incompatible pointer type [-Wincompatible-pointer-types]
func2(double_ptr);
^~~~~~~~~~
arrayparam.c:5:5: note: expected ‘int (*)[3]’ but argument is of type ‘int **’
int func2(int v2[3][3]) {
从生成的汇编中可以非常清楚地看到,这两个函数做了完全不同的事情,而这个汇编代码真的和我上面写的伪代码一模一样:
func1():
664: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi
668: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] ; load the array
66c: 48 83 c0 08 add rax,0x8 ; go 1*8 = 8 bytes forward (sizeof(int*))
670: 48 8b 00 mov rax,QWORD PTR [rax] ; dereference that pointer
673: 8b 40 08 mov eax,DWORD PTR [rax+0x8] ; go 2*4 = 8 bytes forward (2*sizeof(int)) and take the value there
func2():
67c: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi
680: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] ; load the array
684: 48 83 c0 0c add rax,0xc ; go 3*4 = 12 bytes forward
688: 8b 40 08 mov eax,DWORD PTR [rax+0x8] ; go another 2*4 = 8 bytes forward and take the value there