第7章指针.ppt
《第7章指针.ppt》由会员分享,可在线阅读,更多相关《第7章指针.ppt(94页珍藏版)》请在三一办公上搜索。
1、第7章 指针,C语言大学实用教程,西南财经大学经济信息工程学院刘家芬,数据在内存中的存储,计算机的内存区由连续的内存单元构成,每个内存单元的大小是一个字节,每一个字节有一个编号,也就是常说的地址。每一个变量在内存中都占据一定内存空间,空间的大小由变量的类型确定。整型变量在内存中占用2个字节,float型变量在内存中占用4个字节。在程序中一般是通过变量名来对内存单元进行存取操作的。而实际上,编译过程中所有的变量名都会被转换为变量的地址,计算机对变量的值的存取,都是通过地址来进行的。,假设程序中定义了2个整型变量i和j,编译器在编译的时候,为变量i分配地址编号为2000和2001的两个字节;而为变
2、量j分配2002、2003的两个字节。如果有语句 j=i+j,计算机在执行过程中将首先从地址2000、2001中取出其中的值(变量i的值),然后从地址2002、2003中再取出其中的值(变量j的值),然后将这两个值进行相加,将相加的结果保存在内存地址2002、2003中。这种通过变量地址直接存取变量值的方式,称为直接访问方式。,除了直接访问方式外,还可以采用间接访问方式,来对一个变量的值进行存取:将变量i的地址2000,存放在另一个变量i_pointer当中,访问的时候,通过取出i_pointer的值,找到变量i的地址2000,然后再从2000、2001中,取出变量i的值。,2000,i_po
3、inter,i,2000,这种间接访问,从变量i_pointer开始,最终也能访问到变量i的值。,由于通过地址能找到所需的变量,因此可以说地址指向该变量单元。在C语言中,将地址形象化地称作“指针”。,变量的值保存在内存单元中,该内存单元可以通过地址来标识。变量的地址即存放数据的内存地址,就是该变量的指针。指针可以保存在变量中,这种专门用来保存指针的变量,称为指针变量。指针变量i_pointer中保存了变量i的地址2000。2000指向变量i,我们也可以说指针变量i_pointer指向变量i。,指针和地址的概念,变量的指针和指向变量的指针变量,变量的指针就是变量的地址,而指针变量就是指存放地址型
4、数据的变量。为了表示指针变量和所指向的变量之间的联系,C语言中用*表示“指向”关系。右图中(*i_pointer)就是指针变 量i_pointer所指向的变量,即i。指针变量的定义 格式:基类型*指针变量名;基类型是该指针变量指向的变量的类型。例如:float*p;(p是指向浮点型变量的指针变量)int*q;(q是指向整型变量的指针变量),2000,i_pointer,3,i,2000,*i_pointer,注意:指针变量名前面的*表明该变量为指针变量,指针变量的名字是p、q,而不是*p、*q。在定义指针变量时必须指定基类型。基类型规定了指针类型变量所指向的变量的类型,一个指针变量只能指向同一
5、类型的变量。例如:float*p;这里定义的指针类型变量p只能指向float型的变量,也就是说,它只能保存一个float型变量的地址,而不能保存其他类型变量(比如int型的变量)的地址。以下是错误的:int i;float*p=,定义了指针变量后,如何使其指向某个变量?可以通过赋值语句使指针变量得到另一个变量的地址,从而使它指向另一个变量:int i=6;int*p;p=(这个赋值语句将整型变量i的地址赋给了指针变量p,从而使p指向了变量i),p,&i,i,6,指针变量的引用,指针变量只能存放地址,不能将一个整数(或其他非地址类型的数据)赋给一个指针变量。例如下面是错误的:int*p=100;
6、两个有关的运算符:(1),#include void main()int a,b;int*p1,*p2;a=100;b=10;p1=,例7.1,说明:在开始定义了两个指针变量p1和p2,但是定义的时候,并没有给它们赋初值,这个时候,p1和p2并未指向任何变量。p1=这里*p1和*p2分别代表p1和p2所指向的变量的内容。,有如下程序段:int a,*p;p=问:(1)&*p的含义是什么?分析:&和*这两个运算符的优先级相同,按照右结合,先进行*p的运算,它就是变量a,然后再执行&运算,即&a运算,得到变量a的地址。(2)*&a的含义是什么?分析:和上面类似,&a运算是取变量a的地址,然后执行*
7、运算,即&a所指向的变量,即是变量a。,输入a和b两个整数,按照先大后小的顺序输出a和b。#include void main()int*p1,*p2,*p,a,b;scanf(%d,%d,例7.2,这个程序的算法思想是:使用p1和p2分别指向a和b。当a小于b的时候,交换p1和p2的内容,使得p1指向b,p2指向a。输出时,先输出p1指向的那个变量,后输出p2指向的那个变量。,p1,&a,a,5,&b,b,10,p2,p1,&b,a,5,&a,b,10,p2,(1)开始时,(2)输出时,指针变量作为函数参数,指针变量也可以作为函数参数来传递信息例7.3 输入两个整数,按照先大后小的顺序输出。
8、,#include void main()void swap(int*p1,int*p2);int a,b,*point_1,*point_2;scanf(%d,%d,分析程序:1.在main函数中,swap的实参是point_1,point_2。这两个指针变量分别保存了a和b的地址。2.在swap被调用时,形参p1和p2分别被赋予实参point_1和point_2的值,因此,p1指向变量a,p2指向变量b3.swap函数的执行,使p1指向的变量(a)的内容和p2指向的变量(b)的内容进行了交换。4.在main中按先a后b的顺序打印结果。这个时候,a中保存的值,大于b中保存的值的。,p1,&a
9、,a,5,&a,point_1,p2,&b,b,9,&b,point_2,p1,&a,a,9,&a,point_1,p2,&b,b,5,&b,point_2,swap开始时,swap结束时,能否使用普通变量作函数参数,即void swap(int x,int y)int temp;temp=x;x=y;y=temp;并在main函数中调用swap:swap(a,b);,思考,答案:不行。因为a和b的值在传递给swap的形参x和y后,不会改变(我们称为值传递):若要使被调函数对某个变量的值的改变,在主调函数中可见,那么可以使用指针变量作函数参数。调用时,传递变量的地址给被调函数。,a,5,b,9
10、,x,5,y,9,swap开始时,a,5,b,9,x,9,y,5,swap即将结束时,另一个解决方案,是否可行?#include void main()void swap(int*p1,int*p2);int a,b,*point_1,*point_2;scanf(%d,%d,编程者意图通过swap,交换main函数中的point_1和point_2的内容。但是这是不能实现的,因为swap只能交换形参p1和p2的内容,而不能把这种影响反映到main中的point_1和point_2中去。,再一个解决方案,是否可行?#include void main()void swap(int*p1,int
11、*p2);int a,b,*point_1,*point_2;scanf(%d,%d,*p是指针变量p所指向的变量,而p指向哪里?如果对*p赋值,可能造成系统崩溃!,数组和指针,数组也要占用内存。数组占用一个连续的内存区域,数组中元素在内存中的存储位置是相邻的。例如:int a6;假定数组a的首地址为2000:,a0,a1,a2,a3,a4,a5,2000,2002,2004,2006,2008,2010,a,指向数组元素的指针,数组元素相当于变量,所以指向数组元素的指针,和指向变量的指针相同:int a10;int*p;p=,通过指针引用数组元素,程序段:int a10;int*p=的作用是
12、?将数组a中最后一个元素a9赋值为100,C语言规定:如果指针变量p已经指向数组中的一个元素,则p+1指向同一个数组中的下一个元素。例如:int a10;int*p=)这里p+1不是简单的对地址数值进行加1。假设数组a的首地址为2000,p初始值为2000,p+1的值为2002,因为int型的变量在内存中占两个字节,p+1为2002,指向元素a1若p指向数组中第一个元素a0,p+i和a+i就是ai的地址,指向数组元素ai;*(p+i)和*(a+i)就代表数组元素ai。,注意:指向数组的指针变量也可以带下标,如pi与*(p+i)等价数组名也是一个指针变量,它指向数组的首地址。因此:int a10
13、;*(a+5)=100;等价于a5=100;,(1)使用下标法访问#include void main()int a10;int i;for(i=0;i10;i+)scanf(%d,例7.4 输出数组中全部元素,(2)使用数组名计算数组元素地址,找出元素的值#include void main()int a10;int i;for(i=0;i10;i+)scanf(%d,(3)使用指针变量指向数组元素#include void main()int a10;int i,*p;for(i=0;i10;i+)scanf(%d,注意:p作为指向数组元素的指针变量,可以通过改变指针变量的值指向不同的元素
14、。可以进行p+操作,即p的值可以更改;数组名a虽然也作为指针,但是a的值不能修改,因此不能进行a+操作。,注意:1.int a10;int*p=a;则p+10指向的内存地址,已经超出了数组a的范围。如果程序中出现*(p+10)=100;已经超出了数组a的边界。因为不确定(p+10)指向的内存区域用于何种目的,所以这样赋值可能会使系统崩溃。2.比较*(p+)*(+p)+(*p),*p,然后p指向数组下一元素,p+使p指向数组下一元素,读取当前*p,使p指向的内存单元中的值加1,指针变量1-指针变量2结果为:两个指针指向的数组元素间的距离例:指向同一数组的指针变量相减float x10,*p1=x
15、+2,*p2=p1-p2 的值是-p2-p1 的值是说明两个元素之间的距离是6,指向同一数组的指针变量之间的减法运算,用数组名作函数参数,第六章中已经讲过:用数组名作函数参数时,实际上传递的只是实参数组的首地址;在内存中只分配了一个数组空间,而不是两个。被调函数中对形参数组的修改,在主调函数中可见。在掌握了指针的概念之后,我们再来深入分析一下用数组名作函数参数。,void f(int arr,int n)实参数组名array代表该数组的首地址,形参用来接收从实参传递过来的首地址,因此形参应该是一个指针变量,因为只有指针变量能存放地址。使用数组名作函数参数时,形参int arr 表示arr是数组
16、名,C编译器会将形参数组名作为指针变量来处理。因此void f(int arr,int n)和void f(int*arr,int n)是等价的,编译器会建立一个指针变量arr,用来存放从主调函数中传过来的实参数组的首地址。,void main()int array10;f(array,10);,实参数组名为array,当调函数f时,形参arr接受array数组的首地址,因此arr也指向array数组。根据前面讲的指向数组元素的指针所具有的性质,我们可以使用几种等价的方式来访问数组array中的元素。例如array1还可以用以下两种方式来表示:arr1、*(arr+1)。,arr,array,
17、arr+1,数组名作函数参数时,C编译器将形参数组名作为指针变量来处理。传递给这个形参的值是实参数组的首地址。注意:1.因为形参数组名被编译成指针变量,所以可以改变它的值。例如 arr+;是允许的。2.而实参数组名array虽然也指向数组中第一个元素,但是array的值不能被修改,它是一个指针常量。例如 array+;是错误的。,例7.5 将数组a中的n个整数按相反顺序存放#include void main()void inv(int x,int n);int i,a10=3,7,9,1,0,6,7,5,4,2;printf(The original array:n);for(i=0;i10
18、;i+)printf(%d,ai);printf(n);inv(a,10);printf(The array has been inverted:n);for(i=0;i10;i+)printf(%d,ai);printf(n);,void inv(int x,int n)int temp,i,j,m=(n-1)/2;for(i=0;i=m;i+)j=n-1-i;temp=xi;xi=xj;xj=temp;,i,m,j,算法:将a0与an-1互换,再将a1与an-2互换直到中间元素。用for循环处理,i和j分别指示需要对换的两个元素位置,初值为0和n-1,然后i+并且j-,直到i=(n-1)/
19、2。第一个形参x实际上是一个指针变量,x指向数组a的首元素。通过xi指向的实际上就是ai。第二个形参n用来接收需要进行处理的元素的个数。可以通过inv(a,5)表示对数组前5个元素进行颠倒。,注意:算法并不是唯一的!,对程序进行修改,将inv函数中的参数x改成指针:#include void main()void inv(int*x,int n);int i,a10=1,3,5,7,9,11,13,15,17,19;printf(The original array:n);for(i=0;i10;i+)printf(%d,ai);printf(n);inv(a,10);printf(The a
20、rray has been inverted:n);for(i=0;i10;i+)printf(%d,ai);printf(n);,void inv(int*x,int n)int*p,temp,*i,*j,m=(n-1)/2;i=x;j=x+n-1;p=x+m;for(;i=p;i+,j-)temp=*i;*i=*j;*j=temp;这个函数里采用了指向整型变量的指针变量i和j,分别指向将要交换的两个数组元素。,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a数组,i,x,p=x+m,j,归纳起来,如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参和形参可分别为:形参
21、和实参都用数组名实参用数组名,形参用指针变量实参形参都用指针变量实参用指针变量,形参用数组名,#include void main()void inv(int*x,int n);int i,*a;printf(The original array:n);for(i=0;i10;i+)scanf(%d,a+i);printf(n);inv(a,10);printf(The array has been inverted:n);for(i=0;i10;i+)printf(%d,*(a+i);printf(n);,这个main()有问题吗?,#include void main()int a=5,*
22、p1=这条语句,对这个未知内存空间的值进行改变,这是很危险的。如果这个地址存放的是系统关键程序内容,可能会使系统崩溃。总结:在指针变量的值未被初始化时,没有指向某个变量,不要使用该指针变量!,例7.6选择法排序,void sort(int x,int n)int i,j,k,t;for(i=0;ixi)k=j;if(k!=i)t=ki;xi=xk;xk=t;,多维数组与指针,回顾一维数组与指针小节所讲的内容:1.数组名是个指针常量,它指向数组中的首元素。2.若数组名为a,a+i指向数组中第i+1个元素ai;对数组中第i个元素的访问,可以使用ai,也可以使用*(a+i)。,多维数组元素的地址,C
23、语言把二维数组,作为一维数组来处理;这个一维数组的每个元素,又是一个一维数组。例如int a34=1,3,5,7,9,11,13,15,17,19,21,23数组a是一个二维数组。C语言把a被看作一个一维数组,它包含3个元素a0,a1,a2。而每一个元素,又是一个一维数组。例如a0是一维数组名,a0这个一维数组包括了a00,a01,a02,a03这4个元素。,a0,a1,a2,1,3,5,7,9,11,13,15,17,19,21,23,=,=,=,a(2000),按照一维数组的知识:数组名是个指针常量,它指向数组中的第一个元素。可以得出:二维数组名a是个指针常量,它指向数组中第一个元素a0(
24、a0同时也是一个一维数组1,3,5,7)。同时,我们也可以得出:a=2000的话,a+1=2008,因为a+1指向a数组中第二个元素a1(每一行是一个一维数组,包含4个整型数,占用8字节)因为a+1指向a数组中的第二个元素a1,因此可以用*(a+1)来表示第二个元素a1(a1是一维数组9,11,13,15),a0,a1,a2,1,3,5,7,9,11,13,15,17,19,21,23,=,=,=,a(2000),a0,a1,a2是3个一维数组。因此,a0作为一维数组的名字,代表数组1,3,5,7的首地址,即&a00。同样,a1中的值是&a10,a2中的值是&a20。考虑0行1列的元素地址如何
25、表示?答:a0+1。也可以用&a01来表示。a0的值为2000,a0+1的值为2002。a0可以用*(a+0)来表示因此0行1列的元素的地址a0+1也可以用*(a+0)+1来表示 i行j列的元素的地址为ai+j或*(a+i)+j那么i行j列的元素的值,可以用*(*(a+i)+j)来表示。,在分析二维数组的时候,首先把二维数组看成一个一维数组,然后按照一维数组的方法来处理。二维数组作为一维数组处理,其中的每一个元素,又是一个一维数组。对这些一维数组,也使用一维数组的方法来处理。,指向多维数组元素的指针变量,(1)指向数组元素的指针变量例7.7 用指针变量输出二维数组元素的值include voi
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 指针
链接地址:https://www.31ppt.com/p-4855450.html