C++大学基础教程第六章课件_第1页
C++大学基础教程第六章课件_第2页
C++大学基础教程第六章课件_第3页
C++大学基础教程第六章课件_第4页
C++大学基础教程第六章课件_第5页
已阅读5页,还剩155页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

2022/12/101C++大学基础教程第6章指针和引用

2022/12/101C++大学基础教程第6章指针和引2022/12/102指针(Pointer)是C++和C的一种数据类型。很多其他高级语言也有类似的数据类型。引用(Reference)则是C++所特有的一种数据类型。2022/12/102指针(Pointer)是C++和C的一2022/12/103第六章指针和引用6.1指针的概念6.2指针的运算6.3指针和函数6.4指针和字符串6.5通过指针访问数组6.6指针访问动态内存6.7引用概念

2022/12/103第六章指针和引用6.1指针的概念2022/12/1046.1指针的概念

2022/12/1046.1指针的概念2022/12/1056.1.1

指针和指针变量指针是变量的地址 例如,定义整型变量a,a的地址就是一个指针。 直接指定内存某个地址开始(如0x00430100)存放指针的变量就是指针变量。2022/12/1056.1.1指针和指针变量指针是变量的2022/12/1066.1.1

指针和指针变量当声明了一个指针变量后,确定了三件事:1.变量本身在内存中所分配的地址和字节数(4个)2.系统允许的地址范围,及地址单元内可以存放的内容3.指针变量可以进行的运算访问指针变量时,只能看到地址通过这个地址访问地址单元中的内容,这样的访问称为对于内存单元的间接访问。2022/12/1066.1.1指针和指针变量当声明了一个2022/12/1076.1.2指针变量的声明和初始化指针变量声明的格式是:

类型名*变量名1,*变量名2;例如:

int*va1,*va2; char*ch1,*ch2;指针变量在声明后,变量的值(地址)是随机的。这样的指针变量是不能安全的使用的2022/12/1076.1.2指针变量的声明和初始化指2022/12/1086.1.2指针变量的声明和初始化在声明指针变量时的“*”,有两个含义:声明变量va1、ch1、ch2都是指针变量;说明变量va1的类型是(int*)型,即指向整型变量的指针。ch1和ch2的类型是(char*)型指针变量都是有类型的---所指定的地址单元中存放的数据的类型

2022/12/1086.1.2指针变量的声明和初始化在2022/12/1096.1.2指针变量的声明和初始化指针变量必须初始化后才能安全使用声明指针变量时进行初始化的格式:

类型名*指针变量名=&变量名;

charch1=’Y’,ch2=’A’; char*pch1=&ch1,*pch2=&ch2;2022/12/1096.1.2指针变量的声明和初始化指2022/12/10106.1.2指针变量的声明和初始化赋值方式进行初始化:

inti1=’Y’,i2=’A’; int*pi1,*pi2; pi1=&i1; pi2=&i2;没有初始化的指针变量不可以使用

(编译系统会给出警告,而运行时会出现错误)

2022/12/10106.1.2指针变量的声明和初始化2022/12/10116.2指针的运算

2022/12/10116.2指针的运算2022/12/10126.2指针的运算表6.1指针的运算2022/12/10126.2指针的运算2022/12/10136.2.1指针的赋值运算指针的赋值:地址的赋值同类型变量的地址同类型的已经初始化的指针变量;其他同类型的指针用0或者NULL赋值

“空指针”,即不指向任何的内存物理地址不同类型的指针不可以互相赋值

在指针赋值时,不存在类型自动转换的机制2022/12/10136.2.1指针的赋值运算指针的赋2022/12/1014

例6.1观察以下指针赋值运算的结果。如果将注释去掉,结果将如何?#include<iostream>usingnamespacestd;voidmain(){intva1=100,*pva1;int*pva2=NULL;floatvf1='A',*pvf1,*pvf2;cout<<"valueofpva2is"<<pva2<<endl;pva1=&va1;pvf1=pvf2=&vf1;cout<<pva1<<""<<&va1<<endl;cout<<pvf1<<""<<pvf2<<endl;//pvf1=pva1;}valueofpva2is0x000000000x0012FF7C0x0012FF7C0x0012FF740x0012FF74注释去掉会出现编译错误2022/12/1014例6.1观察以下指针赋值运算的结2022/12/10156.2.2间接引用运算

间接引用运算符“*”

:一元运算符

和指针变量连用,对指针所指向的内存地址单元进行间接访问使用的格式:

*指针变量如果指针变量iptr指向整型变量va *iptr就是变量va的内容

2022/12/10156.2.2间接引用运算间接引用运2022/12/1016

例6.2对变量的直接访问和间接访问#include<iostream>usingnamespacestd;

voidmain(){charch1='a',*ch;intk1=100;ch=&ch1; //指针ch指向变量ch1cout<<"*ch="<<*ch<<endl; //间接访问*ch='B';cout<<"ch1="<<ch1<<endl; //直接访问

ch1=k1;cout<<"*ch="<<*ch<<endl;//间接访问

}运行结果:*ch=ach1=B*ch=d2022/12/1016例6.2对变量的直接访问和间接访2022/12/1017

例6.3观察对这种指针变量间接访问的结果。#include<iostream>usingnamespacestd;voidmain(){intva=100,*pva,**ppva;//ppva是指向指针的指针

intk1=100;pva=&va;cout<<"*pva="<<*pva<<endl;//间接访问结果是整型数

ppva=&pva;cout<<"*ppva="<<*ppva<<endl;//间接访问结果是地址

