C大学基础教程第六章课件.ppt

上传人:牧羊曲112 文档编号:1828063 上传时间:2022-12-20 格式:PPT 页数:81 大小:967.01KB
返回 下载 相关 举报
C大学基础教程第六章课件.ppt_第1页
第1页 / 共81页
C大学基础教程第六章课件.ppt_第2页
第2页 / 共81页
C大学基础教程第六章课件.ppt_第3页
第3页 / 共81页
C大学基础教程第六章课件.ppt_第4页
第4页 / 共81页
C大学基础教程第六章课件.ppt_第5页
第5页 / 共81页
点击查看更多>>
资源描述

《C大学基础教程第六章课件.ppt》由会员分享,可在线阅读,更多相关《C大学基础教程第六章课件.ppt(81页珍藏版)》请在三一办公上搜索。

1、2022/12/20,1,C+大学基础教程,第6章 指针和引用,2022/12/20,2,指针(Pointer)是C+和C的一种数据类型。很多其他高级语言也有类似的数据类型。引用(Reference)则是C+所特有的一种数据类型。,2022/12/20,3,第六章 指针和引用,6.1 指针的概念 6.2 指针的运算 6.3 指针和函数 6.4 指针和字符串 6.5 通过指针访问数组 6.6 指针访问动态内存6.7 引用概念,2022/12/20,4,6.1 指针的概念,2022/12/20,5,6.1.1 指针和指针变量,指针是变量的地址例如,定义整型变量a,a的地址就是 一个指针。直接指定内

2、存某个地址开始(如 0 x00430100)存放指针的变量就是指针变量。,2022/12/20,6,6.1.1 指针和指针变量,当声明了一个指针变量后,确定了三件事:1. 变量本身在内存中所分配的地址和字节数(4个)2. 系统允许的地址范围,及地址单元内可以存放的内容3. 指针变量可以进行的运算访问指针变量时,只能看到地址通过这个地址访问地址单元中的内容,这样的访问称为对于内存单元的间接访问。,2022/12/20,7,6.1.2 指针变量的声明和初始化,指针变量声明的格式是: 类型名 *变量名1, *变量名2;例如: int *va1, *va2; char *ch1, *ch2;指针变量在

3、声明后,变量的值(地址)是随机的。这样的指针变量是不能安全的使用的,2022/12/20,8,6.1.2 指针变量的声明和初始化,在声明指针变量时的“*”,有两个含义:声明变量va1、ch1、ch2都是指针变量;说明变量va1的类型是(int *)型,即指向整型变量的指针。ch1和ch2的类型是(char *)型指针变量都是有类型的- 所指定的地址单元中存放的数据的类型,2022/12/20,9,6.1.2 指针变量的声明和初始化,指针变量必须初始化后才能安全使用声明指针变量时进行初始化的格式: 类型名 *指针变量名=,2022/12/20,10,6.1.2 指针变量的声明和初始化,赋值方式进

4、行初始化:int i1=Y, i2=A; int *pi1, *pi2;pi1=没有初始化的指针变量不可以使用 (编译系统会给出警告,而运行时会出现错误),2022/12/20,11,6.2 指针的运算,2022/12/20,12,6.2 指针的运算,表6.1 指针的运算,2022/12/20,13,6.2.1 指针的赋值运算,指针的赋值: 地址的赋值同类型变量的地址同类型的已经初始化的指针变量;其他同类型的指针用0或者NULL赋值 “空指针”,即不指向任何的内存物理地址不同类型的指针不可以互相赋值在指针赋值时,不存在类型自动转换的机制,2022/12/20,14,例6.1 观察以下指针赋值运

5、算的结果。如果将注释去掉,结果将如何?#include using namespace std;void main()int va1=100,*pva1; int *pva2=NULL; float vf1=A,*pvf1,*pvf2;coutvalue of pva2 is pva2endl; pva1=,value of pva2 is 0 x000000000 x0012FF7C 0 x0012FF7C0 x0012FF74 0 x0012FF74 注释去掉会出现编译错误,2022/12/20,15,6.2.2 间接引用运算,间接引用运算符“*” :一元运算符和指针变量连用,对指针所指向

