C++大学基础教程 第6章 指针和引用 北京科技大学 信息基础科学系
第六章 指针和引用 6.1 指针的概念 6.2 指针的运算 6.3 指针和函数(6.3.2和6.3.4不要求) 6.4 指针和字符串 6.5 通过指针访问数组(6.5.3和6.5.4不要求) 6.6 指针访问动态内存 6.7 引用概念
6.1 指针的概念
内存空间的访问 变量的两种属性 地址运算符:& 内存空间的访问方式 变量内容 变量地址 int var; var = 5; //表示变量var中存取的内容 &var //表示变量var在内存中的起始地址 内存空间的访问方式 通过变量名访问 通过地址访问
几个概念 指 针 static int *i_pointer=&i; 概念 指针:变量的地址,用于间接访问变量 指针变量:用于存放地址的变量 内存用户数据区 变量 i 变量 j 变量 i_pointer 3 6 2000 2004 3010 概念 指针:变量的地址,用于间接访问变量 指针变量:用于存放地址的变量 目标变量:指针变量存放的地址对应的变量 指 针 声明 例:static int i; static int *i_pointer=&i; 指向整型变量的指针 引用 例1: i=3; 例2: *i_pointer=3; 2000 3 i_pointer *i_pointer i
指针变量的定义和初始化 [存储类型] 数据类型 *指针名=初始地址; int *pa=&a; 语法形式 例: int a; 注意事项 [存储类型] 数据类型 *指针名=初始地址; 例: int a; int *pa=&a; 注意事项 用变量地址作为初值时,该变量必须在指针初始化之前已说明过,且变量类型应与指针类型一致。 可以用一个已赋初值的指针去初始化另一 个指针变量。 char ch1=’Y’, ch2=’A’; char *pch1=&ch1, *pch2=&ch2;
例1 指针的概念 #include <iostream.h> void main() { int a; int *pa=&a; 内存用户数据区 #include <iostream.h> void main() { int a; int *pa=&a; a=10; cout << " a: " << a << endl; cout << “ *pa: " << *pa << endl; cout << “ &a: " << &a << endl; cout << " pa: " << pa << endl; cout << "&pa: " << &pa<< endl; } pa 0012FF78 0012FF7C a 0012FF7C 10
6.2 指针的运算
6.2 指针的运算 表6.1 指针的运算
6.2.1 指针的赋值运算 指针的赋值运算一定是地址的赋值。用来对指针变量赋值的可以是: 同类型变量的地址; 同类型的已经初始化的指针变量; 指针变量名=地址 指针的赋值运算一定是地址的赋值。用来对指针变量赋值的可以是: 同类型变量的地址; 同类型的已经初始化的指针变量; 其他同类型的指针。 此外,也可以用0或者NULL对指针变量赋值。使得变量包含的是“空指针”,即不指向任何的内存物理地址。即,空指针是允许的。
指针的赋值运算 “地址”中存放的数据类型与指针类型必须相符。 必须注意:不同类型的指针是不可以互相赋值的。 在指针赋值时,不存在类型自动转换的机制。 向指针变量赋的值必须是地址常量或变量,不能 是普通整数。但可以赋值为整数0,表示空指针。
例6.1 观察以下指针赋值运算的结果。如果将注释去掉,结果将如何? #include <iostream> using namespace std; void main() {int va1=100,*pva1; float vf1=1.0,*pvf1,*pvf2; int *pva2=NULL; cout<<"value of pva2 is "<<pva2<<endl; pva1=&va1; pvf1=pvf2=&vf1; cout<<pva1<<" "<<&va1<<endl; cout<<pvf1<<" "<<pvf2<<endl; cout<<&pva1<<endl; cout<<&pvf1<<endl; cout<<&pvf2<<endl; //pvf1=pva1; } 内存用户数据区 pva2 0012FF68 pvf2 0012FF6C 0012FF74 pvf1 0012FF70 vf1 1.0 0012FF74 pva1 0012FF78 0012FF7C va1 100 0012FF7C
例 指针的定义、赋值与使用 #include<iostream.h> void main( ) { int *i_pointer; //声明int型指针i_pointer int i; //声明int型数i i_pointer=&i; //取i的地址赋给i_pointer i=10; //int型数赋初值 cout<<"Output int i="<<i<<endl; //输出int型数的值 cout<<"Output int pointer i="<<*i_pointer<<endl; //输出int型指针所指地址的内容 }
6.2.2 间接引用运算 间接引用运算符“*”是一种一元运算符,它和指针变量连用,对指针所指向的内存地址单元进行间接访问。使用的格式是: *指针变量 如果指针变量iptr指向整型变量va,*iptr就是变量va的内容
例6.2 对变量的直接访问和间接访问:写出以下程序运行结果。 #include <iostream> using namespace std; void main() {char ch1='a',*ch; int k1=100; ch=&ch1; //指针ch指向变量ch1 cout<<"*ch="<<*ch<<endl; //间接访问 *ch='B'; cout<<"ch1="<<ch1<<endl; //直接访问 ch1=k1; cout<<"*ch="<<*ch<<endl; //间接访问 } 运行结果: *ch=a ch1=B *ch=d
例6.3 定义指向指针的指针变量。观察对这种指针变量间接访问的结果。 #include <iostream> using namespace std; void main() { int va=100,*pva,**ppva; //ppva是指向指针的指针 int k1=100; pva=&va; cout<<"*pva="<<*pva<<endl; //间接访问结果是整型数 ppva=&pva; cout<<"*ppva="<<*ppva<<endl; //间接访问结果是地址 cout<<"pva="<<pva<<endl; //就是指针pva的内容 } 运行结果: *pva=100 *ppva=0x0012FF7C pva=0x0012FF7C
6.2.2 间接引用运算
指针p和整数n相加(相减)的含义是指向当前指向位置p的前方或后方第n个数据的地址。 6.2.3 指针的算术运算 指针可以进行的算术运算只有加法和减法。 指针可以和一个整数n做加法或者减法运算。 指针p和整数n相加(相减)的含义是指向当前指向位置p的前方或后方第n个数据的地址。
例6.4 通过指针的间接访问,输出下标为偶数的数组元素的值。 #include <iostream> using namespace std; void main() { int k1[10]={11,24,37,44,58,66,79,86,93,108},*k; k=&k1[0]; for(int i=0;i<10;i=i+2) cout<<"k1["<<i<<"]="<<*(k+i)<< " "; cout<<endl; } 指针常量的下标法k1[i] 指针变量的下标法k[i] 指针常量的指针法*(k1+i) 指针变量的指针法*(k+i)
6.2.3 指针的算术运算 指针和指针的直接加法是没有意义的,也是不允许的。 指针和指针的减法是可以进行的,其意义是求出两个指针之间可以存放几个指定类型的数据。 不允许用一个整数减一个指针。
6.2.4指针的关系运算和逻辑运算 相同类型的指针可以进行各种关系运算。比较两个指针相等还是不相等。 进行指针“大于”、“小于”的比较,只是要判定指针在内存中的相对位置。
6.2.4指针的关系运算和逻辑运算 指向不同数据类型的指针,指针和一般的整数比较是没有意义的,也是不允许的。 惟一可以和指针比较的整数是0。通过指针和0的比较来判定指针本身是不是空指针。即指针可以和零之间进行等于或不等于的关系运算。例如:p==0或p!=0 指针可以进行“逻辑非”运算
6.2.5 void类型指针 void类型的指针就是“无类型”指针。声明的方式如下: void *<指针名>;
void类型指针的使用: 任何其他类型的指针都可以赋值给void指针。必须注意,这样赋值后的void指针的类型仍然是void。
void类型指针的使用: 要通过void类型指针访问内存的数据,必须进行指针类型的强制转换,才可以通过指针间接引用访问内存数据。 某种类型的指针 void指针,进行具体操作后 强制转换
memcpy函数 函数原型: void *memcpy(void *dest,const void *src, size_t count);
复制字符数据,10个字节 复制整型数据,12个字节 例6.5 使用memcpy通用复制函数复制数组。 #include <iostream> using namespace std; #include <string.h> void main() { char src[10]="012345678"; char dest[10]; char* pc=(char*)memcpy(dest,src,10); cout <<pc <<endl; int s1[3]={1,2,3}; int d1[3]; int *pi=(int*)memcpy(d1,s1,12); cout<<*pi<<" "<<*(pi+1)<<" "<<*(pi+2)<<endl; } 复制字符数据,10个字节 复制整型数据,12个字节 运行结果: 012345678 1 2 3
6.2.5 void类型指针 void类型指针还有一个具体的应用:显示字符指针的内容。除了字符指针外,其他指针都可以直接用cout语句来输出地址值。但是,用cout输出字符指针时,则是输出它所指向的字符串。可以将字符指针强制转换为void指针,再用cout语句输出,就可以看到地址值。如: char *pch="Hello C++"; cout<<pch<<endl; cout<<(void*)pch<<endl; int a[3]={1,2,3}; cout << a << endl; cout << *a << endl; char c[]=“hello”; cout << c << endl; cout << *c << endl; cout << c+1 << endl; cout <<(void*) c << endl;
6.3 指针和函数
6.3 指针和函数 在程序设计中,指针有很多应用。其中之一就是用指针作为函数的参数,从而形成了C++函数调用中的另一种调用方式:地址调用。
6.3.1 指针作为函数的参数 用指针作为函数参数,实现地址调用,必须满足以下条件: 函数的形式参数是指针变量; 函数的实参数是内存的地址,具体来说可以是数组名、变量的地址、用变量地址初始化的指针; 形参指针类型和实参地址类型必须相同。
6.3.1 指针作为函数的参数 满足以上条件后,这样的函数调用在使用上有以下的特点: 实参传递给形参的是内存的地址,所以形参指针指向实参变量; 形参指针通过间接引用,直接访问实参变量,包括改变实参变量的值; 函数调用后,可以保留对实参变量的操作结果,如果有多个实参,就可以有多个实参变量在函数调用中得到修改。
变量的地址作为实参数 指针变量作为形式参数 例6.6 编写数据交换的函数。在main中调用这个函数,交换main中定义的变量。 #include<iostream> using namespace std; void Swap(int *a, int *b); void main() { int x(5), y(10); cout<<"主函数变量的值: x="<<x<<" y="<<y<<endl; Swap(&x,&y); cout<<"返回后变量的值: x="<<x<<" y="<<y<<endl; } void Swap(int *a, int *b) { int t; t=*a; *a=*b; *b=t; cout<<"函数中完成了交换:*a="<<*a<<" *b="<<*b<<endl; 变量的地址作为实参数 指针变量作为形式参数 运行结果: 主函数变量的值:x=5 y=10 函数中完成了交换:*a=10 *b=5 返回后变量的值: x=10 y=5
6.3.1 指针作为函数的参数 程序中用变量x和y的地址作实参,传递给指针a和b,如图6.1(a)。通过间接引用*a和*b进行交换,实际上就是x和y进行交换,如图6.1(b)。
例6.7 指针变量指向一个数组。用指针变量作为实参调用一个函数。在函数中指针指向数组的第二个元素。观察函数返回后,实参指针值有无变化。 #include<iostream> using namespace std; void Move(int *a); void main() { int x[5]={10,20,30,40,50}, *px=x; cout<<"调用前的*px="<<*px<<endl; Move(px); cout<<"调用后的px"; if(px==x)cout<<"没有变化,*px还是"<<*px<<endl; else cout<<"也向前移动,*px变为"<<*px<<endl; } void Move(int *a) { a=a+1; cout<<"函数中完成了指针移动:*a="<<*a<<endl; 指针作为实参数 指针变量作为形式参数 运行结果: 调用前的*px=10 函数中完成了指针移动:*a=20 调用后的px没有变化*px还是10
2009.05.12
6.3.3 传递参数的保护:指针和常量 通过数组名的地址调用,可以改变实参数组的内容。 但是,并不是所有以数组名作为实参的函数调用,都需要改变数组的值。例如,在调用一个求数组最大值的函数时,就不希望数组的值发生变化。希望在函数中能够限制对数组元素的修改。 使用常指针可以达到这个目的。
6.3.3 传递参数的保护:指针和常量 常指针是指向常量的指针(Pointer to Constant data)的习惯说法。就是规定指针所指向的内容不可以通过指针的间接引用来改变。 常指针说明的格式是: const <类型名> *<指针名>; 例如: const int *ptint; 指针ptint的类型是(const int *),也就是指向一个恒定的整型数。
例6.10 常指针示例。观察以下程序的运行。 常指针声明 注释去掉会出现编译错误 #include <iostream> using namespace std; void main() {int ia=10, ib=20; const int *ptint; ptint=&ia; //用ia地址初始化 cout<<*ptint<<endl; ptint=&ib; //改变为ib的地址 ib=ib+100; //ib本身仍然可以改变 //*ptint=100; //语句错误:左值是常量 } 常指针声明 注释去掉会出现编译错误 运行结果: 10 120
6.3.3 传递参数的保护:指针和常量 指针常量(Pointer constant)。 指针本身的内容是个常量,不可以改变。 指针常量声明的格式是: <类型名> *const <指针名>=<初值>; 例如: char ch, *const ptch=&ch; 数组名就是数组的首地址。现在可以说:数组名就是一个指针常量。
语句有错:常量不能当左值 语句有错,地址类型不同 例6.11 指针常量示例。指出以下程序的错误。 #include <iostream> using namespace std; void main() { int a=10, b=100; int *const pa=&a; //pa是指针常量 cout<<*pa<<endl; *pa=20; //指针常量的间接引用是允许的 cout<<a<<endl; pa=&b; const int c=50; // int *const pc=&c; } 语句有错:常量不能当左值 语句有错,地址类型不同 // 错误语句注释掉后运行结果: 10 20
例6.12 用常指针作形参,函数printString可以输出数组的内容,不可以对数组修改。 #include <iostream> using namespace std; void printString( const char * ); void main() { char phrase[] = "C++ is a modern programming language"; cout << "The string is:\n"; printString( phrase ); cout << endl; } // main函数结束 void printString( const char *Ptarray ) { while(*Ptarray) cout << *Ptarray++; } 数组名作实参数 常指针作形式参数 不使用常指针也是可以完成打印。但是没有保护了。
6.4 指针和字符串
6.4.1 字符串处理的两种方式 C++字符串常量是用双引号括起的字符序列,并以字符‘\0’作为结束标志。如 "This is a string"。 字符串常量存放在内存的某个区域,有自己固定的首地址。 如果将字符串常量的首地址看成是指针,这种指针既是常指针,也是指针常量。
6.4.1 字符串处理的两种方式 C++处理字符串有两种方式:数组方式和指针方式。 数组方式是将字符串存入字符数组后,再进行处理。一般可以在声明数组的时候用字符串来初始化: char string_array[]="a nice day!"; 指针方式是用字符串常量来初始化一个字符指针: char *string_pt="a nice day!";
6.4.1 字符串处理的两种方式 常量不能放在等式左边 运行时会出错
复制array1到array3,空间不够,有运行错误 按实际数组大小,复制array1到array3,没有问题 例6.14 strcpy和strncpy的比较。 #include <iostream> #include <string> using namespace std; 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'; /*strcpy(array3,array1); cout<<array3<<endl; */ n=sizeof(array3); strncpy( array3, array1, n-1 ); // 复制array1n-1个字符到array3 array3[ 14 ] = '\0'; // 添加'\0' 到array3 cout << "The string in array3 is: " << array3 << endl; } 复制array1到array2,没有问题 复制array1到array3,空间不够,有运行错误 不包括提示的运行结果 Happy Birthday to You Happy Birthday 按实际数组大小,复制array1到array3,没有问题
6.5 通过指针访问数组
6.5 通过指针访问数组 指针和数组有天然的联系。因为数组名本身就是地址,也就是某种类型的指针。将指针和数组名联系起来,访问数组就多了一种方法。 虽然一维数组名和二维数组名都是地址,都可以看作是某种指针,但是指针的类型是不同的。因此,通过指针访问一维数组和二维数组的方法是不同的。
6.5.1 通过指针访问一维数组 要通过指针访问一维数组,必须首先声明一个和数组类型相同的指针,并且用数组名来对指针初始化,如: int A[10], *pa=A; 然后,就可以用多种方式访问数组元素: 数组名和下标,如A[0]、A[4]; 指针和下标,如pa[0]、pa[4]; 指针加偏移量的间接引用,如*(pa+4); 数组名加偏移量的间接引用,如*(A+4); 指针自加后的间接引用,如*pa++。
例6.15 求数组内所存放的字符串的长度。 #include <iostream> using namespace std; void main() { char ChArray[]="This is a string.",*ptch; int i,j,k,offset1,offset2; ptch=ChArray; //指针初始化 for(i=0;ChArray[i]!='\0';i++); cout<<"The length of the string is:"<<i<<endl; for(j=0;ptch[j]!='\0';j++); cout<<"The length of the string is:"<<j<<endl; for(offset1=0;*(ChArray+offset1)!='\0';offset1++); cout<<"The length of the string is:"<<offset1<<endl; for(offset2=0;*(ptch+offset2)!='\0';offset2++); cout<<"The length of the string is:"<<offset2<<endl; for(k=0;*ptch++!='\0';k++); cout<<"The length of the string is:"<<k<<endl; } 方式1:数组名和下标 方式2:指针和下标 方式3: 数组名加偏移量的间接引用 方式4:指针加偏移量的间接引用 方式5:指针自加的间接引用 运行结果 都相同
例6.16 求整型数组的平均值,显示数组元素和平均值。 #include <iostream> using namespace std; void main() { int intArray[10]={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;i<num;i++) sum=sum+*ptint++; average=(float)sum/num; cout<<"数组元素是:\n"; cout<<*ptint++<<" "; cout<<"\n平均值是:"<<average<<endl; } 指针初始化 求数组元素的数目 求平均值 指针再次初始化 输出数组元素和它们的平均值 数组元素是: 8 11 23 34 45 56 65 78 86 97 平均值是:50.3
6.5.2 通过指针访问二维数组 二维数组可以看成是一维数组的一维数组。二维数组名虽然也是地址(指针),但是却和一维数组名有不同的类型。 对一维数组A[5],数组名A的地址,就是数组第一个元素A[0]的地址。指针的类型是指向数组元素的指针。A+1就是元素A[1]的地址。
6.5.2 通过指针访问二维数组 对二维数组B[3][4],数组名B的地址,则是其中第一个一维数组B[0]的地址。指针的类型是指向一维数组的指针。B+1就是下一个一维数组B[1]的地址。如图6.3所示。
6.5.2 通过指针访问二维数组 在定义指向一维数组的指针时,还必须指出一维数组的大小。 声明指向一维数组的指针的格式如下: <类型名> (*指针变量名)[一维数组大小]; 例如,和图6.3中两个二维数组所对应的指向一维数组的指针定义如下: char (*ptchb)[4], (*ptchc)[2]; ptchb=B; ptchc=C;
6.5.2 通过指针访问二维数组 对于指向一维数组的指针,具有以下的特征: 二维数组名是指向一维数组的指针,而不是指向数组元素的指针。 指向一维数组指针加1 的结果,是指向下一个一维数组的指针。 指向一维数组的指针的间接引用的结果仍然是地址,即*ptchb仍然是地址。只是地址的类型变了。变为一维数组B[0]第一个元素B[0][0]的地址。 因为*ptchb是数组元素的地址,**ptchb就是数组元素的值。用指向一维数组指针访问二维数组第i行第j列元素的一般公式是*(*(指针名+i)+j)。
例6.17 比较指向一维数组的指针和指向数组元素的指针。 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; } B的第0行地址 B的第0行第0列 元素的地址 B的第0行第1列 元素的地址 不同类型的指针 ptshb的地址是: 0x0012FF68 *ptshb的地址是: 0x0012FF68 *ptshb+1的地址是: 0x0012FF6A B[0][1]的地址是: 0x0012FF6A *ptshb+1和&B[0][1]相等吗?Yes B的第0行第1列 元素的地址
求数组元素的数目,**dArray就是元素dArray[0][0] 例6.18 用单循环程序,求二维数组元素的平均值。 #include <iostream> using namespace std; void main() {int dArray[3][4]={32,42,12,25,56,76,46,53,76,89,96,82},(*pt)[4]; int sum, j; float average; sum=0; pt=dArray; j=sizeof (dArray)/sizeof( **dArray); for(int i=0;i<j;i++) sum=sum+*(*pt+i); average=(float)sum/j; cout<<"数据的平均值等于:"<<average<<endl; } 指向一维数组指针的初始化 求数组元素的数目,**dArray就是元素dArray[0][0] 数组求和 求平均值 输出平均值 运行结果: 数据的平均值等于57.0833
6.6 指针访问动态内存
6.6 指针访问动态内存 动态内存是在程序执行时才可以申请、使用和释放的内存。也就是存放动态数据的内存区域。存放动态数据的区域称为“堆”,动态内存也称为堆内存。 动态内存不能通过变量名来使用,而只能通过指针来使用。
6.6.1 动态内存的申请和释放 C++中通过运算符new申请动态内存,运算符delete释放动态内存。 运算的结果:如果申请成功,返回指定类型内存的地址;如果申请失败,返回NULL指针。 int *pi; pi=new int(10); 动态内存使用完毕后,要用delete运算来释放。delete运算符使用格式: delete <指针名>;
6.6.2 动态数组空间的申请和释放 申请动态一维数组时,要在new表达式中加上申请数组的大小: 释放动态数组空间都用相同的表达式: 注意:在动态申请数组空间时,不可以对数组进行初始化。 申请一个动态的整型数组: int *piarray; piarray=new int[10]; 也可以申请二维数组的空间: int (*pi_marray)[4]; pi_marray = new int[3][4]; 释放动态数组空间都用相同的表达式: delete []<指针名>;
6.6.3 内存泄漏和指针悬挂 内存泄漏是指动态申请的内存空间,没有正常释放,但是也不能继续使用的情况。如: char *ch1; ch1 = new char('A'); char *ch2 = new char; ch1=ch2; 原来为ch1所申请的存放字符A的空间就不可能再使用了,产生了内存泄漏。
6.6.3 内存泄漏和指针悬挂 让指针指向一个已经释放的空间,即所谓的指针悬挂(Dangling)。如: char *ch1, *ch2; ch1 = new char; ch2 = ch1; *ch2 = 'B'; delete ch1; 指针ch2就是指向了一个已经释放的地址空间,形成指针悬挂。如果还要用delete ch2;语句来释放ch2所指向的空间,就会出现运行错误。
6.7 引用概念
6.7 引用概念 引用(Reference)是C++中新引入的概念,也是C语言中不存在的数据类型。 引用是变量或者其他编程实体(如对象)的别名。因此,引用是不可以单独定义的。如图6.4(a)所示,变量A在内存中有自己的地址,而A的引用B实际上就是变量A,只是A的另外一个名字。
6.7.1 引用的声明和使用 引用是通过运算符&来定义的,定义的格式如下: <类型名> &引用名 = 变量名; 其中的变量名必须是已经定义的,并且和引用的类型必须相同。例如: int someInt; int &refInt = someInt; 必须注意:引用必须在声明的时候就完成初始化,不可以先声明引用,然后再用另一个语句对它初始化。
6.7.1 引用的声明和使用 引用有以下的特点: 引用不能独立存在,它只是其他变量的别名; 引用必须在声明的同时就初始化; 引用一旦定义,引用关系就不可以更改,即B若是A的引用,就不可能是其他变量的引用; 引用的类型就是相关的变量的类型,引用的使用和变量的使用相同。
#include <iostream> using namespace std; void main() 例6.22 引用的使用。观察以下程序的结果。 #include <iostream> using namespace std; void main() { int intA=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的地址=0x0012FF7C
6.7.1 常引用 如果不希望通过引用来改变相关的变量的值,则可以定义常引用。常引用定义的格式: const <类型名> &引用名=变量名; 例如: int someInt = 10; const int &const_refA = someInt; 例如: const_refA = 50; 此时,const_refA就是常引用。不可以通过const_refA来改变someInt变量的值。
#include <iostream> using namespace std; void main() 例6.23 指针的引用。 #include <iostream> using namespace std; void main() { int intA=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=20
例6.24 用引用作为形式参数,通过函数调用,交换两个实参数。 #include <iostream> using namespace std; void swap_1(int &x, int &y) //引用作为形式参数 { int j; j=x; x=y; y=j; } void main() { int a=12345, b=54321; cout<< " 函数调用前:a= " <<a<< " b="<<b<<endl; swap_1(a, b); //变量作为实参数 cout<< " 函数调用后:a= " <<a<< " b="<<b<<endl; 函数调用前:a= 12345 b=54321 函数调用后:a= 54321 b=12345
6.7.2 通过引用传递函数参数 引用使用最多的场合是作为函数的形式参数。 引用作为函数的形式参数具有以下的特点: 引用作为形式参数时,实参数是相同类型的变量; 引用作为形式参数时,在函数中并不产生实参数的副本,形式参数的引用和实参数的变量实际上是同一个实体; 函数对引用的操作,也是对实参变量的操作,函数调用可以改变实参数的值。
6.7.2 通过引用传递函数参数 使用引用作为形式参数还需要注意: 如果实参数需要保护,可以使用“常引用”作为形式参数; 用引用作形参和用变量作形参是有区别的,但是,对于这两种情况,实参数可能相同。例如,函数swap(int a, int b)和swap(int &a, int&b)看起来是两个可以区分的重载函数。但是,都可以用整型变量x和y来调用:swap(x, y),因此,实际上是不可区分的,函数swap(int a, int b)和swap(int &a, int&b)不是可以区分的重载函数。
例6.25 引用作为函数返回值。 返回引用的值 返回引用的地址 程序执行后显示: 20 输出1:pb=20 30 输出2:pc=30 40 #include <iostream> using namespace std; int &fun(int &pf); void main() {int pa=10,pb; cout<<"输出1:pb="<<(pb=fun(pa))<<endl; int &pc=fun(pa); cout<<"输出2:pc="<<pc<<endl; cout<<"输出3:"<<++fun(pa)<<endl; pc=100; cout<<"pc="<<pc<<","<<"pa="<<pa<<endl; pb=200; cout<<"pb="<<pb<<","<<"pa="<<pa<<endl; } int &fun(int &pf) {pf=pf+10; cout<<pf<<endl; return pf; 返回引用的值 返回引用的地址 程序执行后显示: 20 输出1:pb=20 30 输出2:pc=30 40 输出3:41 pc=100,pa=100 pb=200,pa=100
6.7.3 用引用作为函数的返回值 返回引用有以下需要注意的地方: 返回引用需要在函数的返回值类型中加以说明,形式为: <类型名> &函数名(形式参数表) 返回引用的返回语句就是:return 变量名; 返回引用实际是返回地址。在使用上,或者直接使用这个地址;或者使用这个地址单元的数据。 返回的引用可以作为左值继续操作,而返回的变量值是不可以继续运算的。这是返回引用和返回变量值在使用上的主要区别。
总结 指针变量的特点是可变性,即一个指针变量内的地址是可变的。所以,通过一个指针变量,就可以访问一个数组。而引用的特点是不变性,一个变量的引用就只能和这个变量联系在一起。彼此随着对方的变化而变化。本章还介绍了函数调用的另一种方式:地址调用。具体又分为指针调用和引用调用。动态内存的使用也是本章的重点之一。
作业及实验: 第6章习题: 1,2,4,6,7,11
习题6.4 #include <iostream> #include<string> using namespace std ; void main() { char *s1="String01"; char *s2="String02"; char s3[]="String03"; char s4[]="String04"; //strcpy(s1,s2); cout<<s1<<endl; strcpy(s3,s4); cout<<s3<<endl; strcpy(s3,s2); //strcpy(s1,s4); } 习题6.4
习题6.6 #include <iostream> #include<string> using namespace std ; void main() { char *pch; pch=new char; //strcpy(pch,"Book"); cout<<pch<<endl; delete pch; }
习题6.11 #include<iostream> using namespace std; void Move(int *&a); void main() { int x[5]={10,20,30,40,50}, *px=x; cout<<"调用前的*px="<<*px<<endl; Move(px); cout<<"调用后的px"; if(px==x)cout<<"没有变化,*px还是"<<*px<<endl; else cout<<"也向前移动,*px变为"<<*px<<endl; } void Move(int *&a) { a=a+1; cout<<"函数中完成了指针移动:*a="<<*a<<endl;