cout<<"pva="<<pva<<endl; //就是指针pva的内容}运行结果:*pva=100*ppva=0x0012FF7Cpva=0x0012FF7C

2022/12/1017例6.3观察对这种指针2022/12/10186.2.2间接引用运算2022/12/10186.2.2间接引用运算2022/12/10196.2.3指针的算术运算

可以进行的只有加法和减法--和整数n做加法或者减法指针p和整数n相加(相减)的含义是指向当前指向位置p的前方或后方第n个数据的地址。2022/12/10196.2.3指针的算术运算可以进行2022/12/1020

例6.4通过指针的间接访问,输出下标为偶数的数组元素的值。#include<iostream>usingnamespacestd;voidmain(){intk1[10]={11,24,37,44,58,66,79,86,93,108},*k;k=&k1[0]; for(inti=0;i<10;i=i+2)

cout<<"k1["<<i<<"]="<<*(k+i)<<"";cout<<endl;}运行结果:K1[0]=11k1[2]=37….

数组第一个元素(下标为0)的地址赋值给指针k

每次循环,指针加22022/12/1020例6.4通过指针的间接访问,输出2022/12/10216.2.3指针的算术运算指针和指针的直接加法没有意义指针和指针的减法可以进行----求出两个指针之间可以存放几个指定类型的数据不允许用一个整数减一个指针2022/12/10216.2.3指针的算术运算指针和指针2022/12/10226.2.4指针的关系运算和逻辑运算

同类型的指针可以进行关系运算进行指针“大于”、“小于”的比较,是判定指针在内存中的相对位置指针和一般的整数比较没有意义,也不允许

惟一可以和指针比较的整数是0。判定指针是不是空指针

2022/12/10226.2.4指针的关系运算和逻辑运算2022/12/10236.2.5void类型指针

void类型的指针就是“无类型”指针:

void*指针名;存放的也是内存的地址,但不指定这个地址单元内的数据的类型2022/12/10236.2.5void类型指针voi2022/12/10246.2.5void类型指针使用要点

--任何指针都可以赋值给void指针

赋值后的void指针的类型仍然是void

--void指针不可以赋值给其他任何类型的指针

--void指针不可通过间接引用访问内存中数据

--必须进行指针类型的强制转换,才可以使用指针间接引用访问内存数据2022/12/10246.2.5void类型指针使用要点2022/12/1025

例6.5使用memcpy通用复制函数复制数组 原型void*memcpy(void*dest,constvoid*,size)

#include<iostream>usingnamespacestd;#include<string.h>voidmain(){charsrc[10]="012345678";chardest[10];char*pc=(char*)memcpy(dest,src,10);

cout<<pc<<endl;ints1[3]={1,2,3};intd1[3];int*pi=(int*)memcpy(d1,s1,12);

cout<<*pi<<""<<*(pi+1)<<""<<*(pi+2)<<endl;}运行结果:012345678123

复制字符数据,10个字节复制整型数据,12个字节2022/12/1025例6.5使用memcpy通用复制2022/12/10266.2.5void类型指针

void类型指针应用:显示字符指针的内容其他指针直接用cout语句输出地址值而字符指针输出的是字符串将字符指针强制转换为void指针cout语句输出便是地址值例:char*pch="HelloC++";cout<<pch<<endl;cout<<(void*)pch<<endl;

2022/12/10266.2.5void类型指针voi2022/12/1027指针是地址int*p;p=&a;*p=100;b=*p+2;2022/12/1027指针是地址int*p;*p=1002022/12/10286.3指针和函数

2022/12/10286.3指针和函数 2022/12/10296.3指针和函数在程序设计中,指针有很多应用其中之一就是作为函数的参数,形成了C++函数调用中的另一种调用方式:地址调用

2022/12/10296.3指针和函数在程序设计中,指针2022/12/10306.3.1指针作为函数的参数实现地址调用,必须满足以下条件:函数的形式参数是指针变量;函数的实参数是内存的地址 (可以是数组名、变量的地址、用变量地址初始化的指针)形参指针类型和实参地址类型必须相同

2022/12/10306.3.1指针作为函数的参数实现地2022/12/10316.3.1指针作为函数的参数实参传递给形参的是内存的地址,所以形参指针指向实参变量形参指针通过间接引用,直接访问实参变量(包括改变实参变量的值)函数调用后,改变了实参,如果有多个实参,就可以有多个实参变量在函数调用中被修改2022/12/10316.3.1指针作为函数的参数2022/12/1032

例6.6编写数据交换的函数。#include<iostream>voidSwap(int*a,int*b);voidmain(){ intx(5),y(10);

cout<<"主函数变量的值:

x="<<x<<"y="<<y<<endl; Swap(&x,&y); cout<<"返回后变量的值:

x="<<x<<"y="<<y<<endl;}voidSwap(int*a,int*b){ intt; t=*a; *a=*b; *b=t;cout<<"函数中完成了交换:*a="<<*a<<"*b="<<*b<<endl;}

运行结果:主函数变量的值:x=5y=10函数中完成了交换:*a=10*b=5返回后变量的值:

x=10y=5

变量的地址作为实参数指针变量作为形式参数2022/12/1032例6.6编写数据交换的函数。运行2022/12/10336.3.1指针作为函数的参数变量x和y的地址作实参传递给指针a和b,如图(a)通过间接引用*a和*b进行交换,实际上就是x和y进行交换,如图(b)2022/12/10336.3.1指针作为函数的参数变量x2022/12/1034

