笔者在学习C语言后,对C语言还没有深入理解,紧接着就学习了C++。这导致对C语言的理解,长期存在偏差,比如C和C++都有&运算符,C++中&除了取变量地址,还表示引用,导致我一直以为C中也有引用。
今天将另一个关于指针的问题,很长时间以来,笔者都认为数组和指针在很多地方是等价的,比如int *a和int a[];int **a和int a[][]。
那么这个理解有错么?错在哪里呢?
一维数组和指针
我们直接看下面代码:
1  | int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9};  | 
由于“数组的名字等价于数组首元素的地址”,所以上面代码中a、b、c都是指向数组a[10]的首元素(a[0])的地址,三者在用法上完全相同:
1  | 
  | 
但其实a和b的类型都不相同,a是int指针,而b是int指针的指针。
如果第2行改成int *b = a;,两者类型上就相同了,那么这种情况下可以认为a、b等价了吗?还是不行:a[i][j] != b[i][j]
原因如下:
对a来说: 二维数组本质上也是一维数组,只不过这个“一维数组”的每个元素,是一个一维数组。
a[i]表示二维数组的第i个元素(一维数组),a[i][j]表示a[i]这个一维数组的第j个元素。对b来说:b本质是int指针。
&b[i] = b + i,其中b + i表示指向b后第i个元素的地址,元素就是指针指向的类型,对b来说是int。b[i]表示b后第i个int的值,等价于a[0][i]。
所以有下列等式成立:
1  | b[i] == a[0][i];  | 
ps:
下列声明方式,可以使a[i][j] == b[i][j],大家可以试着说明一下原理
 1 int (*b)[3] = a; // pointer to array[13] of int
再回到int **b = a的情况,这种情况下b[i][j]又代表什么意思呢?
b[i][j] = *(*(b + i) + j),由于b的类型是int指针的指针,所以b + i表示b后第i个int指针的地址,*(b + i)表示b后第i个指针。
进一步,*(b +  i) + j就表示该指针(b后第i个指针)后第j个int的地址,*(&(b + i) + j)就表示该int地址对应的int值。
也就是说b[i][[j]对应的也是一个int。
但这不意味着a[i][j] == b[i][j],因为两者在解析式,地址的计算不同,参见本文后面关于“多重指针作为函数参数”的例子。
多维数组作为函数参数
多维数组作为函数参数时,下面3种声明方式都可以
1  | f(int a[2][3]) { ... }  | 
上面3中声明方式,在函数内都可以使用a[i][j]的方式访问数组元素。
ps:
f(int *a[13]) { ... }这种声明方式,无法使用a[i][j]的方式访问数组元素
那么f(int *a) { ... }这种声明方式正确吗?理论上是正确的,只不过不能使用a[i][j]的方式访问元素,而应该使用*(a + 3 * i + j)的方式访问元素。
多重指针作为函数参数:
用数组做函数参数的方式,数组列数是固定的,如果想要实现动态分配内存,需要使用多重数组作为函数参数:
1  | f(int **a) { ... }  | 
上面代码中,arr1可以正常打印,arr2却会报错。