6、的内存地址单元进行间接访问使用的格式: *指针变量 如果指针变量iptr指向整型变量va*iptr就是变量va的内容,2022/12/20,16,例6.2 对变量的直接访问和间接访问#include using namespace std; void main()char ch1=a,*ch; int k1=100; ch= /间接访问 ,运行结果:*ch=ach1=B*ch=d,2022/12/20,17,例6.3 观察对这种指针变量间接访问的结果。#include using namespace std;void main() int va=100,*pva,*ppva; /ppva是指向

7、指针的指针 int k1=100; pva= /就是指针pva的内容,运行结果:*pva=100*ppva=0 x0012FF7C pva=0 x0012FF7C,2022/12/20,18,6.2.2 间接引用运算,2022/12/20,19,6.2.3 指针的算术运算,可以进行的只有加法和减法-和整数n做加法或者减法,指针p和整数n相加(相减)的含义是指向当前指向位置p的前方或后方第n个数据的地址。,2022/12/20,20,例6.4 通过指针的间接访问,输出下标为偶数的数组元素的值。#include using namespace std;void main() int k110=11

8、,24,37,44,58,66,79,86,93,108,*k; k=,运行结果:K10=11 k12=37.,数组第一个元素(下标为0)的地址赋值给指针k,每次循环,指针加2,2022/12/20,21,6.2.3 指针的算术运算,指针和指针的直接加法没有意义指针和指针的减法可以进行-求出两个指针之间可以存放几个指定类型的数据不允许用一个整数减一个指针,2022/12/20,22,6.2.4指针的关系运算和逻辑运算,同类型的指针可以进行关系运算进行指针“大于”、“小于”的比较,是判定指针在内存中的相对位置 指针和一般的整数比较没有意义,也不允许惟一可以和指针比较的整数是0。判定指针是不是空指

9、针,2022/12/20,23,6.2.5 void类型指针,void类型的指针就是“无类型”指针: void *指针名;存放的也是内存的地址,但不指定这个地址单元内的数据的类型,2022/12/20,24,6.2.5 void类型指针使用要点,-任何指针都可以赋值给void指针 赋值后的void指针的类型仍然是void -void指针不可以赋值给其他任何类型的指针 -void指针不可通过间接引用访问内存中数据 -必须进行指针类型的强制转换,才可以使用指针间接引用访问内存数据,2022/12/20,25,例6.5 使用memcpy通用复制函数复制数组原型 void *memcpy(void *

10、dest, const void*, size) #include using namespace std; #include void main() char src10=012345678; char dest10; char* pc=(char*)memcpy(dest,src,10); cout pc endl; int s13=1,2,3; int d13; int *pi=(int*)memcpy(d1,s1,12); cout*pi *(pi+1) *(pi+2)endl;,运行结果:0123456781 2 3,复制字符数据,10个字节,复制整型数据,12个字节,2022/12

11、/20,26,6.2.5 void类型指针,void类型指针应用:显示字符指针的内容其他指针直接用cout语句输出地址值而字符指针输出的是字符串将字符指针强制转换为void指针cout语句输出便是地址值例:char *pch=Hello C+; coutpchendl; cout(void*)pchendl;,2022/12/20,27,指针是地址,int *p;p=,*p=100; b=*p+2;,2022/12/20,28,6.3 指针和函数,2022/12/20,29,6.3 指针和函数,在程序设计中,指针有很多应用其中之一就是作为函数的参数,形成了C+函数调用中的另一种调用方式:地址调

12、用,2022/12/20,30,6.3.1 指针作为函数的参数,实现地址调用,必须满足以下条件: 函数的形式参数是指针变量; 函数的实参数是内存的地址(可以是数组名、变量的地址、用变量地址初始化的指针)形参指针类型和实参地址类型必须相同,2022/12/20,31,6.3.1 指针作为函数的参数,实参传递给形参的是内存的地址,所以形参指针指向实参变量 形参指针通过间接引用,直接访问实参变量 (包括改变实参变量的值)函数调用后,改变了实参,如果有多个实参,就可以有多个实参变量在函数调用中被修改,2022/12/20,32,例6.6 编写数据交换的函数。#includevoid Swap(int