例6.7指针变量指向一个数组#include<iostream>usingnamespacestd;voidMove(int*a);voidmain(){ intx[5]={10,20,30,40,50},*px=x; cout<<"调用前的*px="<<*px<<endl;

Move(px); cout<<"调用后的px"; if(px==x)cout<<"没有变化,*px还是"<<*px<<endl; elsecout<<"也向前移动,*px变为"<<*px<<endl;}voidMove(int*a){ a=a+1;*a=100;cout<<“函数中完成了指针移动:*a="<<*a<<endl;}

运行结果:调用前的*px=10

函数中完成了指针移动:*a=20调用后的px没有变化*px还是10

指针作为实参数指针变量作为形式参数2022/12/1034例6.7指针变量指向一个数组运行2022/12/10356.3.3传递参数的保护:指针和常量通过数组名地址调用,可以改变实参数组内容但有时并不需要改变数组的值例如,在调用一个求数组最大值的函数时,就不希望数组的值发生变化希望在函数中能够限制对数组元素的修改使用常指针可以达到这个目的2022/12/10356.3.3传递参数的保护:指针和常2022/12/10366.3.3传递参数的保护:指针和常量常指针是指向常量的指针(PointertoConstantdata)规定指针所指向的内容不可以通过指针的间接引用来改变。常指针说明的格式是:

const类型名*指针名;

例如:constint*ptint;指针ptint的类型是(constint*),即指向一个恒定的整型数2022/12/10366.3.3传递参数的保护:指针和常2022/12/1037

例6.10常指针示例。观察以下程序的运行。

#include<iostream>usingnamespacestd;voidmain(){intia=10,ib=20;constint*ptint; ptint=&ia; //用ia地址初始化cout<<*ptint<<endl;ptint=&ib; //改变为ib的地址ib=ib+100; //ib本身仍然可以改变cout<<*ptint<<endl;//*ptint=100; //语句错误:左值是常量}运行结果:10

120常指针声明注释去掉会出现编译错误2022/12/1037例6.10常指针示例。观察以下程2022/12/10386.3.3传递参数的保护:指针和常量指针常量(Pointerconstant)指针本身是常量(地址是常量),不可以改变指针常量声明的格式:

类型名*const指针名=初值; charch,*constptch=&ch;数组名是数组的首地址--数组名是指针常量

2022/12/10386.3.3传递参数的保护:指针和常2022/12/1039

例6.11指针常量示例。指出以下程序的错误。#include<iostream>usingnamespacestd;voidmain()

{inta=10,b=100;

int*constpa=&a; //pa是指针常量

cout<<*pa<<endl;

*pa=20;

//指针常量的间接引用是允许的

cout<<a<<endl;pa=&b;

constintc=50; int*constpc=&c; }

错误语句注释掉后运行结果:10

20语句有错:常量不能当左值语句有错,地址类型不同不能用常量地址初始化指针常量2022/12/1039例6.11指针常量示例。指出以下2022/12/1040

例6.12用常指针作形参,函数printString可以输出数组的内容,不可以对数组修改。

#include<iostream>usingnamespacestd;voidprintString(constchar*);voidmain(){charphrase[]="C++isaprogramminglanguage";cout<<"Thestringis:\n";printString(phrase);cout<<endl;}//main函数结束voidprintString(constchar*Ptarray){while(*Ptarray) cout<<*Ptarray++; }

不使用常指针也是可以完成打印。但是没有保护了。数组名作实参数常指针作形式参数2022/12/1040例6.12用常指针作形参,函数p2022/12/10416.4指针和字符串

2022/12/10416.4指针和字符串 2022/12/10426.4.1字符串处理的两种方式字符串常量:双引号括起,‘\0’结束如: "Thisisastring"。字符串常量有自己固定的首地址如果将字符串常量的首地址看成是指针,这种指针既是常指针,也是指针常量。2022/12/10426.4.1字符串处理的两种方式字符2022/12/10436.4.1字符串处理的两种方式处理字符串两种方式:数组方式和指针方式

数组方式是将字符串存入字符数组:charstring_array[]="What’saniceday!";指针方式是用字符串常量来初始化一个字符指针:char*string_pt="What’saniceday!";2022/12/10436.4.1字符串处理的两种方式处理2022/12/10446.4.1字符串处理的两种方式指针常量不能放在等式左边运行时会出错2022/12/10446.4.1字符串处理的两种方式指针2022/12/10456.4.2字符串操作函数

实参数:字符数组名、已经初始化的字符指针,字符串常量目的串必须是可写的。2022/12/10456.4.2字符串操作函数实参数2022/12/1046

例6.14strcpy和strncpy的比较。#include<string>

voidmain(){intn;char*array1="HappyBirthdaytoYou";chararray3[15];chararray2[25];strcpy(array2,array1); cout<<"Thestringinarray1is:"<<array1<<"\nThestringinarray2is:"<<array2<<'\n';

/*strcpy(array3,array1); cout<<array3<<endl;*/n=sizeof(array3);strncpy(array3,array1,n-1);//复制array1的n-1个字符

array3[14]='\0';//添加'\0'到array3cout<<"Thestringinarray3is:"<<array3<<endl;}

不包括提示的运行结果HappyBirthdaytoYouHappyBirthdaytoYouHappyBirthday

复制array1到array2,没有问题复制array1到array3,空间不够,有运行错误按实际数组大小,复制array1到array3,没有问题2022/12/1046例6.14strcpy和strn2022/12/10476.5通过指针访问数组

2022/12/10476.5通过指针访问数组 2022/12/10486.5通过指针访问数组指针和数组有天然的联系数组名就是地址,也就是某种类型的指针虽然一维数组名和二维数组名都是地址,都可以看作是某种指针,但是指针的类型是不同的通过指针访问一维数组和二维数组的方法是不同的2022/12/10486.5通过指针访问数组指针和数组有2022/12/10496.5.1通过指针访问一维数组必须声明和数组类型相同的指针,并用数组名初始化指针:

intA[10],*pa=A;多种方式访问数组元素数组名和下标,如A[0]、A[4]

指针和下标,如pa[0]、pa[4]指针加偏移量的间接引用,如*(pa+4)数组名加偏移量的间接引用,如*(A+4)指针自加后的间接引用,如*pa++

2022/12/10496.5.1通过指针访问一维数组必须2022/12/1050

例6.15求数组内所存放的字符串的长度voidmain(){charChArray[]="Thisisastring.",*ptch;inti,j,k,offset1,offset2;ptch=ChArray; //指针初始化

for(i=0;ChArray[i]!='\0';i++);cout<<"Thelengthofthestringis:"<<i<<endl;for(j=0;ptch[j]!='\0';j++);cout<<"Thelengthofthestringis:"<<j<<endl;for(offset1=0;*(ChArray+offset1)!='\0';offset1++);cout<<"Thelengthofthestringis:"<<offset1<<endl;

for(offset2=0;*(ptch+offset2)!='\0';offset2++);cout<<"Thelengthofthestringis:"<<offset2<<endl;

for(k=0;*ptch++!='\0';k++);cout<<"Thelengthofthestringis:"<<k<<endl;}

运行结果都相同方式1数组名下标方式2:指针和下标方式3:数组名加偏移量的间接引用方式4:指针加偏移量的间接引用方式5:指针自加的间接引用2022/12/1050例6.15求数组内所存放的字符串2022/12/1051

例6.16求整型数组的平均值,显示数组元素和平均值。voidmain(){intintArray[10]={8,11,23,34,45,56,65,78,86,97},*ptint;inti,num,sum;floataverage;ptint=intArray;

sum=0;num=sizeof(intArray)/sizeof(*intArray);

for(i=0;i<num;i++)sum=sum+*ptint++;average=(float)sum/num;ptint=intArray;

cout<<"数组元素是:\n";for(i=0;i<num;i++)cout<<*ptint++<<"";cout<<“\n平均值是:”<<average<<endl;}

运行结果:数组元素是:8112334455665788697平均值是:50.3

指针初始化求数组元素的数目求平均值指针再次初始化输出数组元素和它们的平均值2022/12/1051例6.16求整型数组的平均值,显示2022/12/10526.5.2通过指针访问二维数组二维数组可以看成是一维数组的一维数组二维数组名也是地址(指针),但和一维数组名有不同的类型对一维数组A[5]数组名A的地址,是第一个元素A[0]的地址指针的类型是指向数组元素的指针A+1A[1]的地址2022/12/10526.5.2通过指针访问二维数组二维2022/12/10536.5.2通过指针访问二维数组对二维数组B[3][4]数组名B的地址,一维数组B[0]的地址指针的类型是指向一维数组的指针B+1一维数组B[1]的地址

2022/12/10536.5.2通过指针访问二维数组对二2022/12/10546.5.2通过指针访问二维数组定义指向一维数组的指针时,必须指出一维数组的大小声明格式如下:

类型名(*指针变量名)[一维数组大小];

例如:char(*ptchb)[4],(*ptchc)[2];ptchb=B;ptchc=C;2022/12/10546.5.2通过指针访问二维数组定义2022/12/10556.5.2通过指针访问二维数组指向一维数组的指针具有的特征:

二维数组名是指向一维数组的指针,不指向数组元素

指针加1是指向下一个一维数组的指针

指向一维数组的指针的间接引用的结果仍然是地址,即*ptchb仍然是地址,为一维数组B[0]第一个元素B[0][0]的地址

*ptchb是数组元素的地址,**ptchb就是数组元素的值 访问二维数组第i行第j列元素:*(*(指针名+i)+j)2022/12/10556.5.2通过指针访问二维数组指向2022/12/1056

例6.17比较指向一维数组的指针和指向数组元素的指针voidmain(){shortB[3][4],C[3][2];short(*ptshb)[4],(*ptshc)[2];ptshb=B;ptchc=C;cout<<"比较不同的指向一维数组指针的差别\n";cout<<"ptshb的地址是:"<<ptshb<<"\n";cout<<"ptshb+1的地址是:"<<ptshb+1<<"\n";cout<<"ptchc的地址是:"<<ptshc<<"\n";cout<<"ptchc+1的地址是:"<<ptshc+1<<"\n";比较不同的指向一维数组指针的差别ptshb的地址是:0x0012FF68ptchb+1的地址是:0x0012FF70ptchc的地址是:0x0012FF5Cptchc+1的地址是:0x0012FF60

B的第0行地址

B的第1行地址

C的第0行地址

C的第1行地址2022/12/1056例6.17比较指向一维数组的指针和2022/12/1057

cout<<"不同类型的指针\n";cout<<"ptshb的地址是:"<<ptshb<<endl;cout<<"*ptshb的地址是:"<<*ptshb<<endl;cout<<"*ptshb+1的地址是:"<<*ptshb+1<<endl;cout<<"B[0][1]的地址是:"<<&B[0][1]<<endl;//cout<<“ptchb和*ptchb相等吗?”<<(ptchb==*ptchb)<<endl;//有语法错误,类型不同

cout<<"*ptshb+1和&B[0][1]相等吗?";

if(*ptshb+1==&B[0][1])cout<<"Yes"<<endl;}

不同类型的指针ptshb的地址是:0x0012FF68*ptshb的地址是:0x0012FF68*ptshb+1的地址是:0x0012FF6AB[0][1]的地址是:0x0012FF6A*ptshb+1和&B[0][1]相等吗?Yes

B的第0行地址

B的第0行第0列元素的地址

B的第0行第1列元素的地址

B的第0行第1列元素的地址2022/12/1057cout<<"不同类型的指针2022/12/1058

例6.18用单循环程序,求二维数组元素的平均值。voidmain(){

intArray[3][4]={32,42,12,25,56,76,46,53,76,89,96,82},

(*pt)[4];intsum,j;floataverage;sum=0;pt=Array;

j=sizeof(Array)/sizeof(**Array); for(inti=0;i<j;i++) sum=sum+*(*pt+i);average=(float)sum/j;cout<<"数据的平均值等于:"<<average<<endl;}

运行结果:数据的平均值等于57.0833指向一维数组指针的初始化求数组元素的数目,**Array就是元素Array[0][0]数组求和求平均值输出平均值2022/12/1058例6.18用单循环程序,求二维数2022/12/1059一维数组:intintArray[2]={8,11},*ptint;sum=sum+*ptint++;二维数组:intA[2][3]={32,42,12,25,56,76},(*pt)[3];sum=sum+*(*(pt+i)+j);2022/12/1059一维数组:2022/12/10606.6指针访问动态内存2022/12/10606.6指针访问动态内存2022/12/10616.6指针访问动态内存

动态内存:在程序执行时才可以申请、使用和释放的内存存放动态数据的区域称为“堆”,动态内存也称为堆内存

动态内存不能通过变量名来使用,只能通过指针来使用2022/12/10616.6指针访问动态内存动态内存:2022/12/10626.6.1动态内存的申请和释放C++中通过运算符new申请动态内存,运算符delete释放动态内存new的使用格式:

new类型名(初值)

int*p;p=newint(3);申请成功:返回指定类型内存的地址申请失败:返回NULL指针2022/12/10626.6.1动态内存的申请和释放C+2022/12/10636.6.1动态内存的申请和释放运算符delete释放动态内存delete运算符使用格式:

delete指针名;

deletep;

new与delete应该成对出现2022/12/10636.6.1动态内存的申请和释放运算2022/12/10646.6.2动态数组空间的申请和释放申请动态数组要加上数组的大小:一维:

new类型名[常量表达式];注意:动态申请数组空间时,不可以对数组进行初始化二维:int(*pi_marray)[4];pi_marray=newint[3][4];释放动态数组空间都用相同的表达式:delete[]<指针名>;

2022/12/10646.6.2动态数组空间的申请和释放2022/12/10656.6.3内存泄漏和指针悬挂

内存泄漏动态申请的内存空间,没有正常释放,但也不能继续使用的情况。如:char*ch1;ch1=newchar('A');char*ch2=newchar;ch1=ch2;原来为ch1所申请的存放字符A的空间就不能再使用了,产生了内存泄漏2022/12/10656.6.3内存泄漏和指针悬挂内存2022/12/10666.6.3内存泄漏和指针悬挂指针指向一个已经释放的空间,即指针悬挂char*ch1,*ch2;ch1=newchar;ch2=ch1;*ch2='B';deletech1;指针ch2指向了一个已经释放的地址空间---指针悬挂用deletech2;语句来释放ch2所指向的空间,会出现运行错误2022/12/10666.6.3内存泄漏和指针悬挂指针指2022/12/10676.7引用概念2022/12/10676.7引用概念2022/12/10686.7引用概念引用(Reference)是C++中新引入的概念,是C语言中不存在的数据类型

引用是变量或者其他编程实体(如对象)的别名因此引用不可以单独定义如图6.4(a):变量A在内存中有自己的地址,而A的引用B实际上就是变量A,只是A的另外一个名字

2022/12/10686.7引用概念引用(Referen2022/12/10696.7.1引用的声明和使用引用通过运算符&来定义:

类型名&引用名=变量名;变量名必须已经定义和引用的类型必须相同

intsomeInt; int&refInt=someInt;必须注意:引用必须在声明的时候就完成初始化,不可以先声明引用,然后再用另一个语句对它初始化。

2022/12/10696.7.1引用的声明和使用引用通过2022/12/1070改进C中函数的形参定义为指针型参数时所带来的不安全性intadd(int*x,int*y){*x=*x+*y;return*x;}intadd(int&x,int&y){x=x+y;returnx;}改为2022/12/1070改进C中函数的形参定义为指针型参数时2022/12/10716.7.1引用的声明和使用引用的特点:

引用不能独立存在,它只是其他变量的别名引用必须在声明的同时初始化引用一旦定义,引用关系就不可以更改B是A的引用,就不可能是其他变量的引用引用的类型就是相关的变量的类型,引用的使用和变量的使用相同2022/12/10716.7.1引用的声明和使用引用的特2022/12/1072

例6.22引用的使用。观察以下程序的结果。

voidmain(){ intintA=10; int&refA=intA; cout<<“引用的值和 相关变量值相同:refA="<<refA<<endl; refA=5; cout<<“引用的变化,则相关变量也变化:intA="<<intA<<endl; cout<<“引用的地址和相关变量地址相同:intA的地址="<<&intA<<endl; cout<<“引用的地址和相关变量地址相同:refA的地址="<<&refA<<endl;}引用的值和相关变量值相同:refA=10引用的变化,则相关变量也变化:intA=5引用的地址和相关变量地址相同:intA的地址=0x0012FF7C引用的地址和相关变量地址相同:refA的地址=0x0012FF7C2022/12/1072例6.22引用的使用。观察以下程2022/12/1073

例6.23指针的引用。voidmain(){ intintA=10,intB=20; int*pti=&intA; int*&refi=pti; cout<<“指针的引用可以访问指针所指的变 量:*refi="<<*refi<<endl;cout<<"指针变量原来的值:pti="<<pti<<endl; refi=&intB;cout<<“引用的变化,则相关指针也变化:pti="<<pti<<endl; cout<<"指针所指的变量值也发生变化:*pti="<<*pti<<endl;}

指针的引用可以访问指针所指的变量:*refi=10指针变量原来的值:pti=0x0012FF7C引用的变化,则相关指针也变化:pti=0x0012FF78指针所指的变量值也发生变化:*pti=202022/12/1073例6.23指针的引用。指针的引2022/12/10746.7.1引用的声明和使用如果不希望通过引用来改变相关的变量的值,可以定义常引用:

const类型名&引用名=变量名;例如:intsomeInt=10;constint&const_refA=someInt;const_refA就是常引用。不可以通过const_refA来改变someInt变量的值2022/12/10746.7.1引用的声明和使用如果不希2022/12/10756.7.2通过引用传递函数参数引用使用最多的场合是作为函数的形式参数

引用作为函数的形参有以下特点:形参,实参是相同类型的变量

引用作为形参,参数传递属于地址传递

引用作为形参,在函数中并不产生实参副本是同一个实体

函数对引用的操作,也是对实参变量的操作函数调用可以改变实参数的值2022/12/10756.7.2通过引用传递函数参数引用2022/12/1076

例6.24用引用作为形式参数,交换两个实参数。

#include<iostream>usingnamespacestd;voidswap_1(int&x,int&y)

//引用作为形式参数{intj;j=x;x=y;y=j;}voidmain(){inta=12345,b=54321; cout<<"函数调用前:a="<<a<<"b="<<b<<endl; swap_1(a,b); //变量作为实参数

cout<<"函数调用后:a="<<a<<" b="<<b<<endl;}

函数调用前:a=12345b=54321

函数调用后:a=54321b=123452022/12/1076例6.24用引用作为形式参数,交2022/12/1077函数返回多个值(可以用地址,还可用引用)floatLength(intr){return2*PI*r;}floatArea(intr){returnPI*r*r;}给定半径,计算面积、周长VoidCalculateCircle(intr,float&length,float&area){length=2*PI*r;area=PI*r*r;}main(){intr=10;floatl,s;CalculateCircle(r,l,s);}2022/12/1077函数返回多个值(可以用地址,还2022/12/10786.7.2通过引用传递函数参数使用引用作为形式参数还需要注意:如果实参数需要保护,使用“常引用”作为形参用引用作形参和用变量作形参是有区别的尽管实参数可能相同函数swap(inta,intb)和swap(int&a,int&b)看起来是两个可以区分的重载函数但调用相同:swap(x,y),无法区分

函数swap(inta,intb)和swap(int&a,int&b)不是可以区分的重载函数2022/12/10786.7.2通过引用传递函数参数使用2022/12/10796.7.3用引用作为函数的返回值返回引用需注意:

返回引用需在函数的返回值类型中加以说明

类型名&函数名(形式参数表)返回引用的返回语句是:return变量名;返回引用实际是返回地址在使用上,或者直接使用这个地址;或者使用这个地址单元的数据返回的引用可以作为左值继续操作而返回的变量值是不可以继续运算的。这是返回引用和返回变量值在使用上的主要区别。2022/12/10796.7.3用引用作为函数的返回值返2022/12/1080

例6.25引用作为函数返回值。

#include<iostream>usingnamespacestd;int&fun(int&pf); voidmain(){intpa=10,pb;cout<<"输出1:pb="<<(pb=fun(pa))<<endl;

int&pc=fun(pa);

cout<<"输出2:pc="<<pc<<endl;cout<<"输出3:"<<++fun(pa)<<endl; }int&fun(int&pf){pf=pf+10;cout<<pf<<endl;returnpf;}

返回引用的函数原型使用返回引用的变量值使用返回引用的地址返回的引用继续加1返回引用的函数20输出1:pb=2030输出2:pc=3040输出3:412022/12/1080例6.25引用作为函数返回值。返2022/12/1081总结指针变量的特点是可变性,即一个指针变量内的地址是可变的。所以,通过一个指针变量,就可以访问一个数组。引用的特点是不变性,一个变量的引用就只能和这个变量联系在一起。本章还介绍了函数调用的另一种方式:地址调用。具体又分为指针调用和引用调用。2022/12/1081总结指针变量的特点是可变性,即一个指2022/12/1082C++大学基础教程第6章指针和引用

2022/12/101C++大学基础教程第6章指针和引2022/12/1083指针(Pointer)是C++和C的一种数据类型。很多其他高级语言也有类似的数据类型。引用(Reference)则是C++所特有的一种数据类型。2022/12/102指针(Pointer)是C++和C的一2022/12/1084第六章指针和引用6.1指针的概念6.2指针的运算6.3指针和函数6.4指针和字符串6.5通过指针访问数组6.6指针访问动态内存6.7引用概念

2022/12/103第六章指针和引用6.1指针的概念2022/12/10856.1指针的概念

2022/12/1046.1指针的概念2022/12/10866.1.1

指针和指针变量指针是变量的地址 例如,定义整型变量a,a的地址就是一个指针。 直接指定内存某个地址开始(如0x00430100)存放指针的变量就是指针变量。2022/12/1056.1.1指针和指针变量指针是变量的2022/12/10876.1.1

指针和指针变量当声明了一个指针变量后,确定了三件事:1.变量本身在内存中所分配的地址和字节数(4个)2.系统允许的地址范围,及地址单元内可以存放的内容3.指针变量可以进行的运算访问指针变量时,只能看到地址通过这个地址访问地址单元中的内容,这样的访问称为对于内存单元的间接访问。2022/12/1066.1.1指针和指针变量当声明了一个2022/12/10886.1.2指针变量的声明和初始化指针变量声明的格式是:

类型名*变量名1,*变量名2;例如:

int*va1,*va2; char*ch1,*ch2;指针变量在声明后,变量的值(地址)是随机的。这样的指针变量是不能安全的使用的2022/12/1076.1.2指针变量的声明和初始化指2022/12/10896.1.2指针变量的声明和初始化在声明指针变量时的“*”,有两个含义:声明变量va1、ch1、ch2都是指针变量;说明变量va1的类型是(int*)型,即指向整型变量的指针。ch1和ch2的类型是(char*)型指针变量都是有类型的---所指定的地址单元中存放的数据的类型

2022/12/1086.1.2指针变量的声明和初始化在2022/12/10906.1.2指针变量的声明和初始化指针变量必须初始化后才能安全使用声明指针变量时进行初始化的格式:

类型名*指针变量名=&变量名;

charch1=’Y’,ch2=’A’; char*pch1=&ch1,*pch2=&ch2;2022/12/1096.1.2指针变量的声明和初始化指2022/12/10916.1.2指针变量的声明和初始化赋值方式进行初始化:

inti1=’Y’,i2=’A’; int*pi1,*pi2; pi1=&i1; pi2=&i2;没有初始化的指针变量不可以使用

(编译系统会给出警告,而运行时会出现错误)

2022/12/10106.1.2指针变量的声明和初始化2022/12/10926.2指针的运算

2022/12/10116.2指针的运算2022/12/10936.2指针的运算表6.1指针的运算2022/12/10126.2指针的运算2022/12/10946.2.1指针的赋值运算指针的赋值:地址的赋值同类型变量的地址同类型的已经初始化的指针变量;其他同类型的指针用0或者NULL赋值

“空指针”,即不指向任何的内存物理地址不同类型的指针不可以互相赋值

在指针赋值时,不存在类型自动转换的机制2022/12/10136.2.1指针的赋值运算指针的赋2022/12/1095

例6.1观察以下指针赋值运算的结果。如果将注释去掉,结果将如何?#include<iostream>usingnamespacestd;voidmain(){intva1=100,*pva1;int*pva2=NULL;floatvf1='A',*pvf1,*pvf2;cout<<"valueofpva2is"<<pva2<<endl;pva1=&va1;pvf1=pvf2=&vf1;cout<<pva1<<""<<&va1<<endl;cout<<pvf1<<""<<pvf2<<endl;//pvf1=pva1;}valueofpva2is0x000000000x0012FF7C0x0012FF7C0x0012FF740x0012FF74注释去掉会出现编译错误2022/12/1014例6.1观察以下指针赋值运算的结2022/12/10966.2.2间接引用运算

间接引用运算符“*”

:一元运算符

和指针变量连用,对指针所指向的内存地址单元进行间接访问使用的格式:

*指针变量如果指针变量iptr指向整型变量va *iptr就是变量va的内容

2022/12/10156.2.2间接引用运算间接引用运2022/12/1097

例6.2对变量的直接访问和间接访问#include<iostream>usingnamespacestd;

voidmain(){charch1='a',*ch;intk1=100;ch=&ch1; //指针ch指向变量ch1cout<<"*ch="<<*ch<<endl; //间接访问*ch='B';cout<<"ch1="<<ch1<<endl; //直接访问

ch1=k1;cout<<"*ch="<<*ch<<endl;//间接访问

}运行结果:*ch=ach1=B*ch=d2022/12/1016例6.2对变量的直接访问和间接访2022/12/1098

例6.3观察对这种指针变量间接访问的结果。#include<iostream>usingnamespacestd;voidmain(){intva=100,*pva,**ppva;//ppva是指向指针的指针

intk1=100;pva=&va;cout<<"*pva="<<*pva<<endl;//间接访问结果是整型数

ppva=&pva;cout<<"*ppva="<<*ppva<<endl;//间接访问结果是地址

cout<<"pva="<<pva<<endl; //就是指针pva的内容}运行结果:*pva=100*ppva=0x0012FF7Cpva=0x0012FF7C

2022/12/1017例6.3观察对这种指针2022/12/10996.2.2间接引用运算2022/12/10186.2.2间接引用运算2022/12/101006.2.3指针的算术运算

可以进行的只有加法和减法--和整数n做加法或者减法指针p和整数n相加(相减)的含义是指向当前指向位置p的前方或后方第n个数据的地址。2022/12/10196.2.3指针的算术运算可以进行2022/12/10101

例6.4通过指针的间接访问,输出下标为偶数的数组元素的值。#include<iostream>usingnamespacestd;voidmain(){intk1[10]={11,24,37,44,58,66,79,86,93,108},*k;k=&k1[0]; for(inti=0;i<10;i=i+2)

cout<<"k1["<<i<<"]="<<*(k+i)<<"";cout<<endl;}运行结果:K1[0]=11k1[2]=37….

数组第一个元素(下标为0)的地址赋值给指针k

每次循环,指针加22022/12/1020例6.4通过指针的间接访问,输出2022/12/101026.2.3指针的算术运算指针和指针的直接加法没有意义指针和指针的减法可以进行----求出两个指针之间可以存放几个指定类型的数据不允许用一个整数减一个指针2022/12/10216.2.3指针的算术运算指针和指针2022/12/101036.2.4指针的关系运算和逻辑运算

同类型的指针可以进行关系运算进行指针“大于”、“小于”的比较,是判定指针在内存中的相对位置指针和一般的整数比较没有意义,也不允许

惟一可以和指针比较的整数是0。判定指针是不是空指针

2022/12/10226.2.4指针的关系运算和逻辑运算2022/12/101046.2.5void类型指针

void类型的指针就是“无类型”指针:

void*指针名;存放的也是内存的地址,但不指定这个地址单元内的数据的类型2022/12/10236.2.5void类型指针voi2022/12/101056.2.5void类型指针使用要点

--任何指针都可以赋值给void指针

赋值后的void指针的类型仍然是void

--void指针不可以赋值给其他任何类型的指针

--void指针不可通过间接引用访问内存中数据

--必须进行指针类型的强制转换,才可以使用指针间接引用访问内存数据2022/12/10246.2.5void类型指针使用要点2022/12/10106

例6.5使用memcpy通用复制函数复制数组 原型void*memcpy(void*dest,constvoid*,size)

#include<iostream>usingnamespacestd;#include<string.h>voidmain(){charsrc[10]="012345678";chardest[10];char*pc=(char*)memcpy(dest,src,10);

cout<<pc<<endl;ints1[3]={1,2,3};intd1[3];int*pi=(int*)memcpy(d1,s1,12);

cout<<*pi<<""<<*(pi+1)<<""<<*(pi+2)<<endl;}运行结果:012345678123

