C指针系列二

一、函数调用——传址

      函数调用中将数组作为参数进行传递,此时,它和指针的关系变得很微妙。我们应该要弄明白调用时到底传递的是什么。首先分析下面的简单代码:

 1 int array[5] = {1, 2, 3, 4, 5};
 2 void print( int a[] ) {
 3     int i = 0;
 4     for(; i < sizeof(a)/sizeof(a[0]); ++i)
 5         printf(“%d  ”, a[i]);
 6 }
 7 int main()
 8 {
 9     print(array);
10     return 0;
11 }

  调用print(array)时打印出来的结果是1(而我们的预想结果是1 2 3 4 5), n = sizeof(a)/sizeof(a[0])是一种常用的表示数组元素个数的方法。之所以没有打印预料的结果,是因为将数组作为实参调用函数时,所做的工作仅仅是在栈中开辟一个4字节的空间,用来存放数组首元素地址的副本。故此时的a仅是个指向array[0]的指针,sizeof(a) /sizeof(a[0])=1,ok…问题解释完毕!!!

  刚才啰嗦的半天得到的结论是:数组作为实参传递时,传递的只是一个指针(数组首元素地址的副本),不管你的函数形式是如下哪一种:

        void print( int a[] )    void print( int *a )---------两种形式等价

这就决定了我们在C/C++中传递数组时必须带上一个n(表示数组元素个数),故函数print正确的形式是void print( int *a, int n)或void print(int a[], int n)。

谈到这里我们马上想到了一个例外——如果我们想将一个字符串中的所有字符转换为大写的(这里假设字符串的字符全是小写字符),代码应该是这样:

1 void UpperCase( char str[] ){
2     int i = 0;
3     while( str[i] != ‘\0’ )
4         str[i++] -= (‘a’-‘A’);
5 } 

  这里没有添加一个形参n去表示字符串长度,难道传递的是整个数组而不是指针???其实,这里传递的也只是字符数组首元素的地址而已,之所以不需要指定数组的长度,是因为字符数组有个结尾标识符’\0’,即隐含的传递了数组大小的信息。基于这个思想我们可以修改上面的情况——假设要打印出5个童鞋的英语成绩(不可能为负,故添加-1作为结束符):

1 int array[5] = {1, 2, 3, 4, 5, -1};//末尾添加了一个标识
2 void print( int a[] ) {//这里不用传递数组大小n
3     int i = 0;
4     while( a[i] != -1)                
5             printf(“%d  ”, a[i]);
6 }

  上面谈到的情形——将数组作为实参进行传递的时候,具体传递了什么——虽然不复杂,但还是应该弄清楚的好,至少它体现了C/C++设计思想的一个很小的部分。java和pyhton舍弃了指针,用引用替代之;java中的数组或是python中的列表都被视为对象,即它们都对数组或列表进行了封装,并提供了一些有用的属性(如数组的长度信息),加上以引用的方式进行传递,使得操作更加便利(至少不需要传递一个表示数组大小的参数)。C++中的数组是内置型,操作方式和C中的一样,故大家更倾向于使用vector。

二、指针的应用 

