正文

数组和指针的互换2012-02-01 23:46:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/katwood/53210.html

分享到:

由来:

    我们在学习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. 二维数组和双重指针尽量不要互换使用, 除非你真的知道这些底层的编译原理, 并且有十分的把握

阅读(1739) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册