13、*a, int *b);void main()int x(5), y(10); cout主函数变量的值: x=x y=yendl;Swap(,运行结果:主函数变量的值:x=5 y=10函数中完成了交换:*a=10 *b=5返回后变量的值: x=10 y=5,变量的地址作为实参数,指针变量作为形式参数,2022/12/20,33,6.3.1 指针作为函数的参数,变量x和y的地址作实参传递给指针a和b,如图(a)通过间接引用*a和*b进行交换,实际上就是x和y进行交换,如图(b),2022/12/20,34,例6.7 指针变量指向一个数组 #includeusing namespace std;v

14、oid Move(int *a);void main()int x5=10,20,30,40,50, *px=x;cout调用前的*px=*pxendl; Move(px);cout调用后的px;if(px=x)cout没有变化,*px还是*pxendl;else cout也向前移动,*px变为*pxendl;void Move(int *a)a=a+1; *a=100; cout“函数中完成了指针移动:*a=*aendl;,运行结果:调用前的*px=10 函数中完成了指针移动:*a=20 调用后的px没有变化*px还是10,指针作为实参数,指针变量作为形式参数,2022/12/20,35,6

15、.3.3 传递参数的保护:指针和常量,通过数组名地址调用,可以改变实参数组内容但有时并不需要改变数组的值 例如,在调用一个求数组最大值的函数时,就不希望数组的值发生变化 希望在函数中能够限制对数组元素的修改使用常指针可以达到这个目的,2022/12/20,36,6.3.3 传递参数的保护:指针和常量,常指针是指向常量的指针(Pointer to Constant data) 规定指针所指向的内容不可以通过指针的间接引用来改变。 常指针说明的格式是: const 类型名 *指针名; 例如: const int *ptint; 指针ptint的类型是(const int *),即指向一个恒定的整型

16、数,2022/12/20,37,例6.10 常指针示例。观察以下程序的运行。 #include using namespace std;void main()int ia=10, ib=20;const int *ptint; ptint= /语句错误:左值是常量,运行结果:10 120,常指针声明,注释去掉会出现编译错误,2022/12/20,38,6.3.3 传递参数的保护:指针和常量,指针常量(Pointer constant)指针本身是常量(地址是常量),不可以改变指针常量声明的格式: 类型名 *const 指针名=初值; char ch, *const ptch= 数组名是数组的首地

17、址-数组名是指针常量,2022/12/20,39,例6.11 指针常量示例。指出以下程序的错误。 #include using namespace std;void main() int a=10, b=100; int *const pa=,错误语句注释掉后运行结果:10 20,语句有错:常量不能当左值,语句有错,地址类型不同不能用常量地址初始化指针常量,2022/12/20,40,例6.12 用常指针作形参,函数printString可以输出数组的内容,不可以对数组修改。 #include using namespace std;void printString( const char *

18、 );void main() char phrase = C+ is a programming language; cout The string is:n; printString( phrase ); cout endl; / main函数结束void printString( const char *Ptarray ) while(*Ptarray) cout *Ptarray+;,不使用常指针也是可以完成打印。但是没有保护了。,数组名作实参数,常指针作形式参数,2022/12/20,41,6.4 指针和字符串,2022/12/20,42,6.4.1 字符串处理的两种方式,字符串常量:

19、双引号括起,0结束 如:This is a string。 字符串常量有自己固定的首地址 如果将字符串常量的首地址看成是指针,这种指针既是常指针,也是指针常量。,2022/12/20,43,6.4.1 字符串处理的两种方式,处理字符串两种方式:数组方式和指针方式 数组方式是将字符串存入字符数组: char string_array =Whats a nice day!; 指针方式是用字符串常量来初始化一个字符指针: char *string_pt=Whats a nice day!;,2022/12/20,44,6.4.1 字符串处理的两种方式,指针常量不能放在等式左边,运行时会出错,2022

20、/12/20,45,6.4.2 字符串操作函数,实参数:字符数组名、已经初始化的字符指针,字符串常量目的串必须是可写的。,2022/12/20,46,例6.14 strcpy和strncpy的比较。 #include void main() int n; char *array1 = Happy Birthday to You; char array3 15 ; char array2 25 ; strcpy( array2, array1 ); cout The string in array1 is: array1 nThe string in array2 is: array2 n; /

