《第九章 指针.ppt》由会员分享,可在线阅读,更多相关《第九章 指针.ppt(144页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、课程主讲人:第九章 指针 9.1 地址和指针9.1.1 指针概述1变量的“直接访问”方式凡在源程序中定义的变量,在编译时系统都给它们分配相应的存储单元,每个变量所占的存储单元都有确定的地址。具体的地址是在编译时分配的。例如:short a=1,b=2;float c=3.4,d=5.6;char e=x,f=y;2变量的“间接访问”方式“间接访问”方式就是把一个变量的地址放在另一个变量的存储单元中。如图9-2所示。图9-2 将变量的地址放在另一个变量的存储单元中 变量pa、pb分别用来存放变量a、b的地址(即&a、&b)。要得到变量a的值,可以先访问变量pa,得到变量pa的值1010后,再通过
2、地址1010找到它所指向的存储单元中的值。这种把地址存放在一个变量中,然后通过先找出地址变量中的值(一个地址),再由此地址找到最终要访问的变量的方法,称为“间接访问”方式。 3指针概念的引出 通过用一个地址变量存储某一普通变量的起始地址,进而指向一个普通变量。可以将它形象地表示为如图9-3所示的逻辑关系。图9-3 指针指向变量 一个变量的地址称为该变量的“指针”。例如,地址1010是变量a的指针。存放地址的变量叫“指针变量”。从图9-3可以看到,pa是指针变量,因为pa中存放着变量a的地址,称为pa指向变量a,通过变量pa就能找到a的值。请区分“指针”和“指针变量”这两个概念,指针是一个地址,
3、而指针变量是存放地址的变量。 9.2 一维数组的指针表示方法9.2.1 一维数组的地址表示法(地址法) 在程序中定义一个数组后,编译系统就会为它分配一个可以容纳数组中所有元素的存贮区,其中数组名代表这个数组的起始地址。那么在C语言中,数组各元素的地址是如何表示和计算的呢?下面讨论一下一维数组的地址表示法。 定义一个含有5个元素的一维数组a如下: int a5=1,3,5,7,9; 它在内存中的分配情况如图9-4所示。return图9-5 一维数组和二维数组的地址比较对比结果见表9-1。 从表9-1中可以看出:右边与左边的差别仅在于,右边以a0代替了左边的b。二维数组a的第1行和第2行的情况与此
4、相类似,无非是分别用a1和a2代替a0而已。9.3.2 二维数组中的行地址与列地址 下面分析二维数组名a和a0,a1,a2的关系。 前面已经提到,一个二维数组可以认为是由若干个一维数组所组成。如果定义了一个二维实型数组float a35,则数组每行占54=20个字节。假设第0行从地址1000开始,则第1行从1020开始,第2行从1040开始,也就是说对应于a的值是1000,a+1的值为1020,a+2的值为1040。千万不要以为a+1是加4个字节,因为现在a是二维数组,对a来说,它的每一个“元素”是一行而不是一个基本数据元素。图9-6 单精度浮点型数组a35的各行首地址在内存中位置图9-7 二
5、维数组各元素行、列地址的表示从图9-7可以看到有二个方向的控制: a、a+i或&ai控制的是行,或者说它们是行控制。 *(a+i)+j或ai+j控制的是列,即列控制。 虽然a+1和a1的值均是1020,但含义(属性)是不一样的。a+1指向a1,是向纵向移动;a1指向a10,是向横向移动。 例如*(*(a+1)+2),表达式中的*(a+1)相当于a1,代表第1行第0列地址;那么对于*(a+1)+2是指第1行第2列地址,所以*(*(a+1)+2)就是1行2列元素的值。 既然二维数组的地址有行地址与列地址的区别,那么如果在程序中定义指针变量,也应该确定是指向行、还是指向列的问题,二者不能混淆。 1)
6、程序中定义指针变量的形式为int (*p)5,它表示p是一个行指针,每行含有5个基本整型元素(一维数组)。如图9-8所示。(2)对于行指针变量的定义形式,需要注意的是,括号是不可缺少的,它表示p先与“*”结合,是指针变量。如果写成*p5,由于方括弧优先于“*”,p先与 相结合,就成了数组类型,那么*p5就是后面我们要讲的指针数组.(3)printf函数中的*(*(p+i)+j)表示第i行第j列元素值。由于p是指向行(一维数组)的指针变量,因此,应赋予它行地址,于是p=a 是合法的。如果写成p=a0或p=&a00反倒不对了,因为类型不匹配。*(*(p+i)+j)的含义就是数组第i行第j列元素的值
7、,例如*(*p+3)是第0行第3 列元素的值4(见图9-8)。图9-8 行指针的使用 return图9-9 指针作为函数参数的数据传递示意图 在例9.9中,我们实现了调用一次函数得到两个所需的值。前面曾得到这样的结论,即“函数参数的传递是单向的,调用一次函数最多可以得到一个返回值”。初看起来,上面的例子与这个结论似有矛盾,实际上这并不矛盾。程序中从main函数传递到fun函数的数据是变量x和y的地址,即&x和&y。这两个函数参数&x和&y在函数调用过程中,传递方向确实是单向的,也就是说,只有&x和&y传给p1和p2的过程,而没有从p1和p2将数据传回到&x和&y的过程。因此,函数参数&x和&y
8、的值并没有改变。 以指针作为函数参数时,数据传送的方向是“双向的”。函数fun()是根据形参指针变量中存放的地址值向实参变量单元赋值,从而使实参变量单元得到所需的值。在C语言中,通常把这种用指针作为函数参数的传递方式称为“引用调用”。通过以上叙述可看出:用指针(地址)作函数参数,可以实现“通过被调用的函数改变主调函数中的变量值”。例9.9实际上只是利用函数fun对变量x和y进行间接访问,故可以改写或如下形式:main() int x,y,*p1=&x,*p2=&y; *p1=1;*p2=2; printf(%d,%d,x,y);整个过程如图9-10所示。 主程序同上。当然也可以用数组名(必须为
9、二维数组的列地址)作实参,只需将主程序中调用函数的语句改为t=arr_add(a0,12); 即可。 归纳起来,利用数组名和指针变量作为函数的参数,实参与形参之间的对应关系可以有表9-4所示的几种。 数组指针作为函数参数的用法是十分灵活的,通过改变指针变量的值,可以指向数组内任一元素。这就给编程带来了很大的方便。 分析:从图9-11中可以看到,指针str的初值是数组line的起始地址,也就是&line0,将*str与ch比较,如果 *str(即str当前指向的字符)不等于ch的值,str+(让str下移一个字符),直到str指向字符C为止,将str值返回主调函数,它是字符C的地址。当移动str
10、至串尾,仍找不到查找的字符时,返回NULL。图9-11 返回指针值的strchr函数的数据存储示意图 return 第二次调用pro函数时,实参为a、N、odd_add。将函数odd_add的入口地址传给pro函数中的形参fun。此时指针变量fun 指向函数odd_add(见图9-12所示),pro中的*(fun)(p,n);相当于odd_add(p,n);,调用odd_add函数。以后依此类推,四次调用pro函数时,在pro函数中调用的是不同函数。图9-12 四次调用pro函数时,形参fun指向不同的函数入口地址 return return图9-14 字符数组name512的数据存储示意 上
11、面定义了一个一维指针数组,它有5个元素,每个元素都是指向字符数据的指针型数据。其中,name0指向第一个字符串Li fun,name1指向第二个字符串Zhang Li。如图9-15所示。分析:(1)本题用指针数组指向三个字符串。如图9-16所示。然后将字符串两两相比,进行排序处理。图9-16 利用指针数组按字母顺序排序三个字符串程序执行过程中数组string的变化如图9-17所示。图9-17 用二维数组按字母顺序排序三个字符串 return if(strcmp(*p1,*p2)0) p=*p1;*p1=*p2;*p2=p; for(i=0;i3;i+) printf(%sn,stringi);
12、图9-19 用指向指针的指针变量按字母顺序排序三个字符串 在介绍了“指向指针的指针”这个概念之后,让我们重新回过头来理一下思路。 首先对于一个二维数组short a34来说(见图9-20),根据前面的讨论可知,a0、a1、a2是三个一维数组名,它们分别代表三个地址:&a00、&a10、&a20。9.9.3 多重指针的用法和举例 从理论上说,还可以有“多重指针”,如图9-22所示。 即p1、p2、pn均为指针变量,指向一个整型变量(当然也可以指向任一类型数据),除了pn以外的指针变量都是指向指针的指针,但它们的类型是不同的。 例如,当n=5时,定义各变量如下: int i,*p5,*p4,*p3,*p2,*p1; return则实际如图9-24所示。 return return