(1)指针数组

   下面先列出几个看起来有点复杂的声明,再聊聊指针数组及多级指针

          int *a[5];//声明a为指针数组

        int **b[5];//声明b为指针数组

                      int ***c[5];//声明c为指针数组

   a,b和c都是指针数组,只是数组元素不同;a中的元素是指向int型变量的指针,b中的元素是指向int*型变量的指针,同理,c中的元素是指向int**型变量的指针。谈到二级指针或是更高级的指针时,很抽象的赶脚啊!!!其实多级指针没有多维数组来的抽象,不管是多少级的指针,它都只是一个4个字节的变量,用来存放其它变量的地址而已,不要将其视为一个多么可怕的潘多拉盒子。下面的赋值完全正确:

           int ***p = NULL;

           int *pi = &p;//ok…编译器最多就是给出一个警告,不想看到的警告的话就强制性转换吧!!!

      一点也不神秘嘛。。。只要让类型匹配(逃过编译器的法眼),指针随你怎么操作,前提是你的脑子里面必须有张很清晰的内存分布图。下面摆出一个动态创建二维数组的模板程序(更高维的依此类推)

 1 int main()
 2 {
 3     int **p = NULL;//记得初始化,Don’t be a slacker!!!
 4     int i, j;
 5     int m, n;
 6     scanf(“%d %d”, &m, &n);//动态创建m*n二维数组
 7     if((p = (int **)malloc( sizeof(int*)*m )) != NULL){//作为一个合格的程序员不要忘了这步!!!
 8         for(i=0; i<m; ++i){
 9             if((p[i] = (int*)malloc( sizeof(int)*n))!= NULL){
10                 memset(p[i], 0, sizeof(int)*n);//如果最后的结果全是垃圾数据时应该想起这步!!!
11             }
12         }
13       for(i=0; i<m; ++i){
14           for(j=0; j<n; ++j)
15               scanf("%d", &p[i][j]);
16       }
17       //具体的数据处理操作
18       //...
19       for(i=0; i<m; ++i)
20           free(p[i]);//忘了这步真是活该没有妹子要!!!
21       free(p);
22     }
23     return  0;
24 }

(2)数组指针

  先摆上一个程序题,来引出后面的话题:

1 #include <stdio.h>
2 int main( void )
3 {
4     int a[5][10];
5     printf(“%d  %d\n”, a+1, &a+1 );//假设a的值是1310000!!!
6     return  0;
7 }

  暂且搁置一个这个题目,下面来讨论数组指针和二维数组的关系,了解这层关系之后,使用二维数组就会容易的多。。。就从数组指针的声明形式开始吧!!!

            int (*a)[5];//声明a为数组指针

            int p[5][5];//作对比之用

 两者都是定义了元素个数为5的数组(每个元素又是一个数组),可以将a和p视为等价, 看看下面两者的关系图。

                           

  要弄明白一个指针指向的数据类型,最好的办法是对其进行加减操作。a进行加1操作跳过的字节数=sizeof(int)*5, a加1跳过的是一个元素个数为5的数组耶,故a是一个数组指针,指向的是一个数组而不是一个int型变量;同样的方法来分析&p, p和*p。三者的值是一样的(存放的是相同的地址),但是三者代表的含义不同。&p加1操作后跳过的字节数=sizeof(int)*5*5,表示&p指向的是一个5行5列的int型二维数组;而p加1操作后跳过的字节数=sizeof(int)*5,表示p指向的是一个元素个数为5的int型一维数组(和a一样);*p加1后跳过的字节数=sizeof(int),表示*p指向的是一个int型变量。同样的数据类型可以直接赋值咯!!!ok…下面这么写绝对没有错:

    int (*a)[5];

    int p[5][5];

    a = p;//都是指向一个拥有5个元素的int型数组

到这里就可以轻松的将上面那道程序题解出来了。。。。

   遵循上面谈到的内容,考虑二维数组作为实参进行传递的时候,传递的是什么东东???传递的也是p的一个副本(也可以说是*p或&p的值的一个副本,因为三者的值都相同),告诉起始地址还不够,必须加上元素个数n,是一维的个数还是二维的个数呢???先看看下面代码之后在继续闹吧…

 1 #include <stdio.h>
 2 void print( int (*a)[5], int n ){//写成print( int a[][], int m, int n)行不???
 3     int i, j;
 4     for(i=0; i<n; ++i){
 5         for(j=0; j<5; ++j)
 6             printf("%d ", a[i][j]);
 7         printf("\n");
 8     }
 9 }
10 int main()
11 {
12     int p[2][5] = {{1,2,3,4,5}, {6,7,8,9,10}};
13     print(p, 2);
14     return 0;
15 }

 

 

posted @ 2013-06-05 15:14  Happy_he  阅读(224)  评论(0编辑  收藏  举报