复制字符数据,10个字节复制整型数据,12个字节2022/12/1025例6.5使用memcpy通用复制2022/12/101076.2.5void类型指针

void类型指针应用:显示字符指针的内容其他指针直接用cout语句输出地址值而字符指针输出的是字符串将字符指针强制转换为void指针cout语句输出便是地址值例:char*pch="HelloC++";cout<<pch<<endl;cout<<(void*)pch<<endl;

2022/12/10266.2.5void类型指针voi2022/12/10108指针是地址int*p;p=&a;*p=100;b=*p+2;2022/12/1027指针是地址int*p;*p=1002022/12/101096.3指针和函数

2022/12/10286.3指针和函数 2022/12/101106.3指针和函数在程序设计中,指针有很多应用其中之一就是作为函数的参数,形成了C++函数调用中的另一种调用方式:地址调用

2022/12/10296.3指针和函数在程序设计中,指针2022/12/101116.3.1指针作为函数的参数实现地址调用,必须满足以下条件:函数的形式参数是指针变量;函数的实参数是内存的地址 (可以是数组名、变量的地址、用变量地址初始化的指针)形参指针类型和实参地址类型必须相同

2022/12/10306.3.1指针作为函数的参数实现地2022/12/101126.3.1指针作为函数的参数实参传递给形参的是内存的地址,所以形参指针指向实参变量形参指针通过间接引用,直接访问实参变量(包括改变实参变量的值)函数调用后,改变了实参,如果有多个实参,就可以有多个实参变量在函数调用中被修改2022/12/10316.3.1指针作为函数的参数2022/12/10113

