第6章 指针.docx
- 文档编号:3444280
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:34
- 大小:99.09KB
第6章 指针.docx
《第6章 指针.docx》由会员分享,可在线阅读,更多相关《第6章 指针.docx(34页珍藏版)》请在冰豆网上搜索。
第6章指针
第6章指针与字符串
本章要点
● 指针与内存地址的关系
● 指针型变量的声明和用法
● 指针与数组的关系
● 在函数参数中使用指针
● 指针与类、对象
● 指针与字符串
● 动态内存分配与new和delete运算符
● string类
6.1指针的概念
如果在程序中定义了一个变量,在编译时就给这个变量分配内存单元。
内存区的每一个字节(即单元)有一个编号,这就是地址。
直接访问:
是按变量地址存取变量值的方式。
例如,程序已定义了一个整型变量i、j、k:
inti=3,j=6,k;
如图6-1所示,
图6-1内存单元分配示意图
编译时系统分配ox20000000~0x20000003四个字节给变量i,0x20000004~ox20000007字节给j,0x20000008~ox2000000B字节给k。
例如,运算式k=i+j的执行是根据变量名与地址的对应关系,找到变量i的开始地址ox20000000,然后从由ox20000000起始的四个字节中取出数据(即变量i的值3),同样再从0x20000004~ox20000007字节取出j的值(6),将它们相加后的和(9)送到k所占用的0x20000008~ox2000000B字节单元中。
间接访问:
将变量i的地址存放在另一个内存单元中。
假设定义了变量i_pointer是存放整型变量的地址的,可以通过下面的语句将i的地址存放到i_pointer中:
i_pointer=&i;
这时,i_pointer的值就是0x20000000,即变量i所占用单元的起始地址。
6.2指针型变量
指针型变量是保存指针数据类型的变量。
6.2.1指针型变量的声明
定义指针变量的一般形式如下:
<类型标识符>*指针变量名
例如:
int*pn;//pn是用来保存一个int型变量的地址的指针型变量
f1oat*pf;//pf是用来保存一个float型变量的地址的指针型变量
BOOL*pb;//pb是保存一个int型变量的地址的指针型变量,注意BOOL型与int类型的共用特性
char*ppBuffer[5];//数组ppBuffer是用来保存5个char型变量的地址的指针型数组
void*pAddress;//pAddress是一个void*型指针型变量
void*型指针是很特殊的一种指针,它不像int*、float*等指针那样,指向的内存单元中保存的是具体的一种数据类型的变量,void*只是一个地址,这个地址中保存的可能是int型变量的起始地址,也可能是f1oat型变量的起始地址,也可能什么都不是。
C++语言中定义了一个符号常数NULL,用来代表空指针值。
所谓空指针值是人为规定的一个数值,用来表示“无效”的指针值。
在VisualC++中,NULL被定义为0。
6.2.2指针的基本操作
与指针有关的基本运算符有以下两个。
(1) &变量名。
&为取地址运算符,用来获取变量的首地址。
(2)*指针变量名。
*为指向运算符,用来获取指针变量所指向变量的值。
&和*运算符都是单目运算符,其优先级高于所有双目运算符,采用从右到左的结合性。
例如:
inti=5,j,*pint;//定义整型变量i、j;同时定义整型指针变量phint
phint=&i:
//将i的指针(即首地址)赋给phint,使phnti指向i
j=*phint;//将phint所指变量i的值(5)赋给变量j
说明:
①指针变量是有类型的。
②要注意第1句和第3句中*phint的区别:
第1句是指针变量定义语句,其中的*表示phint是一个指针类型变量;第3句其中的*表示取出指针变量phint的值(即phint所指内存单元中的内容)。
6.2.3指针变量的初始化与引用
与其它变量一样,指针变量也可以在定义时对其赋初值(即指针变量的初始化)。
这里要注意的是,所赋的初值必须是所定义数据类型的变量地址。
如果没有具体明确的变量地址要被赋值,可以先赋以NULL。
【例6-1】指针变量的使用例题。
#include
voidmain( )
{
intm,n,*p1=&m,*p2=&n,*phint=NULL;
m=n=6;
cout<<"*p1="<<*p1<<",*p2="<<*p2< cout<<"p1="< cout<<"m="< cout<<"phint="< *p1+=3; p2=p1; *p2*=4; phint=p2; cout<<"*p1="<<*p1<<",*p2="<<*p2< cout<<"p1="< cout<<"m="< cout<<"phint="< } 程序运行结果为 *p1=6,*p2=6 p1=0x0012FF7C,p2=0x0012FF78 m=6,n=6 pjint=0x00000000 *p1=36,*p2=36 p1=0x0012FF7C,p2=0x0012FF7C m=36,n=6 pjint=0x0012FF7C 特殊的void*型指针由于没有任何一种数据类型同它对应,要将其它的任何一种指针值赋予void*型指针变量都必须通过强制类型转换。 例如: void*nPointer; int*pnValue; nPointer=(void*)pnValue; 2.指针的算术运算 对于非void*型的指针型变量,只能进行加一个整数、减一个整数和两个指针变量相减三种运算。 void*型指针变量不能做任何算术运算。 例如: intnValue; int*pPointer1=&nValue,*pPointer2; pPointer2=pPointer1+1; 假设pPointer1的初始地址是0x20000000,则进行pPointer2=pPointer1+1;运算后,pPointer2的地址值是0x20000004,如图6-2所示。 对于指针的自加(++)与自减(--)运算,显然很好理解,即 pPointer2=pPointer1+1; 相当于: pPointer2=pPointer1++; 注意: 两个指针不能相加。 3.指针的关系运算 指针间也可以进行关系运算。 关系运算符>、<、==、>=、<=、! =可以用来连接两个指针型变量做关系运算。 指针间的关系运算结果就是两个指针所指的地址值的大小的关系运算结果。 由于内存地址是线性编码的,所以,如果一个变量m的保存位置在变量n之前,则&m<&n就等于1,&m>&n就等于0。 两个进行关系运算的指针一般要求是同一类型的 指针。 4.常指针(const修饰符与指针) const也可以与指针一起使用。 它们的组合情况较复杂,可简单归纳为三种: 指向常量的指针、常指针和指向常量的常指针。 (1)指向常量的指针 指向常量的指针是指一个指向常量的指针变量,指针可更新,但指针所指的数据不可修改,例如: constchar*name="chen";//声明指向字符常量的指针变量 name[3]='a';//错误,即name所指的数据不可修改 name="zhang";//合法,指针可更新 (2)常指针 常指针是指把指针本身,而不是它指向的对象声明为常量。 创建一个常指针,就是创建一个不能移动的固定指针,但是它所指的数据可以改变。 例如: char*constname="chen";//常指针 name[3]='a'//合法 name="zhang";//出错 (3)指向常量的常指针 指向常量的常指针是指这个指针本身不能改变,它所指向的数据值也不能改变。 例如: constchar*constname="chen";//指向常量的常指针 name[3]='a';//出错,不能改变指针所指的值 name="zhang";//出错,不能改变指针本身 5.引用型变量 关于引用的声明在1.2.4节中已介绍过,这里,我们再通过以下例题来看看引用与目标变量之间的关系。 【例6-2】引用的声明和使用例题。 #include voidmain( ) { inti,j=4,&ri=i;//定义ri为整型变量i的引用 i=3; cout<<"i="< cout<<"i的地址是"<<&i<<",ri的地址是"<<&ri< ri+=5; cout<<"i="< cout<<"i的地址是"<<&i<<",ri的地址是"<<&ri< ri=j; cout<<"i="< cout<<"i的地址是"<<&i<<",ri的地址是"<<&ri<<",j的地址是"<<&j< ri=&j;//出错 } 删除最后一句出错的语句后,程序的运行结果为 i=3,ri=3 i的地址是0x0012FF7C,ri的地址是0x0012FF7C i=8,ri=8 i的地址是0x0012FF7C,ri的地址是0x0012FF7C i=4,ri=4,j=4 i的地址是0x0012FF7C,ri的地址是0x0012FF7C,j的地址是0x0012DD78 6.3指针与数组 指针与数组之间有着密切的联系。 实际上,C++语言对数组的操作完全是通过指针来进行的。 6.3.1指针与数组的关系 数组的实质是内存中一块连续的空间,存储着同一类型变量的数据。 实际上,C++语言就是这样对数组元素进行访问的。 在声明一个数组时,C++语言将在内存中开辟两个空间,一个用于保存数组元素,另一个用于保存数组的第1个元素的地址。 数组名就是用于保存数组第1个元素的地址的指针型常量。 通过保存数组第1个元素地址的数组名这个指针型常量,可以使用“[]”运算符访问数组元素,也可以直接使用指针的运算规则访问数组元素。 例如: intpnArray[10]; intnValue; nValue=*pnArray;//nValue等于prlArray的第1个元素的值 nValue=pnArray[5];//nValue等于pnArray的第6个元素的值 nValue=*(pnArray+5);//与上一句nValue=pnArray[5]等价 同样,对于指针型变量也可以使用“[]”运算符,将其作为数组来使用。 如有 int*pnPointer=pnArray;//数组名pnArray本身是指针型,因此不必用取地址运算符& 则以下3条语句等价: nValue=pnArray[5]; nValue=pnPointer[5]; nValue=*(pnPointer+5); 指针与数组之间的区别: 指针型变量是变量,是可以不断赋值的;而数组名虽然是指针,但它是一指针型常量,只能指向固定的内存地址,不能将一个指针值赋予一个数组名。 以下语句在C++语言中是错误的: charcArray[10]; charch; cArray=&ch;//错误,不允许将一个指针值赋予数组名 6.3.2通过指针引用数组元素 由于指针同数组的特殊关系,我们已经看到,数组与指针几乎可以互换使用,下面利用一个具体例子来说明通过指针引用数组元素的方法。 【例6-3】用指针的方法编写求一个数组中所有元素之和的程序。 分析: 此函数的参数可以写成指针型。 由于数组名的实质就是指针,在调用该函数时,只要将数组名作为实际参数传递给函数即可。 程序代码如下: #include intSum(int*pPointer,intn) { intnSum=0; while(n>0) { nSum+=*pPointer++; n--; } returnnSum; } voidmain( ) { intpnArray[10]={6,7,8,9,5,4,3,2,10,1}; cout<<"数组各元素和: sum1="< } 程序运行结果为 数组各元素之和: sum1=55 需要注意的是,在main( )中调用Sum函数时,实际参数pnArray与形式参数pPointer尽管是指针类型,但它们间仍然是赋值关系,在Sum中对形式参数pPointer进行的加法运算不会影响到Sum函数以外的指针型常量(数组名)pnArray的值。 但由于pPointer与pnArray的值相同,也就是说它们指向同一个地址,所以在函数内如果对pPointer所指向的地址中保存的值进行更改操作时,在函数外的pnArray如果访问这个地址会得到被Sum更改后的值。 上例中可以将语句nSum+=*pPointer++替换成nSum+=pPointer[10-n],或者将语句intSum(int*pPointer,intn)替换成intSum(intpPointer[],intn),程序运行结果都完全一样。 【例6-4】用指针的方法编写将数组各元素进行排序的函数程序。 #include voidCompositor(int*pPointer,intn) { inti,j,t,p; for(i=0;i { p=i; for(j=i+1;j { if(*(pPointer+p)<=*(pPointer+j))p=j; } t=*(pPointer+i); *(pPointer+i)=*(pPointer+p); *(pPointer+p)=t; } } voidmain( ) { intpnArray[10]={6,7,8,9,5,4,3,2,10,1}; inti; Compositor(pnArray,10); cout<<"排序后的数组为: "< for(i=0;i<10;i++) cout<<""< } 程序运行结果为 排序后的数组如下: 10,9,8,7,6,5,4,3,2,1 6.3.3指向多维数组的指针 如定义一维数组 inta[10],*pa=a; 则一维数组a中的任何一个元素: a[i]等同于*(a+i) 如定义二维数组 intfMatrix[3][4],*pPointer=&fMatrix[0][0]; 则二维数组fMatrix中的任何一个元素: fMatrix[m][n]等同于*(pPointer+m*4+n) 对二维数组fMatrix[3][4]中关于一维数组名fMatrix[m](0 fMatrix[0]等于&fMatrix[0][0] fMatrix[1]等于&fMatrix[1][0] fMatrix[2]等于&fMatrix[2][0] fMatrix[m][n]等同于*(fMatrix[m]+n) 二维数组名fMatrix的含义: fMatrix等于&fMatrix[0] fMatrix[m][n]等同于*(*(fMatrix+m)+n) 注意: fMatrix的数据类型不是int*,而是int**。 【例6-5】用指针的方法操作二维数组,并输出相应的指针值(地址),注意观察和比较一级指针和二级指针。 #include voidmain() { staticintaMatrix[3][4]={{1,2,3,4},{2,3,4,5},{5,4,3,2}}; inti,j,*p; //使用二维数组的行地址(一级指针) cout<<"使用二维数组的行地址(一级指针)输出数组: "< for(i=0;i<3;i++) { p=aMatrix[i]; for(j=0;j<4;j++) cout<<""<<*(p+j); cout< } //使用二维数组名(二级指针) cout<<"使用二维数组名(二级指针)输出数组: "< for(i=0;i<3;i++) { p=aMatrix[i]; for(j=0;j<4;j++) cout<<""<<*(*(aMatrix+i)+j); cout< } cout<<"相应的指针值: "< cout<<"二维数组名"<<"行地址"<<"数组元素地址"< cout< cout<<"指针+1后的值: "< cout< cout<<"各行首地址: "< for(i=0;i<3;i++) cout< } 程序运行结果为 使用二维数组的行地址(一级指针)输出数组: 1234 2345 5432 使用二维数组名(二级指针)输出数组: 1234 2345 5432 输出相应的指针值(以下地址值根据系统运行情况而定): 二维数组名行地址数组元素地址 0x0012FF500x0012FF500x0012FF50 指针+1后的值: 0x0012FF600x0012FF540x0012FF54 各行首地址: 0x0012FF50 0x0012FF60 0x0012FF70 【例6-6】用指针的方法将二维数组转置(行列互换),并输出结果。 #include voidmain( ) { intaMatrix[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; inti,j,*p; cout<<"转置前的矩阵: "< for(i=0;i<3;i++) { p=aMatrix[i];//p存放各行的起始地址 for(j=0;j<4;j++) cout<<""<<*(p+j); cout< } cout<<"转置后的矩阵: "< for(j=0;j<4;j++) { for(i=0;i<3;i++) { p=aMatrix[i];//p存放各行的起始地址(注意所在的位置) cout<<""<<*(p+j); } cout< } 程序运行结果为 转置前的矩阵: 1234 5678 9101112 转置后的矩阵: 159 2610 3711 4812 6.3.4指针数组与多级指针 1.指针数组 声明指针型变量的形式如下: <类型标识符>*指针数组名[数组长度] 例如: int*ppArray[5];//声明一个保存5个int*型元素的指针数组 char*ppString[6];//声明一个保存6个char*型元素的指针数组 2.多级指针 多级指针一般又称为指针的指针。 定义二级指针变量的一般形式如下: <类型标识符>**二级指针变量名 例如,二维数组名就是一个典型的二级指针。 intaMatrix[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; int**pp=aMatrix;//编译可以通过,执行时,内存错误,可以直接用aMatrix作二级指针。 6.4指针与函数 6.4.1指针变量作为函数参数 指针类型也可以作为函数的参数。 通过指针型参数,可以将一个变量的地址传递给函数,而通过这个地址,函数体中的语句就可以修改函数以外的变量的值。 【例6-7】用传送地址的方法实现变量a和b的内容交换。 #include voidchangFunction(int*,int*);//函数声明语句,参数为指针类型 voidmain( ) { inta=5,b=10; cout<<"函数调用前:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第6章 指针