由来: 我们在学习C语言的时候, 经常会写这样的程序: void Func1(int *p) { } void Func2(int a[]) { } int main() { int a[10]; Func1(a); int *p; Func2(p); } 即一维数组与指针作为函数参数的互换使用, 我们可以一眼看出这个程序是可以通过编译的. a是数组a[10]第一个元素的地址, 作为参数传递到函数中去, 接收方是*p, 在函数中可以使用*p, 或者p[0]来访问元素. 看起来数组和指针可以这样互换着使用, 编译器会把它们编译成一样的东西. 问题的提出: 再来看下面的代码, 为什么这些代码会报错? void Func1(int (*p)[10]) { } void Func2(int (*p)[]) { } void Func3(int **p) { } int main() { int a[10][10]; int **p; Func1(a); //Pass Func2(a); //Error1 Func3(a); //Error2 Func1(p); //Error3 Func2(p); //Error4 Func3(p); //Pass } 分析: 当把a作为参数传给函数时候, a代表的是"指向数组的指针", 该数组一共有10个int型的元素. a本身确实是一个指针, 但是这个指针是指向数组的, 不是指向"数组的首元素". 接收方也需要是此类型, int (*p)[10]表示p这个指针是指向数组, 该数组有10个int元素, 跟传入参数形式一致, 因此pass. Error1和Error2自然就是因为不匹配. 当把p作为参数传给函数的时候, p代表的是"指向指针的指针", 也叫做双重指针, 所以接收的时候int **p类型相匹配, pass. 小结: 一维数组和指针可以互换使用, 这是由于编译器帮我们规定了数组名=首元素的地址, 我们才能够这样用. 因此不要以为二维数组和双重指针也可以用类似的方法来互换使用. 这当然不会是本文的最终目的. 我要说明的是, 当我们非得将**p和a[][]这样的形式混用时, 我们应该怎么做. 作为参数时: 看下面的程序: void Func4(int *p[]) { } int main() { int **p; Func4(p); //Pass } 这次我们可以将**p送给*p[], 原因在于: *p[]表示一个指针数组, p就是这个指针数组的数组名, 也就是第一个元素(这个元素是指针)的地址, 说白了, 就是指针的地址. 当然, 作为形式参数, p这里就是一个变量, 即从"地址", 变成了"指针"(变量), 所以送过来的p是双重指针, 接收的p还是双重指针, 当然可以编译通过, 这又是编译器玩的一个花样. 可以看出, 这个过程其实我们还是没有摆脱上面所说的, 编译器帮我们规定了数组名=首元素的地址. 在函数内使用时: 看下面的程序: #include <iostream> using namespace std; void Func1(int **c, int a, int b) { cout<<c[0][0]<<endl; cout<<c[1][0]<<endl; } void main () { int i; int *a = new int[2*3]; int **p = &a; for(i=0; i<2*3; i++) { a[i] = i; } Func1(p, 2, 3); } 编译运行, 如果你像我一样运气好的话, 程序不会崩溃, 你会得到一组数字: 0 3436320 如果定义a[2][3]这样的二维数组, 我们在使用的时候编译器会将a[x][y]采用形如p+3*x+y的寻址方式, 因为编译器知道我们的一行是3个元素. 在Func1函数里, 传入的是双重指针, 接收的是**p也是双重指针, 可是使用的时候呢? 上面的是错误的示例, 因为在函数内部编译器不知道你这个数组是几行几列的. 它没法将c[x][y]转换成p+3*x+y的寻址方式. 所以我需要同时传入两个额外的参数a和b来表示数组的行列, 在使用的时候我就可以手动将c[x][y]转换成p+a*x+y, 再加上取值符号就可以取出正确的值: *(p+a*x+y) 实际上如果你使用c[x][y]这样的形式(如上面的程序), 编译器不会报错, 但是你运行的时候, 就有很大的几率崩溃, 因为编译器将行列错误的进行了解析. 这时编译器是这样编译的: 它取出c[x]作为行的首地址, 再加上y. 由于c[0]一定是传入数组的首地址, 所以c[0][y]这样的访问都是OK的, 只要你不越界. c[1]我们就不知道是个什么东西了, c[1][y]这样的访问就是一个随机数, 当访问到无法访问的地方时, 程序就会崩溃. 最后的总结: 1. 一维数组和指针可以比较简单的互换使用 2. 二维数组和双重指针尽量不要互换使用, 除非你真的知道这些底层的编译原理, 并且有十分的把握

评论