1、第6章 指针与字符串 第第6 6章章 指针与字符串指针与字符串 6.1 指针的概念指针的概念 6.2 指针型变量指针型变量 6.3 指针与数组指针与数组 6.4 指针与函数指针与函数 6.5 指针与类、对象指针与类、对象 6.7 动态内存分配与动态内存分配与new和和delete运算符运算符 6.8 string类类 第6章 指针与字符串 6.1 指针的概念指针的概念 指针既是C+语言学习中的难点又是重点,要掌握C+语言的精髓,必须学会熟练使用指针。本节先引入指针的概念。 为了说明什么是指针,必须弄清楚数据在内存中是如何存储的,又是如何读取的。如果在程序中定义了一个变量,在编译时就给这个变量分
2、配内存单元。 第6章 指针与字符串 系统根据程序中定义的变量类型,分配一定长度的空间。例如,许多计算机系统对整型变量分配四个字节,对实型变量分配四个字节,对字符型变量分配一个字节,。内存区的每一个字节(即单元)有一个编号,这就是“地址”,它相当于旅馆中的房间号。在地址所标识的单元中存放数据,这相当于旅馆中各个房间中住旅客一样。第6章 指针与字符串 在此,务必弄清楚一个内存单元的地址与内存单元的内容这两个概念的区别。如图6-1所示,假设程序已定义了三个整型变量i、j、k,编译时系统分配2000和2001两个字节给变量i,2002、2003字节给j,2004、2005给k。在内存中已没有i、j、k
3、这些变量名了,对变量值的存取都是通过地址进行的。 第6章 指针与字符串 图6-1 内存单元分配示意 第6章 指针与字符串 还可以采用另一种称之为“间接访问”的方式。这类似于在程序中定义整型、实型、字符等变量,也可以定义这样一种特殊的变量,它是用来存放地址的,即可以将变量i的地址存放在另一个内存单元中。假设我们定义了变量i_pointer是存放整型变量的地址的,它被分配为3030、3031字节单元。可以通过下面的语句将i的地址存放到i_pointer中: i_pointer=&i; 这时,i_pointer的值就是2000,即变量i所占用单元的起始地址。 第6章 指针与字符串 要存取变量i的值,
4、也可以采用间接方式:先找到存放“i的地址”的单元地址(3030、3031),从中获得i的起始地址(2000),然后从2000、2001字节中取出i的值(3)。 简而言之,指针就是指向内存中某个单元的地址值。所有的变量、数组、对象都是保存在内存中的。凡是保存在内存中的信息,必须知道它在内存中的位置。在计算机中,引用了“地址”这个词来代表内存的某个位置。内存以字节为单位编址,称为内存地址。第6章 指针与字符串 6.2 指针型变量指针型变量 6.2.1指针型变量的声明 在C+语言中,任何一种数据类型都有对应的指针类型,甚至指针类型本身也有指针类型。要声明一种数据类型的指针型变量,必须在声明变量时将“
5、*”号加在数据类型的后面。定义指针变量的一般形式如下: *指针变量名第6章 指针与字符串 定义中的“*”表示所定义的变量是一个指针变量,以示与其它变量的区别。指针变量名的命名应符合标识符的命名规则。指针变量的值是另一变量的指针(即首地址)。定义中的数据类型是指针变量所指变量的数据类型。 注意,一个指针变量只能指向定义所指定数据类型的变量,即存放定义所指定数据类型的变量的地址,因此,指针变量所存放的地址是一个具有指定数据类型的地址。例如: 第6章 指针与字符串 int* pnNumber;/pnNumber是用来保存一个指向int型变量的指针的指针型变量f1oat* pfRealNumber;
6、/pfRealNumber是用来保存一个指向float型变量的指针的指针型变量BOOL*pbResult;/pbResult是保存一个指向int型变量的指针的指针型变量,注意BOOL型 /与int型的共用特性第6章 指针与字符串 char*ppBuffer5;/数组ppBuffer是用来保存5个指向char型变量的指针的指针型变量数组void*pAddress;/pAddress是一个保存内存地址的指针型变量,该内存地址中保存的数据 /类型无须知道 void*型指针是很特殊的一种指针,它不像int*、float*等指针那样,指向的内存单元中保存的是具体的一种数据类型的变量,void*只是一个地
7、址,这个地址中保存的可能是int型变量的起始地址,也可能是f1oat型变量的起始地址,也可能什么都不是。第6章 指针与字符串 声明指针型变量时,“*”前后可以加空格,也可以不加,这完全取决于程序员的习惯。 尽管大多数指针变量类型同其对应的普通数据类型之间表面上只差一个“*”号,但它们之间的关系相当特殊。必须牢记这样的事实:指针型变量也是变量,指针型变量保存的是地址值,这个值实际上就是一个有特殊意义的整数,称为指针。 C+语言中定义了一个符号常数NULL,用来代表空指针值。所谓空指针值是人为规定的一个数值,用来表示“无效”的指针值。在VisualC+中,NULL被定义为0。 第6章 指针与字符串
8、 6.2.2 指针的基本操作 与指针有关的基本运算符有以下两个。 (1)&变量名。“&”为取地址运算符,用来获取变量的首地址。 (2)*指针变量名。“*”为指向运算符,用来获取指针变量所指向变量的值。 “&和“*”运算符都是单目运算符,其优先级高于所有双目运算符,采用从右到左的结合性。第6章 指针与字符串 例如:int i=5,j,*phint; /定义整型变量i、j;同时定义整型指针变量phintphint=&i:/将i的指针(即首地址)赋给phint,使phnti指向ij=*phint;/将phint所指变量i的值(5)赋给变量j第6章 指针与字符串 说明: 指针变量是有类型的。要使指针变
9、量phint存放整型变量i的地址,phint也必须定义为整型指针变量,否则编译时将出错。 要注意第1句和第3句中*phint的区别:第1句是指针变量定义语句,其中的“*”表示phint是一个指针类型变量;第3句中的“*”表示取出指针变量phint的值(即phint所指内存单元中的内容)。第6章 指针与字符串 6.2.3 指针变量的初始化与引用 与其它变量一样,指针变量也可以在定义时对其赋初值(即指针变量的初始化)。这里要注意的是,所赋的初值必须是所定义数据类型的变量地址。对于上面的程序片段,我们也可以用以下两句来实现: int i=5,j,*phint=&i;/用整型变量i的首地址初始化整型指
10、针变量phint j=*phint;第6章 指针与字符串 在此还是要强调,指针变量定义语句中的“*”只是一个标志,并没有间接存取内容的含义,不能把int*phint=&i;理解为将i的地址赋给phint所指的内存单元,而应理解为将i的地址赋给phint本身。 除void*型指针变量以外,其它指针型变量可以通过在变量名前加“*”号的方法访问该变量所指向的内存地址中所保存的相应数据类型变量的值。 第6章 指针与字符串 【例6-1】指针变量的使用例题。#includevoid main()int m,n,*p1=&m,*p2=&n,*phint=NULL;m=n=6;cout*p1=*p1,*p2=
11、*p2endl;coutp1=p1,p2=p2endl;coutm=m,n=nendl;coutphint=phintendl;第6章 指针与字符串 *p1+=3;p2=p1;*p2*=4;phint=p2;cout*p1=*p1,*p2=*p2endl;coutp1=p1,p2=p2endl;coutm=m,n=nendl;coutphint=phint、=、=、!=可以用来连接两个指针型变量做关系运算。指针间的关系运算结果就是两个指针所指的地址值的大小的关系运算结果。由于内存地址是线性编码的,所以,如果一个变量m的保存位置在变量n之前,则&m&n就等于0。两个进行关系运算的指针一般要求是同
12、一类型的指针。第6章 指针与字符串 4常指针(const修饰符与指针) 与#define语句相比较,C+提供了一种更灵活、更安全的方式来定义常量,即使用const修饰符来定义常量。例如: const int LIMIT=100; 它类似于#defineLIMIT100;语句,但此时这个常量是类型化的,它有自身的地址。 const也可以与指针一起使用。它们的组合情况较复杂,可简单归纳为三种:指向常量的指针、常指针和指向常量的常指针。第6章 指针与字符串 (1)指向常量的指针。指向常量的指针是指一个指向常量的指针变量。例如:const char*name=chen;/声明指向字符常量的指针变量 这
13、个语句的含义是:声明一个名为name的指针变量,它指向一个字符型常量,初始化name为指向字符串chen。 由于使用了const,不容许改变指针所指的常量,因此以下语句是错误的: name3=a;/即name所指的数据不可修改 但是,由于name是一个指向常量的普通指针变量,不是常指针,因此可以改变name的值。 第6章 指针与字符串 (2)常指针。常指针是指把指针本身,而不是它指向的对象声明为常量。例如: char*const name=chen; /常指针 这个语句的含义是:声明一个名为name的指针,该指针是指向字符型数据的常指针,用chen的地址初始化该常指针。创建一个常指针,就是创建
14、一个不能移动的固定指针,但是它所指的数据可以改变。例如: name3=a/合法 name=zhang;/出错 第一个语句改变了常指针所指的数据,这是允许的;但第二个语句要改变指针本身,这是不允许的。第6章 指针与字符串 (3)指向常量的常指针。指向常量的常指针是指这个指针本身不能改变,它所指向的数据值也不能改变。要声明一个指向常量的常指针,二者都要用const修饰。例如: const char*const name=chen;/指向常量的常指针 这个语句的含义是:声明了一个名为name的指针,它是一个指向字符型常量的常指针,用chen的地址初始化该指针。不难理解以下两个语句都是错误的: nam
15、e3=a;/出错,不能改变指针所指的值 name=zhang;/出错,不能改变指针本身第6章 指针与字符串 5引用型变量 引用型变量是从指针型变量发展来的一种特殊类型的变量。引用变量的实质仍然是指针,但采用了不同于指针的表示方法,可以简化对函数有多个返回值时的处理,使得程序更加清晰可读。 引用型变量是C+的新增特性,是一种不具备自己的实际数据存储空间的变量,它必须与另一个同类型的变量共用一个空间。换句话说,它只是另一个目标对象的别名,其本身不是一个独立的变量。因此,引用只有声明,没有定义。第6章 指针与字符串 【例6-2】引用的声明和使用例题。#includevoid main()int i,
16、j=4,&ri=i;/定义ri为整型变量i的引用i=3;couti=i,ri=riendl;couti的地址是&i,ri的地址是&riendl; 第6章 指针与字符串 ri+=5;couti=i,ri=riendl;couti的地址是&i,ri的地址是&riendl;ri=j;couti=i,ri=ri,j=jendl;couti的地址是&i,ri的地址是&ri,j的地址是&jendl;ri=&j;/出错第6章 指针与字符串 删除最后一句出错的语句后,程序的运行结果为i=3,ri=3i的地址是0 x0012FF7C,ri的地址是0 x0012FF7Ci=8,ri=8i的地址是0 x0012FF
17、7C,ri的地址是0 x0012FF7Ci=4,ri=4,j=4i的地址是0 x0012FF7C,ri的地址是0 x0012FF7C,j的地址是0 x0012DD78第6章 指针与字符串 这个例子清楚地表明:引用ri与它的目标变量i具有相同的内存地址,它们的值是同步变化的,即使引用ri被重新赋予变量j的值,ri的值发生改变(i的值也随之改变),但ri仍以i作为自己的目标,仍以i的地址作为自己的地址,这一点不会改变。 第6章 指针与字符串 6.3 指针与数组指针与数组 6.3.1 指针与数组的关系 数组的实质是内存中一块连续的空间,存储着同一类型变量的数据。要访问数组中的某个元素,可以通过“数组
18、名下标”这样的语法形式来实现。其中,下标是要被访问的元素在数组中的位置。由此,不难得出这样的结论:如果知道数组第1个(下标为0)元素的地址,将这个地址保存在某个指针变量中,那么对数组元素的访问就完全可以转换为指针操作。 第6章 指针与字符串 例如,已知指针型变量int* pnPointer中保存了数组int pnArray10的第1个元素的地址,则通过对指针变量pnPointer加上一个整数,就可以访问数组pnArray中的任何一个元素,即,pnArrayn与*(pnPointer+n)是等同的。第6章 指针与字符串 实际上,C+语言就是这样对数组元素进行访问的。在声明一个数组时,C+语言将在
19、内存中开辟两个空间,一个用于保存数组元素,另一个用于保存数组的第1个元素的地址。数组名就是用于保存数组第1个元素的地址的指针型常量。通过保存数组第1个元素地址的数组名这个指针型常量,可以使用“”运算符访问数组元素,也可以直接使用指针的运算规则访问数组元素。 第6章 指针与字符串 int pnArray10;int nValue;nValue=*pnArray;/nValue等于prlArray的第1个元素的值nValue=pnArray5; /nValue等于pnArray的第6个元素的值nValue=*(pnArray+5);/与上一句nValue=pnArray5;等价同样,对于指针型变量
20、也可以使用“”运算符,将其作为数组来使用。如有int*pnPointer=pnArray;/数组名pnArray本身是指针型,因此不必用取地址运算符&第6章 指针与字符串 则以下三条语句等价:nValue=pnArray5;nValue=pnPointer5;nValue=*(pnPointer+5);尽管指针与数组之间有着密切的联系,但也有区别。 第6章 指针与字符串 指针型变量是变量,是可以不断赋值的;而数组名虽然是指针,但它是一指针型常量,只能指向固定的内存地址,不能将一个指针值赋予一个数组名。以下语句在C+语言中是错误的: char cArray10; char ch; cArray=
21、&ch; /错误,不允许将一个指针值赋予数组名第6章 指针与字符串 6.3.2 通过指针引用数组元素 由于指针同数组的特殊关系,我们已经看到,数组与指针几乎可以互换使用。下面利用一个具体例子来说明通过指针引用数组元素的方法。 【例6-3】用指针的方法编写求一个数组中所有元素之和的程序。 分析:此函数的参数可以写成指针型。由于数组名的实质就是指针,在调用该函数时,只要将数组名作为实际参数传递给函数即可。程序代码如下: 第6章 指针与字符串 #includeint Sum(int*pPointer,int n)int nSum=0;while(n0)nSum+=*pPointer;pPointer
22、+;n-;第6章 指针与字符串 return nSum;void main()int pnArray10=6,7,8,9,5,4,3,2,10,1;int sumArray;sumArray=Sum(pnArray,10);cout数组各元素和:sum1=sumArrayendl;程序运行结果为数组各元素之和:sum1=55第6章 指针与字符串 【例6-4】对上例6-3,用指针的方法编写将数组各元素进行排序的函数程序。分析:参照例3-5,用选择法进行排序(降序)。#includevoid Sum(int*pPointer,int n)int i,j,t,p;for(i=0;in-1;i+)p=
23、i;for(j=i+1;jn;j+)第6章 指针与字符串 if(*(pPointer+p)=*(pPointer+j)p=j;t=*(pPointer+i);*(pPointer+i)=*(pPointer+p);*(pPointer+p)=t;void main()第6章 指针与字符串 int pnArray10=6,7,8,9,5,4,3,2,10,1;int i;Sum(pnArray,10);cout排序后的数组为:endl;for(i=0;i10;i+)coutpnArrayiendl;程序运行结果为排序后的数组如下:10,9,8,7,6,5,4,3,2,1第6章 指针与字符串 6.
24、3.3 指向多维数组的指针 多维数组的元素在计算机中同一维数组的元素一样,是线性存储的。只要知道保存某个多维数组的内存区域的首地址,就可以通过指针访问多维数组中的任何一个元素。 以二维数组int fMatrix34为例。它的首地址应该是&fMatrix00,如果将这个地址值赋予指针型变量int*pPointer,则二维数组fMatrix中的任何一个元素使用 p P o i n t e r 这 个 指 针 来 访 问 的 规 则 是 :*(pPointer+m*4+n)等同于fMatrixmn。其中,4是二维数组的列长度。第6章 指针与字符串 这是因为对于多维数组,C+语言采用按行顺序保存的规则
25、(列标优先变化)。对二维数组fMatrix而言,在保存fMatrix的存储区中排在前面的是第1行fMatrix00到fMatrix03这4个元素,然后是第2行fMatrix10到fMatrix13这4个元素,最后是第3行fMatrix20到fMatrix23这4个元素。 多维数组的数组名同样是个指针,但它不指向多维数组的最前一个被保存的元素的地址(&fMatrix00),它指向的是存放下一维元素的首地址指针所在的地址。对于初学者来说,这很难理解。仍以上述中的fMatrix数组来说明这个问题。第6章 指针与字符串 对二维数组fMatrix34中关于一维数组名fMatrixm(0m3)所代表的含义
26、,我们已在第3章多维数组中进行过讨论。如果把fMatrixm当成一个一维数组的数组名,则将fMatrixm加上下标,就可以访问一维数组fMatrixm中的各元素了,即将二维数组fMatrix看成了3个一维数组。不难发现,这是一个保存了3个指向一维数组的指针的数组。也就是说,此时一维数组fMatrix中数组元素的数据类型是指针类型,其数组元素fMatrixm保存的是下一维数组的第1个元素的首地址(&fMatrixm0)。 第6章 指针与字符串 于是,就有fMatrix0等于&fMatrix00fMatrix1等于&fMatrix10fMatrix2等于&fMatrix20最后,再来看fMatri
27、x的含义。 第6章 指针与字符串 【例6-5】用指针的方法将二维数组的转置(行列互换)形式输出,并输出相应的指针值,进行观察、比较。 #include void main() int aMatrix34=1,2,3,4,5,6,7,8,9,10,11,12; int i,j,*p; cout转置前的矩阵:endl; for(i=0;i3;i+) 第6章 指针与字符串 p=aMatrixi;/p存放各行的起始地址for(j=0;j4;j+)cout*(p+j);coutendl;cout转置后的矩阵:endl;for(j=0;j4;j+)for(i=0;i3;i+)p=aMatrixi;/p存放
28、各行的起始地址(注意所在的位置) 第6章 指针与字符串 cout*(p+j);coutendl;cout输出相应的指针值:endl;coutaMatrixaMatrix0&aMatrix00endl;for(i=0;i3;i+)coutaMatrixi&aMatrixi0endl;程序运行结果为第6章 指针与字符串 转置前的矩阵:123456789101112转置后的矩阵:159261037114812第6章 指针与字符串 输出相应的指针值(以下地址值根据系统运行情况而定): 0 x0012FF500 x0012FF500 x0012FF50 0 x0012FF500 x0012FF50 0
29、x0012FF600 x0012FF60 0 x0012FF700 x0012FF70第6章 指针与字符串 6.3.4 指针数组与多级指针 1指针数组 同一般数组一样,多个类型相同的指针变量也可以依数组的形式进行表示。声明指针型变量数组的形式同声明普通数组一样,只不过类型是指针类型。声明指针型变量的形式 如下: *指针数组名数组长度第6章 指针与字符串 例如: int*ppArray5;/声明一个保存5个int*型元素的指针数组 char*ppString6;/声明一个保存6个char*型元素的指针数组 指针型变量的数组中每个元素都是指针,同普通数组一样,这些指针按顺序保存在内存的某个空间中,
30、数组名就是指向这个空间首地址的指针。 由于指针数组中每个元素都是指针,那么从指针数组中取出的元素就具有指针的全部性质。例如,*(ppString0)代表数组元素ppString0所指向的单元(保存的地址值)。第6章 指针与字符串 2多级指针 多级指针一般又称为指针的指针。 如前所述,指针变量的内容是某种数据类型的地址,当然也包括指针类型的地址。一个指针型变量保存的是另一个指针型变量的地址,则这个指针型变量就是指针的指针。指针的指针类型的表示法是在原先的指针类型后再加一个“*”号。由于多级指针比较复杂,在此我们只以二级指针为例进行介绍。第6章 指针与字符串 定义二级指针变量的一般形式如下: *二
31、级指针变量名 当一个指针变量(如pp)的内容是另一个指针变量(如p是一个指向非指针类型的基本数据类型,称为一级指针)的地址(称为pp指向p)时,pp就是二级指针变量,或称为指向指针的指针变量。图6-4说明了一级指针变量p与二级指针变量pp之间的区别和联系。 第6章 指针与字符串 第6章 指针与字符串 显然,如果我们希望通过二级指针变量pp来访问整型变量i,必须经过两次间接寻址。先由pp取出它所指的内容(p的地址),即*pp,再由*pp(即i的地址)取出变量i的值,即*pp,这里要两次使用取内容运算符“*”。所以,i的值也可有*pp与*p两种形式表示。 另外,由此可进一步理解二维数组名是一个二级
32、指针的含义,实质是指向一维指针数组第1个元素(下标为0)的指针型常量。第6章 指针与字符串 例如,对上例6-5,若定义二级指针变量 int *pp; 则pp=aMatrix;是合法的,且*pp、*aMatrix、*aMatrix0都可表示元素aMatrix00 的值。 指针的指针对初学者来说是个不易接受的概念,但只要明确以下两点,所有的问题都会迎刃而解: 指针的指针仍然是变量。 指针的指针仍然是指针型变量,只不过保存的地址指向的内存单元中保存的还是一个指针。 第6章 指针与字符串 6.4 指针与函数指针与函数 6.4.1 指针变量作为函数参数 指针类型也可以作为函数的参数。通过指针型参数,可以
33、将一个变量的地址传递给函数,而通过这个地址,函数体中的语句就可以修改函数以外的变量的值。为了使大家能够对普通变量作为函数参数与指针变量作为函数参数深入理解,下面我们以这两种不同的参数传递方式举例,以进行对比说明。第6章 指针与字符串 (1)数值传递。所谓数值传递,即普通变量作为函数参数。主调函数中的实参内容是一个数值,其特点是,形参改变,实参不变。 【例6-6】通过函数调用交换两个变量的值。 #include void Swap(int,int);/函数声明语句 void main() 第6章 指针与字符串 int a=5,b=10;cout函数调用前:a=a,b=bendl;Swap(a,b
34、);/以a、b的值为实参调用函数cout函数调用后:a=a,b=bendl;void Swap(int m,int n)int temp;temp=m;m=n;n=temp;第6章 指针与字符串 cout函数中参数:m=m,n=nendl;程序运行结果为函数调用前:a=5,b=10函数中参数:m=10,b=5函数调用后:a=5,b=10第6章 指针与字符串 说明:由程序的运行结果可以看到,函数调用后变量a与b的值并未实现交换。其原因是,在调用函数时采用数值传递的方式,只是将实参a和b的值传送给了形参m和n,如图6-5所示;而由于实参a和b与形参m和n占用不同的内存单元,即使被调函数中的m和n进
35、行了交换,也无法将交换后的结果传回给实参a和b,函数调用结束后,形参m和n被撤消,实参a和b的值仍然保持原值不变。 第6章 指针与字符串 第6章 指针与字符串 (2)地址传递。所谓地址传递,即指针变量作为函数参数。主调函数中实参的内容是某一变量的内存地址,被调函数的形参必须是与实参相同类型的指针变量,用以接受由实参传过来的地址。这样实参与形参指向相同的内存单元,一旦被调函数中形参所指内容发生变化,意味着实参所指内容也发生相应的变化,函数返回后主调函数就可获得变化后的结果。第6章 指针与字符串 【例6-7】用传递地址的方法改写上例6-6,我们就能实现变量a和b的内容交换,并对照图6-6分析改进后
36、程序的运行结果。#includevoid Swap(int*,int*);/函数声明语句,参数为指针类型void main()int a=5,b=10;cout函数调用前:a=a,b=bendl;第6章 指针与字符串 Swap(&a,&b);/以a、b的地址为实参调用函数cout函数调用后:a=a,b=bendl;void changFunction(int*m,int*n)int temp;/互换用的变量temp=*m;*m=*n;*n=temp;cout函数中参数:*m=*m,*n=*nendl;第6章 指针与字符串 程序运行结果为函数调用前:a=5,b=10函数中参数:*m=10,*n=
37、5函数调用后:a=10,b=5第6章 指针与字符串 第6章 指针与字符串 由上例我们不难总结出指针变量作为函数参数的特点是,形参所指内容改变,实参所指内容也相应改变。为此编程时要注意两点:第一,实参必须是欲改变内容的变量的地址,形参必须是与实参类型相同的指针变量,用以接受实参传来的地址;第二,在被调函数中直接通过形参指针变量修改它所指内存单元的内容。第6章 指针与字符串 【例6-8】编写一个复数加法函数。 分析:复数的加法要求对参与运算的两个复数的实部和虚部分别进行加法运算,运算的结果也有实部和虚部两个部分。如果只利用函数的返回值,不能有效地表示一个复数的实部和虚部两个部分。所以采用指针操作,
38、通过指针将结果值赋予外部调用函数的变量。 函数程序代码如下:第6章 指针与字符串 void ComplexPlus(float fRealpartl, float fVirtualpartl, float fRealpart2, float fVirtualpart2, float*pfRealR, float*pfVirtualR)*pfRealR=fRealpartl+fRealpart2;*pfVirtualR=fVirtualpartl+fVirtualpart2;第6章 指针与字符串 6.4.2 指向函数的指针 1指向函数指针变量的定义与使用 在C+语言中,函数的调用实际上是通过指针
39、来完成的。同数据一样,程序也是保存在内存中的。CPU在执行程序时,首先需要找到程序的入口(开始)地址,然后从内存中取出指令并逐条执行。显然,同程序一样,函数的指针是一个函数在内存中的入口地址。在调用一个函数时,系统从当前的位置跳到该函数的入口地址处执行指令;函数返回时,系统回到刚才调用函数的那条指令的位置继续执行程序。第6章 指针与字符串 由此可见,要调用一个函数,只需要知道被调用函数的入口地址,即函数的指针即可。函数名实际上就是一个指针,它指向函数的入口。我们可以定义一个指针变量,并赋予它函数名,这样该指针变量的内容就是该函数在内存的入口地址。这样的指针变量称为指向函数的指针变量,简称函数指
40、针变量。由此,我们就可以通过函数指针变量来调用它所指向的函数。 第6章 指针与字符串 函数的指针还可以声明成变量。声明一个函数指针变量的形式如下: (*函数指针变量名)(参数表) 注意,定义中的两对圆括号都不要遗漏,数据类型是函数指针变量所指函数的返回值的数据类型。第6章 指针与字符串 例如,若定义函数 void FunctionA(int m,int n); 则对应于函数FunctionA的指针型变量可以按如下方法声明: void(*pFPointer)(int,int); 要用已经定义的函数指针变量来调用函数,需要分两步进行。 第一步:将函数名赋给已定义的函数指针变量。采用的格式为函数指针
41、变量名=函数名第6章 指针与字符串 例如,可以将FunctionA的入口地址值赋予pFPointer这个函数指针变量: pFpointer=FunctionA; 第二步:使用函数指针变量调用它所指的函数。采用的格式为 (*函数指针变量名)(实参表)或函数指针变量名(实参表) 事实上,由于pFPointer和FunctionA指向同一函数入口,除了可以使用函数名FunctionA调用函数外,当然可以使用函数指针pFPointer调用函数。例如: FunctionA(3,4) 等价于 (*pFPointer)(3,4);或pFPointer(3,4); 第6章 指针与字符串 2函数指针变量作为函数
42、的参数 函数指针变量的一个主要用途是在函数调用时把函数指针变量作为参数,也就是说传递的不是数值,而是函数的入口地址,目的是实现对若干个不同函数的灵活调用。具体编程时要注意的是:主调函数的实参应设置成将被调用的函数名,被调函数的相应形参应设置成接受同类型函数入口地址的函数指针变量。第6章 指针与字符串 【例6-9】将函数指针变量作为函数参数例题。#includeint add(int x,int y)return x+y;int sub(int x,int y)return x-y;int funct(int(*fun)(int,int),int x,int y)第6章 指针与字符串 int r
43、esult;result=(*fun)(x,y);/等价于result=fun(x,y);return result;void main()int a=5,b=10;cinab;couta+b=funct(add,a,b)endl;couta-b=funct(sub,a,b)endl;第6章 指针与字符串 程序运行结果为输入:323+2=53-2=1第6章 指针与字符串 6.4.3指针作为函数的返回类型 如前所述,函数的数据类型是指函数返回值的类型,它可以是整型、字符型、浮点型或void(无返回值),也可以是指针类型,即函数的返回值是地址。返回值是地址的函数称为指针函数,其定义的一般形式如下:
44、 *函数名(形参表) 函数体 第6章 指针与字符串 例如: int*ip; int*fFun1(int x) . return ip; 其中fFun1是函数名,前面的“*”表示fFun1是一个指针函数(注意与函数指针的区别),它的返回值是一个int类型的地址。与此相匹配的是,在函数体内return语句中的ip也必须是指向int型的指针变量。 第6章 指针与字符串 【例6-10】函数的返回值是指针类型例题。 char*PointerCheck(char*pBuffer) if(*pBuffer=A)return NULL;elsereturn pBuffer;/pBuffer不是函数内部的局部变
45、量 第6章 指针与字符串 6.5 指针与类、对象指针与类、对象 1. 类的指针变量 类的指针变量是一个用于保存该类对象在内存中存储空间首地址的指针型变量,同普通数据类型的指针变量有相同的性质。 声明一个类的指针变量的语法如下: *指针变量名;第6章 指针与字符串 2对象的指针 对象的指针指的是一个对象在内存中的首地址。取得一个对象在内存中首地址的方法同取得变量在内存中首地址的方法一样,都是通过取地址运算符“&”。例如,若有 CStudent*pStu,Stu1; 则 pStu=&Stu1; 表示表达式&Stu1是取得对象Stu1内存中的首地址,其值的类型是CStudent*,同类指针pStu就
46、指向对象Stu1在内存中的首地址。第6章 指针与字符串 同样,已知一个对象的指针,要访问该对象,仍然可以使用指向运算符“*”。例如:*(pStu)就同Stu1等价。 如果已知一个对象的指针,要访问该对象中的成员,其方法同结构体变量的指针是相似的,既可以使用“*”运算符先取得该对象的实际“值”,然后再使用成员运算符“.”访问数据成员和成员函数,也可以使用“-”运算符直接访问该指针所指向的对象的数据成员和成员函数。 第6章 指针与字符串 例如,若有CStudentStu1;Cstudent*pStu=&Stu1;则*pStu.SetID;/其中SetID是类CStudent的数据成员pStu-Se
47、tDispl(张建华);/其中SetDispl是类CStudent的成员函数都是合法的表达式。 第6章 指针与字符串 3this指针 this指针是每个对象中隐藏的指针。this指针是默认的。当一个对象生成后,这个对象的this指针就指向内存中保存该对象数据的存储空间的首地址。 在类的成员函数中使用这个this指针,就好像this指针是类的一个自动隐藏的私有成员一样。this指针可以形象地用如下定义来说明:第6章 指针与字符串 classMyClassprivate:MyClass*this;.public:MyClass();.;第6章 指针与字符串 实际上不必像上面那样定义this指针。t
48、his指针对一个对象来说是系统自动生成的,主要用于在成员函数中需要把对象本身作为参数传递给另一个函数。我们通过以下例子来说明this指针的作用。第6章 指针与字符串 【例6-11】this指针的作用例题。#includeclassThisSampleint n;public:ThisSample();ThisSample(int m)n=m;第6章 指针与字符串 void addvalue(int m)/定义一个非静态成员函数ThisSample q;q.n=n+m;/此处n相当于this-n*this=q;/将临时对象q的值传回调用对象;void disp()coutn=nendl;第6章
49、指针与字符串 void main()ThisSamples(10);s.disp();s.addvalue(5);/相当于函数调用:addvalue(&s,5);s.disp();程序运行结果为n=10n=15第6章 指针与字符串 6.6 指针与字符串指针与字符串 6.6.1 字符串指针 第3章我们已经介绍过,在C+语言中,字符串是以“0”为结束标志的字符序列,是以字符数组的形式进行存储与处理的,而数组与指针又紧密相连(数组名就保存着字符串在内存的起始地址),因此,字符串实质上与char*型指针相对应。第6章 指针与字符串 等价于下面两行:char*cString;cString=IloveC
50、hina!;由此总结出有关char*型指针的两个结论如下:char*型指针变量可以在定义时进行初始化,其形式为char*指针变量名=字符串;例如:char*myString=Thisisastring.;这样就可以用myString这个指针来访问字符串“Thisisastring.”了。第6章 指针与字符串 char*型的指针变量(或函数参数)既可以用于接收字符串常量,也可以接收字符型数组。例如: char pString=IloveChina!; char*myString=Thisisastring.; myString=pString; 最后,我们以第3章字符串的复制为例,进一步对字符串