C语言程序设计第八章 指针的使用.docx
- 文档编号:19748649
- 上传时间:2023-04-24
- 格式:DOCX
- 页数:22
- 大小:142.64KB
C语言程序设计第八章 指针的使用.docx
《C语言程序设计第八章 指针的使用.docx》由会员分享,可在线阅读,更多相关《C语言程序设计第八章 指针的使用.docx(22页珍藏版)》请在冰豆网上搜索。
C语言程序设计第八章指针的使用
第八章指针的使用
【学习目标】
本章将详细介绍在C语言中如何使用指针。
学习要点包括如下几点:
(1)掌握指针和指针变量的概念,了解指针变量的特点以及直接访问数据和间接访问数据的原理。
。
(2)掌握指针变量的定义、赋值方法及指针运算符的使用,熟练运用指针访问简单变量。
(3)熟悉指针和一维数组的关系,掌握指向一维数组的指针变量的定义方法,熟练使用指针变量访问一维数组元素。
(4)了解指针与字符串的关系,能熟练使用指针处理字符串。
(5)熟练掌握用指针变量作函数的参数时函数的定义和调用方法、数组名作函数的参数用法。
(6)指向指针的指针的运用。
【学习导航】
本章的在整个课程中的位置如图5-1所示。
图8-1本章学习导航
在本书的第一章介绍C语言有一个灵活性的特点,那么它的灵活性具体体现在哪里呢?
其实就是指针。
指针是C语言的精华部分,通过利用指针,我们能很好地利用内存资源,使其发挥最大的效率。
有了指针技术,我们可以描述复杂的数据结构,对字符串的处理可以更灵活,对数组的处理更方便,使程序的书写简洁,高效。
8.1地址和指针
指针是C语言的一种数据类型,类似于整型、字符型等。
既然指针也是一种类型,那么也可以定义该类型的变量,称为指针变量。
指针变量和其他类型的变量的区别是:
指针变量存储的是地址。
所以要学好指针,就一定要明白数据在内存中是如何存储的。
计算机所有数据都是存储在存储器里,系统的内存可看作编了号的小房间,如果要取房间的东西(读取数据)就需要得到房间编号。
地址就是内存区中对每个字节的编号。
下面通过两个整型变量来说明。
intx=1,y=2;
整型变量x、y(基本整型需4个字节)在内存中的存储如图8-2所示(假设内存编号是从2000开始)。
把变量所占用的存储单元首字节的地址作为变量的地址。
C语言中利用取地址运算符“&”获取变量的存储地址。
例如,&c将返回c的首地址;&x将返回x的首地址。
图8-2变量x和y在内存中的存储
图8-2中2000H和2004H就是内存单元的地址。
对内存中变量的访问,用scanf("%d%d",&x,&y)表示将数据输入变量的地址所指示的内存单元。
那么,访问变量,首先应找到其在内存的地址,或者说,一个地址唯一指向一个内存变量,我们称这个地址为变量的指针。
如果将变量的地址保存在内存的特定区域,用变量来存放这些地址,这样的变量就是指针变量,通过指针对所指向变量的访问,也就是一种对变量的“间接访问”。
指针是什么呢?
可以看作是内存中的一个地址。
这个地址是内存中另一个变量的位置。
在图8-2中,变量x的地址是2000H,就可以称为x的“指针”。
知识链接—变量的直接访问和间接访问
在C语言中,访问变量的值有两种方式——直接访问和间接访问
变量的“直接访问”方式:
按变量名(即变量的地址)存取变量值的方式。
比如在图8-3中直接访问x。
变量的“间接访问”方式:
将变量的地址放在另一个内存单元中,先到另一个内存单元中取得变量的地址,再由变量的地址找到变量并进行数据存取。
比如在图8-3中直接访问p。
图8-3直接访问和间接访问
8.2指针变量的定义与引用
8.2.1指针变量的定义和赋值
1.定义
指针变量的定义格式如下:
类型说明*变量名
其中“*”表示这是个指针变量,变量名表示定义的指针变量的名称,类型说明表示指针变量所指向的变量的数据类型。
例如:
int*p;
表示定义了一个名称为p的指针变量(注意变量名不是*p),它指向的变量的数据类型是int。
2.赋值
指针变量和普通变量一样,使用之前必须赋值,未经赋值的指针变量不可使用。
给指针变量只能给它赋值地址,而不能赋予任何其他数据。
C语言中提供了地址运算符“&”来表示变量的地址,其一般形式为:
&变量名
比如&x表示变量x的地址。
指针变量的赋值可以按下面的方式:
1int*p;
2intx=5;
3p=&x;
4printf("%d\n",*p);
试一试
(一):
将上面的3行代码改成:
p=x;
能通过编译吗?
运行结果是什么?
【说明】
编译时会出现警告:
'int*'differsinlevelsofindirectionfrom'int',意思是int*(p变量的数据类型)和int(x变量的数据类型)不匹配。
运行结果会报错,如图8-4所示:
图8-4指针变量p初始化错误1
从上面的错误可以看出,整数5被编译器当作了地址(0x00000005)。
而当第4行输出p指向的变量的值,报出不能读取的错误,因为0x00000005不是本程序分配的地址。
所以从安全性方面的考虑,会不允许这个读取操作。
试一试
(二):
将第3行代码改成:
*p=x;
能通过编译吗?
运行结果是什么?
【说明】
编译时会出现警告:
localvariable'p'usedwithouthavingbeeninitialized。
意思是变量p在使用前没有初始化。
显然,*p=x这条语句只给*p赋了值,而并没有给变量p赋值。
运行后会报错,如图8-5所示。
图8-5指针变量p初始化错误2
这是因为指针变量p没有赋初值,所以它指向的是一个系统给的不知名的地址(在这次执行时系统分配的是0xcccccccc),虽然p的空间本程序有控制权,但是地址0xcccccccc本程序并没有控制权。
出于安全性考虑,系统禁止向这个地址写入值(*p=x),所以报错。
试一试(三):
下面的代码编译会有问题吗?
1int*p;
2doublex=5.0;
3p=&x;
【说明】第1行定义了指针变量p,它指向的变量的数据类型为int。
而在第3行给它赋的值是一个double型的地址。
所以会报警告:
incompatibletypes-from'double*'to'int*'。
8.2.2指针变量的引用
1.指针变量的引用
引用指针变量,是提供对变量的一种间接访问形式。
对指针变量的引用形式为:
*指针变量
其含义是指针变量所指向的值。
【课堂案例8-1】接收键盘输入的一个小数,用指针的方法将这个数输出。
【案例目标】会使用指针变量实现输出。
【案例知识要点】指针变量的定义、指针变量的赋值、指针的引用。
【案例程序代码】pointRef.c
1#include
2
3intmain()
4{
5float*p1;
6floatx;
7scanf("%f",&x);
8p1=&x;
9printf("输入的小数是:
%f\n",*p1);
10return0;
11}
【程序输出】
图8-6pointRef.exe执行结果
【程序说明】
●第5行定义了指针变量p1,它指向的变量的数据类型是浮点型。
●第8行将浮点型变量x的地址赋给指针变量p1。
●第9行用指针的方法输出。
2.“&”和“*”运算符
前面用到了“&”和“*”运算符。
现在总结一下:
“&”运算符是一个返回操作数地址的单目运算符,称为取地址运算符。
例如:
p1=&x;
就是将变量x的内存地址赋给p1。
“*”运算符称为指针运算符,作用是返回指定内存中的变量值。
例如上面程序中的p1中装有变量x的地址,则:
y=*p1;
就是将变量x的值赋给y。
若x的值是1.0,则y的值也是1.0。
下面通过案例8-2来深入了解指针的使用。
【课堂案例8-2】创建程序sort.c,使用指针实现:
接收从键盘输入两个整数,按由大到小的顺序输出。
【案例目标】指针的综合运用。
【案例知识要点】&运算符、*运算符、指针变量的定义和使用。
【案例程序代码】swap.c
1#include
2
3intmain()
4{
5int*p1,*p2,x,y,t;/*定义指针变量与整型变量*/
6
7scanf("%d,%d",&x,&y);
8p1=&x;/*使指针变量指向整型变量*/
9p2=&y;
10
11if(*p1<*p2)
12{
13t=*p1;
14*p1=*p2;
15*p2=t;
16}
17printf("%d,%d\n",x,y);
18return0;
19}
【程序说明】
●执行完第9行代码后,内存中指针与变量的关系如图8-7(假定输入的是10,15)。
●第11行的表达式如果为真,则表示第1个数小于第2个数,会执行12~16行的代码。
●第15行执行完毕后,内存中指针与变量的关系如图8-8。
注意:
在程序的运行过程中,指针变量所指向的变量始终没变。
即*p1始终代表x,*p2始终代表y。
改变的只是变量x和y所在内存里的内容。
试一试(四):
将sort.c中的代码修改成sort1.c,如下:
1#include
2
3intmain()
4{
5int*p1,*p2,a,b,*t;//t被定义为指针变量
6scanf("%d,%d",&a,&b);
7p1=&a;
8p2=&b;
9
10if(*p1<*p2)
11{
12t=p1;
13p1=p2;
14p2=t;
15}
16printf("%d,%d\n",*p1,*p2);
17return0;
18}
结果会相同吗?
【说明】程序运行结果是完全相同的。
但程序在运行过程中,实际存放在内存中的数据没有移动,而是将指向该变量的指针交换了指向。
如图8-9右所示:
图8-9sort1.exe执行后内存图
当指针交换指向后,p1和p2由原来指向的变量x和y改变为指向变量y和x,这样一来,*p1就表示变量y,而*p2就表示变量x。
在上述程序中,无论在何时,只要指针与所指向的变量满足p=&x;我们就可以对变量x以指针的形式来表示。
此时p等效于&x,*p等效于变量x。
8.3数组与指针
8.3.1指针的自加自减运算
假定定义了指针变量p,对它做“加1”运算,不是简单地将p的值加上1,而是将p的值加上1倍的它所指向的变量占用的内存字节数。
指针加i(即p+=i)是将p的值加上i倍的它所指向变量占用的内存字节数。
对于int型变量,它占用内存的字节数为4,因此,p++后p的值增加4,如图8-10所示。
图8-10p++后的内存图
8.3.2使用指针访问一维数组
假设我们定义一个一维数组,该数组在内存会有系统分配的一个存储空间,其数组的名
字就是数组在内存的首地址。
若再定义一个指针变量,并将数组的首址传给指针变量,则该
指针就指向了这个一维数组。
我们说数组名是数组的首地址,也就是数组的指针。
而定义的
指针变量就是指向该数组的指针变量。
对一维数组的引用,既可以用传统的数组元素的下标
法,也可使用指针的表示方法。
例如:
1int*p,a[5];
2p=a;
这里a是数组名,也代表数组的首地址,所以可以将它赋值给指针变量p。
知识链接—认识数组名
一维数组名是个指针常量,它存放的是一维数组第一个元素的地址。
常量是不能被改变的,也就是说,一维数组名是不能被改变的。
数组名a存放的是一维数组第一个元素的地址,也就是a=&a。
也就说把a数组的内容以十六进制和把a数组元素的第一个值的地址输出是一样的。
由于a[0]的地址就是数组的首地址,所以下面的语句和上面的等价。
1int*p,a[5];
2p=&a[0];
C语言中指针对数组的表示方法有下面几种:
●p+n与a+n表示数组元素a[n]的地址,即&a[n]。
对整个a数组来说,共有5个元素,n的取值为0~4,则数组元素的地址就可以表示为p+0~p+4或a+0~a+4,与&a[0]~&a[4]保持一致。
●知道了数组元素的地址表示方法,*(p+n)和*(a+n)就表示为数组的各元素即等效于a[n]。
●指向数组的指针变量也可用数组的下标形式表示为p[n],其效果相当于*(p+n)。
下面用【课堂案例8-3】来具体说明。
【课堂案例8-3】定义一个长度为5的数组,以数组的不同引用形式输出数组各元素的值。
【案例目标】会使用指针访问数组元素。
【案例知识要点】指针变量表示的地址法访问数组元素、数组名表示的地址法访问数组元素、指针表示的下标法访问数组元素、指针法访问数组元素。
【案例程序代码】pointArray.c
1#include
2
3intmain()
4{
5inta[5]={1,2,3,4,5};
6int*p;
7
8p=a;
9printf("%d\n",*p);
10printf("%d\n",*(p+1));
11printf("%d\n",*(a+1));
12printf("%d\n",a[1]);
13printf("%d\n",p[1]);
14
15return0;
16}
【程序输出】
图8-11pointArray.exe执行后内存图
【程序说明】
●第8行:
给指针变量p赋值了数组a的首地址。
●第9行:
最开始p指向数组的首地址,即第一个元素的地址,所以*p输出数组的第1个元素。
●第10行:
采用指针变量表示的地址法输出数组第2个元素。
因为p+1表示数组元素a[1]的地址。
所以*(p+1)表示a[1]的值。
●第11行:
采用数组名表示的地址法输出数组第2个元素。
因为a表示的是数组的第1个元素的地址,a+1表示的是第2个元素的地址。
所以*(a+1)表示a[1]的值。
●第12行:
普通的数组下标法输出第2个元素。
●第13行:
采用指针表示的下标法输出第2个元素。
8.3.3使用指针访问二维数组
定义一个二维数组:
inta[2][3];
表示二维数组有2行3列共6个元素,在内存中按行存放,存放形式为图8-11:
图8-11二维数组在内存中的存放
其中a是二维数组的首地址,&a[0][0]既可以看作数组第1行第1列的首地址,同样还可以看作是二维数组的首地址,a[0]是第1行的首地址,也是这个一维数组的首地址。
同理a[n]就是第n+1行的首址;&a[n][m]就是数组元素a[n][m]的地址。
既然二维数组每行的首地址都可以用a[n]来表示,我们就可以把二维数组看成是由n行一维数组构成,将每行的首地址传递给指针变量,行中的其余元素均可以由指针来表示。
用地址法来表示数组各元素的地址。
对元素a[0][1],&a[0][1]是其地址,a[0]+1也是其地址(可参考8-12来理解)。
分析a[0]+1与a[0]+2的地址关系,它们地址的差并非整数1,而是一个数组元素的所占位置4,原因是每个数组元素占4个字节。
图8-12指针和二维数组的关系
下面用【课堂案例8-4】来具体说明。
【课堂案例8-4】定义一个2行3列的二维数组,以数组的不同引用形式输出数组各元素的值。
【案例目标】会使用指针访问二维数组元素。
【案例知识要点】二维数组元素不同的访问方式。
【案例程序代码】pointTwoDemArray.c
1#include
2
3intmain()
4{
5inta[2][3]={{1,2,3},{4,5,6}};
6int*p;
7
8p=a[0];
9printf("%x,%x,%x\n",a,a[0],&a[0][0]);
10printf("%d\n",*p);
11printf("%d\n",*(p+1));
12printf("%d\n",*(a[0]+2));
13printf("%d\n",*(a[1]));
14
15return0;
16}
【程序输出】
图8-13pointTwoDemArray.exe执行结果
【程序说明】
●第8行:
给指针变量p赋值了数组a的首地址。
●第9行:
输出了三个变量的值,a、a[0]、&a[0][0]都表示二维数组的首地址,所以三个值相同。
●第10行:
因为给p赋的值是数组首地址,而就是第1个元素的值,所以相当于输出a[0][0],即整数1。
●第11行:
p+1表示将指针下移了一个元素,所以输出的是a[0][1],即整数2。
●第12行:
a[0]表示的是第1行第1个元素的地址,a[0]+2表示下移两个元素的地址,相当于&a[0][2],所以输出的是a[0][2]的值,即整数3。
●第13行:
a[1]表示的是第2行第1个元素的地址,所以输出的是a[1][0]的值,即整数4。
【课堂练习】
求解二维数组中的最大值及该值在二维数组中的位置。
8.3.4指针操作字符串
访问一个字符串可以通过两种方式:
●通过数组名来表示字符串,数组名就是数组的首地址,是字符串的起始地址。
●使用字符指针指向一个字符串,可不定义数组。
对第一种方式,可以将字符数组的名赋予一个指向字符类型的指针变量,让字符类型指针指向字符串在内存的首地址,对字符串的表示就可以用指针实现。
下面的程序介绍了具体用法。
1#include
2main()
3{
4charc[10];
5char*p;
6
7p=c;//p=c则表示将字符数组的首地址传递给指针变量p
8gets(c);
9printf("%s\n",p);//使用指针变量p输出字符串
10}
下面的程序说明了第二种方式的用法:
1#include
2
3intmain()
4{
5char*str="这是一个字符串...";
6printf("%s\n",str);
7return0;
8}
上面程序第5行把一个字符串赋值给指针变量p,实际上只是把字符串中的第1个字符的地址赋值给它。
【课堂案例8-5】
1#include
2
3intmain()
4{
5chars1[]="c语言是一门基础语言课";
6char*p1,*p2;
7chars2[22];
8
9p1=s1;
10p2=s2;
11
12while(*p1!
='\0')
13{
14*p2=*p1;
15p1++;
16p2++;
17}
18
19*p2='\0';
20printf("第二个字符串的内容:
%s\n",s2);
21return0;
22}
【程序输出】
图8-14pointCopyStr.exe执行结果
8.4指针变量作函数的参数
学习了指向一维和二维数组指针变量的定义和正确引用后,我们现在学习用指针变量作
函数的参数。
使用经典的交换两个数的案例来说明。
程序swap.c希望通过定义一个swapNum的函数来交换两个数。
1#include
2voidswapNum();
3intmain()
4{
5inta=5;
6intb=10;
7
8printf("互换前:
a=%d,b=%d\n",a,b);
9swapNum(a,b);
10printf("函数调用完毕-------互换后:
a=%d,b=%d\n",a,b);
11}
12
13voidswapNum(inta,intb)
14{
15intt;
16t=a;
17a=b;
18b=t;
19printf("swapNum函数中------互换后:
a=%d,b=%d\n",a,b);
20return;
21}
程序执行结果如图8-14所示,显然没有互换成功。
因为函数的a,b已经执行完成,分配给内存的空间已经释放了,所以最终a,b的值还是主函数a,b的值。
互换的是形参的a,b。
和主函数没有关系。
图8-14swap.exe执行结果
下面使用指针变量作为参数来完成两个数的互换。
【课堂案例8-5】使用指针变量作为参数来完成两个数的互换。
【案例目标】会使用指针变量做参数。
【案例知识要点】函数的定义、指针变量做函数参数、函数的调用。
【案例程序代码】pointSwap.c
1#include
2voidswapNum();
3intmain()
4{
5inta=5;
6intb=10;
7
8printf("互换前:
a=%d,b=%d\n",a,b);
9swapNum(&a,&b);
10printf("函数调用完毕-------互换后:
a=%d,b=%d\n",a,b);
11}
12
13voidswapNum(int*p1,int*p2)
14{
15intt;
16t=*p1;
17*p1=*p2;
18*p2=t;
19printf("swapNum函数中------互换后:
a=%d,b=%d\n",*p1,*p2);
20return;
21}
【程序输出】
图8-15pointSwap.exe执行结果
【程序说明】
●第6行执行完毕后变量a、b在内存中的表示如图8-16
(1)所示。
●第9行调用函数swapNum,传递的是a和b的地址。
●第13行定义了swapNum函数,参数是int型的指针类型。
形参分别为p1和p2。
第13行执行完毕后内存中的表示如图8-16
(2)所示。
●执行完第15~18行后,内存中的变化如图8-16(3)所示。
显然,交换了a、b两个变量的内容。
图8-16pointSwap.exe执行结果
【课堂练习】
1.编写一个函数max,能返回一维数组中的最大元素。
2.编程实现将一个数组中的元素按照相反的顺序存放。
8.4本章小节
本章介绍了C语言中各种流程控制语句的用法。
学习本章后,应该掌握如下内容:
●如何定义指针变量。
●如何给指针变量赋值。
●如何使用指针访问数组。
●如何使用指针操作字符串。
●在函数中使用指针变量做参数
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C语言程序设计第八章 指针的使用 语言程序设计 第八 指针 使用