例6.6编写数据交换的函数。#include<iostream>voidSwap(int*a,int*b);voidmain(){ intx(5),y(10);

cout<<"主函数变量的值:

x="<<x<<"y="<<y<<endl; Swap(&x,&y); cout<<"返回后变量的值:

x="<<x<<"y="<<y<<endl;}voidSwap(int*a,int*b){ intt; t=*a; *a=*b; *b=t;cout<<"函数中完成了交换:*a="<<*a<<"*b="<<*b<<endl;}

运行结果:主函数变量的值:x=5y=10函数中完成了交换:*a=10*b=5返回后变量的值:

x=10y=5

变量的地址作为实参数指针变量作为形式参数2022/12/1032例6.6编写数据交换的函数。运行2022/12/101146.3.1指针作为函数的参数变量x和y的地址作实参传递给指针a和b,如图(a)通过间接引用*a和*b进行交换,实际上就是x和y进行交换,如图(b)2022/12/10336.3.1指针作为函数的参数变量x2022/12/10115

例6.7指针变量指向一个数组#include<iostream>usingnamespacestd;voidMove(int*a);voidmain(){ intx[5]={10,20,30,40,50},*px=x; cout<<"调用前的*px="<<*px<<endl;

Move(px); cout<<"调用后的px"; if(px==x)cout<<"没有变化,*px还是"<<*px<<endl; elsecout<<"也向前移动,*px变为"<<*px<<endl;}voidMove(int*a){ a=a+1;*a=100;cout<<“函数中完成了指针移动:*a="<<*a<<endl;}

运行结果:调用前的*px=10

函数中完成了指针移动:*a=20调用后的px没有变化*px还是10

指针作为实参数指针变量作为形式参数2022/12/1034例6.7指针变量指向一个数组运行2022/12/101166.3.3传递参数的保护:指针和常量通过数组名地址调用,可以改变实参数组内容但有时并不需要改变数组的值例如,在调用一个求数组最大值的函数时,就不希望数组的值发生变化希望在函数中能够限制

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论