21、*strcpy(array3,array1); coutarray3endl; */ n=sizeof(array3); strncpy( array3, array1, n-1 ); /复制array1的n-1个字符 array3 14 = 0; / 添加0 到array3 cout The string in array3 is: array3 endl;,不包括提示的运行结果Happy Birthday to YouHappy Birthday to YouHappy Birthday,复制array1到array2,没有问题,复制array1到array3,空间不够,有运行错误,按实际

22、数组大小,复制array1到array3,没有问题,2022/12/20,47,6.5 通过指针访问数组,2022/12/20,48,6.5 通过指针访问数组,指针和数组有天然的联系数组名就是地址,也就是某种类型的指针虽然一维数组名和二维数组名都是地址,都可以看作是某种指针,但是指针的类型是不同的通过指针访问一维数组和二维数组的方法是不同的,2022/12/20,49,6.5.1 通过指针访问一维数组,必须声明和数组类型相同的指针,并用数组名初始化指针: int A10, *pa=A; 多种方式访问数组元素数组名和下标,如A0、A4 指针和下标,如pa0、pa4 指针加偏移量的间接引用,如*(

23、pa+4)数组名加偏移量的间接引用,如*(A+4)指针自加后的间接引用,如*pa+,2022/12/20,50,例6.15 求数组内所存放的字符串的长度 void main() char ChArray=This is a string.,*ptch; int i,j,k,offset1,offset2; ptch=ChArray;/指针初始化 for(i=0;ChArrayi!=0;i+); coutThe length of the string is:iendl; for(j=0;ptchj!=0;j+); coutThe length of the string is:jendl; f

24、or(offset1=0;*(ChArray+offset1)!=0;offset1+); coutThe length of the string is:offset1endl; for(offset2=0;*(ptch+offset2)!=0;offset2+); coutThe length of the string is:offset2endl; for(k=0;*ptch+!=0;k+); coutThe length of the string is:kendl;,运行结果都相同,方式1数组名下标,方式2:指针和下标,方式3: 数组名加偏移量的间接引用,方式4:指针加偏移量的间接

25、引用,方式5:指针自加的间接引用,2022/12/20,51,例6.16求整型数组的平均值,显示数组元素和平均值。 void main() int intArray10=8,11,23,34,45,56,65,78,86,97,*ptint; int i,num,sum; float average; ptint=intArray; sum=0; num=sizeof(intArray)/sizeof(*intArray); for(i=0;inum;i+) sum=sum+*ptint+; average=(float)sum/num; ptint=intArray; cout数组元素是:n

26、; for(i=0;inum;i+) cout*ptint+ ; cout“n平均值是:” averageendl;,运行结果:数组元素是:8 11 23 34 45 56 65 78 86 97平均值是:50.3,指针初始化,求数组元素的数目,求平均值,指针再次初始化,输出数组元素和它们的平均值,2022/12/20,52,6.5.2 通过指针访问二维数组,二维数组可以看成是一维数组的一维数组 二维数组名也是地址(指针),但和一维数组名有不同的类型对一维数组A5数组名A的地址,是第一个元素A0的地址指针的类型是指向数组元素的指针A+1 A1的地址,2022/12/20,53,6.5.2 通过

27、指针访问二维数组,对二维数组B34数组名B的地址,一维数组B0的地址指针的类型是指向一维数组的指针B+1 一维数组B1的地址,2022/12/20,54,6.5.2 通过指针访问二维数组,定义指向一维数组的指针时,必须指出一维数组的大小 声明格式如下: 类型名 (*指针变量名)一维数组大小; 例如: char (*ptchb)4, (*ptchc)2; ptchb=B; ptchc=C;,2022/12/20,55,6.5.2 通过指针访问二维数组,指向一维数组的指针具有的特征: 二维数组名是指向一维数组的指针,不指向数组元素 指针加1 是指向下一个一维数组的指针 指向一维数组的指针的间接引用

