第三次课大纲

  在第二次课学习了指针的定义和引用,那么指针有什么用那?主要是两个用途,一种是将指针作为子函数参数时,在子函数内部可以对外面的变量进行修改;二是函数只有一个返回值,当需要返回多个值的时候可以通过将指针作为函数参数来完成。

(1)指针作为函数参数

  在学习函数调用时,函数参数的传递是值传递,即将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。比如下面的例子,如果再g函数中修改k的值i的值是否会改变?大家可以试验下:

 1 #include <stdio.h>
 2 void g(int k);
 3 
 4 int main(void) {
 5     int i=6;
 6     printf(" i=%d\n",i);
 7     g(i);
 8     return 0;
 9 }
10 
11 void g(int k){
12     printf(" k=%d\n",k);
13 }
View Code

  结果肯定i的值不会改变。那么如果将参数换成指针那?参照g函数再写一个f函数(对照着g解释f): 

 1 #include <stdio.h>
 2 void g(int k);
 3 void f(int *p);
 4 
 5 int main(void) {
 6     int i=6;
 7     g(i);
 8     printf(" &i=%p\n",&i);
 9     f(&i);
10     return 0;
11 }
12 
13 void f(int *p){
14     printf(" p=%p\n",p);
15 } 
16 
17 void g(int k){
18     printf(" k=%d\n",k);
19 }
View Code

  从这个例子看出g和f函数很类似,只是传递的参数的类型不同,一个是整数类型,一个是指针类型,都是值传递,g传递的是整数,f传递的是一个地址。

  我们知道在g函数里修改的k的值,i的值没有变化,那么如果在f里修改*p的值,main里i的值是否变化那?再上述程序上添加代码如下:

 1 #include <stdio.h>
 2 void f(int *p);
 3 void g(int k);
 4 
 5 int main(void) {
 6     int i=6;
 7     printf("&i=%p\n",&i);
 8     f(&i);
 9     g(i);
10     printf("i=%d\n",i);
11     return 0;
12 }
13 
14 void f(int *p){
15     printf(" p=%p\n",p);
16     printf("*p=%d\n",*p);
17     *p = 26;
18 }
19 
20 void g(int k){
21     printf(" k=%d\n",k);
22 }
View Code

  在调用f函数之后调用g函数和输出i的值,发现i的值和k的值都发生了变化。这就是g和f函数的不同点:c语言的函数在调用时发生的参数的转移是一种值的传递,我们把值传进了函数,所以函数和调用它的地方没用任何的联系,现在情况有点不一样了,但是我们仍然坚持说,现在这个传递依然是值的传递,地址值被传进了函数,但是因为传进来的是地址,所以通过这个地址在函数内部可以以*p的方式访问到外面的变量的值。

(2)函数通过指针返回多个值

  那么在子函数里可以访问到main中的变量又有什么用那?再看swap例子(听课笔记:指针的应用场景),当传递到swap中的是整型数值时,交换只发生在swap函数内部。而当传递的是指针类型的地址时main里的两个值也实现了交换。为什么?如下图所示。

 

 

 

 

 

  所以以指针作为函数参数时,可以在函数内部访问到外面变量,如何访问,可以读可以写。

  在上面的例子中,swap函数在做交换后,返回来2个值,只返回a不够,还应该返回b。这种让子函数返回两个值的需求,通过函数的返回值是不能实现的,但是通过指针就可以实现,如何实现?通过指针带回,也就是说传入的参数实际上是需要保存带回的结果的变量。再看下例,(2.1)函数返回运算的状态,结果通过指针返回(以听课笔记:指针的应用场景中的divide为例进行说明)

  以上2点就是指针的主要用途,那么这两点在使用指针操作数组时也很常见,并且利用指针可以替代通过数组下标所能完成的所有操作,并且利用指针编写的程序比数组下标编写的程序执行速度还要快,下面讨论下指针和数组的关系:

(1)数组中各元素的地址

  使用int a[10]定义数组a,其实是在内存里申请了10个存储空间,每个单元都有相应的地址,那我们输出各个单元地址查看他们的地址有什么关系:

  从结果可以看出:通过a[0]、a[1]、a[2]的地址可以发现数组各元素的地址是递增的关系,并且每个元素地址之间的差值是4(可以尝试将所有数组元素的地址都输出出来验证),注意和相邻变量的地址分配是不一样的,相邻变量中后定义的变量比先定义的地址要小。

(2)数组名是一个特殊的指针

  另外从上述实验结果可以看出:&a=a=&a[0],在c语言中,数组的地址就是数组首元素a[0]的地址,数组名a本身就是地址,就是这个首地址,可以不使用&取地址符,所以要求数组地址时直接输出数组名就可以。在前面介绍过,指针变量的值就是地址,那么数组名a的值也是地址,可否当指针使用?能否对a使用*运算符,下面试一下:

  从结果可以看出完全可以,对a进行*运算和[]运算结果是一样的,都是取该地址指向的变量的值。

  既然a可以使用*运算符,那么将a赋值给指针变量p,变量p是否也可当数组首地址,对p进行[]运算符那?,比如输出数组中第5个元素的值,其实直接打印a[4]即可实现,那么p[4]结果是什么那?

  从结果看出p也可以做[]运算来访问数组中的每个元素,那么是不是就说明指针p和数组名a就是一样的?或者数组名a就是一个普通指针那?答案是否定的,实际上数组名a是const常量指针,在之前讲数组时说到过,如果定义两个数组,int a[],int b[],使a=b是不可以的,数组之间是不能赋值的。而int *q=a,这个是可以的,一个不可以一个可以有什么区别那?实际上我们说的int a[]可以被看作int * const a,const意思是a是常量,不可以改变,它是这个数组就不可以是别的数组了,所以数组名在被当作值使用时,可理解为或者相当于一个常量指针,不可以赋值,不可以代表别的东西

 (3)数组名作为函数参数

  我们知道如果通过函数参数传递一个普通变量,那么参数接收到的是值,如果传递一个指针变量,参数接收到的也是值,只不过这时的值是地址。那么数组是什么?将数组作为值传给一个函数,在函数的参数里有一个数组变量来接收这个数组,看下到底接收到数组变量的什么东西呢?

  新建test函数,以数组作为参数,输出a在main和test中的值,结果地址是一样的,这说明什么,说明在test里的这个数组就是main里的这个数组,他们是同一个。再比如在test中修改a[2]的值,然后在main中调用完test函数后输出a[2]的值,发现确实结果确实改变了。

  那么在test函数中能不能计算出数组a的个数?在test和main中分别添加printf函数:

  从结果可以看出,在main里a的大小是64,而在test函数中数组a的大小为8,8是什么,在64位机器下,8刚好和一个指针的大小是一样的,和地址的大小是一样的。test参数中int a[]就是指针,那么将这个参数修改为int a[10],在test中sizeof没有办法得到这个数组元素的个数,原因就在于它其实就是个指针,它只是样子看上去像一个数组,那么既然它实际上是一个指针,我们把它写成像一个指针行不行,a[]写成*a,发现编辑和运行结果都没有变化。所以我们可以说数组和指针存在某种联系:函数参数中的数组实际上是指针,也就是说sizeof(a)= siziof(int*),但是对于这种指针可以使用数组的方括号[]运算符来进行运算。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

学生反应知识点有点多,前后关联性不强,指针作为函数参数应该和第二次课讲,而数组和指针是一次课。

posted on 2017-03-05 22:45  niuxiaoxia  阅读(275)  评论(0编辑  收藏  举报

导航