C 函数参数差异:双指针与2D数组
请理解,在声明的情况下,以下两行不相等(在分配方面) 但是当这些被用作函数参数时呢C 函数参数差异:双指针与2D数组,c,arrays,function,pointers,C,Arrays,Function,Pointers,请理解,在声明的情况下,以下两行不相等(在分配方面) 但是当这些被用作函数参数时呢 void func1(double** v1) void func2(double v2[3][3]) 除了关于func2中值的数量的提示外,是否存在功能差异 严格来说,double**arr和double arr[3][3]是两种完全不同的类型 double**arr不是数组,它只是指向double的指针。你有 没有更多的信息。您无法事先知道arr是一个双精度数组数组。因此,执行arr[1][2]将意味着“跳
void func1(double** v1)
void func2(double v2[3][3])
除了关于
func2
中值的数量的提示外,是否存在功能差异 严格来说,double**arr
和double arr[3][3]
是两种完全不同的类型
double**arr
不是数组,它只是指向double
的指针。你有
没有更多的信息。您无法事先知道arr
是一个双精度数组数组。因此,执行arr[1][2]
将意味着“跳过第一个double
指针,并将第二个指针作为第三个元素(索引2)的数组取消引用”
因此,x=arr[1][2]
可以翻译为以下伪代码:
tmp=memory[+sizeof(double*)]
x=内存[+2*sizeof(双精度)]
双arr[3][3]
相反,它是一个恒定大小的数组,可以做更多的假设。它保证在内存中是连续的,执行arr[1][2]
意味着“跳过3个double
的第一个数组,然后跳过第二个数组的两个double
并查看第三个(索引2)”
因此,x=arr[1][2]
可以翻译为以下伪代码:
x=内存[+(3+2)*sizeof(双精度)]
作为一个实际的例子,考虑下面的程序:
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:在函数“main”中:
arrayparam.c:13:8:警告:从不兼容的指针类型[-Wincompatible指针类型]传递“func1”的参数1
职能1(常数);
^~~~~~~~~
arrayparam.c:1:5:注意:应为“int**”,但参数的类型为“int(*)[3]”
int func1(int**v1){
^~~~~
arrayparam.c:14:8:警告:从不兼容的指针类型[-Wincompatible指针类型]传递“func2”的参数1
func2(双重ptr);
^~~~~~~~~~
arrayparam.c:5:5:注意:应为“int(*)[3]”,但参数的类型为“int**”
int func2(int v2[3][3]){
您可以从生成的程序集中非常清楚地看到,这两个函数执行完全不同的操作,而此程序集代码实际上与我上面编写的伪代码完全相同:
func1()
:
func2()
:
您是否尝试同时使用这两个函数并查看会发生什么?将const_arr传递给func2和func1在功能上不是很相似吗?不,不会。您不能这样做,正如您从GCC警告中看到的那样。这将导致
func1()
非常严重的segfault。您的意思是func2将崩溃吗?不,func1(const_arr)
崩溃。请看我的答案。阅读警告。您不能调用func1(const\u arr)
,也不能调用func2(double\u ptr)
。
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);
}
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
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