28、的结果仍然是地址,即*ptchb仍然是地址,为一维数组B0第一个元素B00的地址 *ptchb是数组元素的地址,*ptchb就是数组元素的值访问二维数组第i行第j列元素: *(*(指针名+i)+j),2022/12/20,56,例6.17比较指向一维数组的指针和指向数组元素的指针 void main() short B34, C32; short (*ptshb)4, (*ptshc)2; ptshb=B; ptchc=C; cout比较不同的指向一维数组指针的差别n; coutptshb的地址是: ptshbn; coutptshb+1的地址是:ptshb+1n; coutptchc的地址是

29、: ptshcn; coutptchc+1的地址是:ptshc+1n;,比较不同的指向一维数组指针的差别ptshb的地址是: 0 x0012FF68ptchb+1的地址是:0 x0012FF70ptchc的地址是: 0 x0012FF5Cptchc+1的地址是:0 x0012FF60,B的第0行地址,B的第1行地址,C的第0行地址,C的第1行地址,2022/12/20,57,cout不同类型的指针n; coutptshb的地址是:ptshbendl; cout*ptshb的地址是:*ptshbendl; cout*ptshb+1的地址是:*ptshb+1endl; coutB01的地址是: ,

30、不同类型的指针ptshb的地址是: 0 x0012FF68*ptshb的地址是: 0 x0012FF68*ptshb+1的地址是: 0 x0012FF6AB01的地址是: 0 x0012FF6A*ptshb+1和&B01相等吗?Yes,B的第0行地址,B的第0行第0列 元素的地址,B的第0行第1列 元素的地址,B的第0行第1列 元素的地址,2022/12/20,58,例6.18 用单循环程序,求二维数组元素的平均值。 void main() int Array34=32,42,12,25,56,76,46,53,76,89,96,82, (*pt)4; int sum, j; float av

31、erage; sum=0; pt=Array; j=sizeof(Array) / sizeof (*Array); for(int i=0;ij;i+) sum=sum+*(*pt+i); average=(float)sum/j; cout数据的平均值等于:averageendl;,运行结果:数据的平均值等于57.0833,指向一维数组指针的初始化,求数组元素的数目,*Array就是元素Array00,数组求和,求平均值,输出平均值,2022/12/20,59,一维数组:int intArray2=8,11,*ptint;sum=sum+*ptint+;二维数组:int A23=32,42

32、,12,25,56,76,(*pt)3;sum=sum+*(*(pt+i)+j);,2022/12/20,60,6.6 指针访问动态内存,2022/12/20,61,6.6 指针访问动态内存,动态内存: 在程序执行时才可以申请、使用和释放的内存存放动态数据的区域称为“堆”,动态内存也称为堆内存 动态内存不能通过变量名来使用,只能通过指针来使用,2022/12/20,62,6.6.1 动态内存的申请和释放,C+中通过运算符new申请动态内存,运算符delete释放动态内存new的使用格式: new 类型名 (初值) int *p; p = new int(3); 申请成功: 返回指定类型内存的地

33、址申请失败: 返回NULL指针,2022/12/20,63,6.6.1 动态内存的申请和释放,运算符delete释放动态内存delete运算符使用格式: delete 指针名; delete p ; new 与delete 应该成对出现,2022/12/20,64,6.6.2 动态数组空间的申请和释放,申请动态数组要加上数组的大小: 一维: new 类型名常量表达式 ; 注意:动态申请数组空间时,不可以对数组进行初始化二维:int (*pi_marray)4;pi_marray = new int34;释放动态数组空间都用相同的表达式: delete ;,2022/12/20,65,6.6.3

34、 内存泄漏和指针悬挂,内存泄漏 动态申请的内存空间,没有正常释放,但也不能继续使用的情况。如: char *ch1;ch1 = new char(A);char *ch2 = new char;ch1=ch2; 原来为ch1所申请的存放字符A的空间就不能再使用了,产生了内存泄漏,2022/12/20,66,6.6.3 内存泄漏和指针悬挂,指针指向一个已经释放的空间,即指针悬挂 char *ch1, *ch2;ch1 = new char;ch2 = ch1;*ch2 = B;delete ch1; 指针ch2指向了一个已经释放的地址空间-指针悬挂,用delete ch2;语句来释放ch2所指向

35、的空间,会出现运行错误,2022/12/20,67,6.7 引用概念,2022/12/20,68,6.7 引用概念,引用(Reference)是C+中新引入的概念,是C语言中不存在的数据类型 引用是变量或者其他编程实体(如对象)的别名因此引用不可以单独定义如图6.4(a):变量A在内存中有自己的地址,而A的引用B实际上就是变量A,只是A的另外一个名字,2022/12/20,69,6.7.1 引用的声明和使用,引用通过运算符必须注意:引用必须在声明的时候就完成初始化,不可以先声明引用,然后再用另一个语句对它初始化。,2022/12/20,70,改进C中函数的形参定义为指针型参数时所带来的不安全性

36、int add( int *x, int *y ) *x=*x+*y;return *x;,int add( int ,改为,2022/12/20,71,6.7.1 引用的声明和使用,引用的特点: 引用不能独立存在,它只是其他变量的别名 引用必须在声明的同时初始化 引用一旦定义,引用关系就不可以更改B是A的引用,就不可能是其他变量的引用 引用的类型就是相关的变量的类型,引用的使用和变量的使用相同,2022/12/20,72,例6.22 引用的使用。观察以下程序的结果。 void main() int intA=10; int ,引用的值和相关变量值相同:refA=10引用的变化,则相关变量也变

37、化:intA=5引用的地址和相关变量地址相同:intA的地址0 x0012FF7C引用的地址和相关变量地址相同:refA的地址0 x0012FF7C,2022/12/20,73,例6.23 指针的引用。 void main() int intA=10,intB=20; int *pti=,指针的引用可以访问指针所指的变量:*refi=10指针变量原来的值:pti=0 x0012FF7C引用的变化,则相关指针也变化:pti=0 x0012FF78指针所指的变量值也发生变化:*pti20,2022/12/20,74,6.7.1 引用的声明和使用,如果不希望通过引用来改变相关的变量的值,可以定义常引

38、用: const 类型名 const_refA就是常引用。不可以通过const_refA来改变someInt变量的值,2022/12/20,75,6.7.2 通过引用传递函数参数,引用使用最多的场合是作为函数的形式参数 引用作为函数的形参有以下特点: 形参,实参是相同类型的变量 引用作为形参,参数传递属于地址传递 引用作为形参,在函数中并不产生实参副本是同一个实体 函数对引用的操作,也是对实参变量的操作函数调用可以改变实参数的值,2022/12/20,76,例6.24 用引用作为形式参数,交换两个实参数。 #include using namespace std;void swap_1(int

39、 ,函数调用前:a=12345 b=54321 函数调用后:a=54321 b=12345,2022/12/20,77,函数返回多个值 (可以用地址,还可用引用),float Length( int r ) return 2*PI*r;float Area( int r) return PI*r*r;,给定半径,计算面积、周长Void CalculateCircle( int r, float ,2022/12/20,78,6.7.2 通过引用传递函数参数,使用引用作为形式参数还需要注意: 如果实参数需要保护,使用“常引用”作为形参 用引用作形参和用变量作形参是有区别的尽管实参数可能相同函数s

40、wap(int a, int b)和swap(int &a, int&b)看起来是两个可以区分的重载函数 但调用相同: swap(x, y),无法区分,函数swap(int a, int b)和swap(int &a, int&b)不是可以区分的重载函数,2022/12/20,79,6.7.3 用引用作为函数的返回值,返回引用需注意: 返回引用需在函数的返回值类型中加以说明 类型名 返回引用实际是返回地址在使用上,或者直接使用这个地址;或者使用这个地址单元的数据 返回的引用可以作为左值继续操作而返回的变量值是不可以继续运算的。这是返回引用和返回变量值在使用上的主要区别。,2022/12/20,80,例6.25 引用作为函数返回值。 #include using namespace std;int ,返回引用的函数原型,使用返回引用的变量值,使用返回引用的地址,返回的引用继续加1,返回引用的函数,20输出1:pb=2030输出2:pc=3040输出3:41,2022/12/20,81,总结,指针变量的特点是可变性,即一个指针变量内的地址是可变的。所以,通过一个指针变量,就可以访问一个数组。引用的特点是不变性,一个变量的引用就只能和这个变量联系在一起。本章还介绍了函数调用的另一种方式:地址调用。具体又分为指针调用和引用调用。,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号