5.5 指针类型——内存地址的描述
在C++中,用指针类型来描述内存地址,通过指针操作来实现与内存地址有关的程序功能
5.5.1 指针类型的定义
- 指针类型(pointer type)是一种用户自定义的数据类型,值集是由一些指针构成的集合,是内存地址的抽象表示。
- 指针与无符号整数的联系与区别:
- 形式:指针属于无符号整数
- 概念:
- 一个指针对应某个内存单元,关联到某个变量或函数;一个无符号整数代表一个数量或序数,不关联到其他程序实体
- 对无符号整数所能实施的运算,有些运算对指针没意义(乘除)
- 对于一个内存地址所属的指针类型由该内存地址所代表的内存单元中存储的程序实体的类型决定,因此内存地址不宜用无符号整数类型来表示
- 定义指针时,必须指出该指针指向何种类型的程序实体。指向数据的指针类型通常与该类型的指针变量同时定义,格式为:
- <类型> * <指针变量>;
- <类型>:为指针所指向的数据的类型
- 一个指针变量所指向的数据类型可以是void,表明该指针可指向任意类型的数据,使用该类型的指针时,要对其进行显示转换为某个具体类型的指针
- *:表示定义的是指针变量,区分于普通变量的定义
- <指针变量>:所定义的指针类型的变量的名字,用标识符表示
- <类型>:为指针所指向的数据的类型
- <类型> * <指针变量>;
- 指针变量拥有自己的内存空间,指针变量的值是该指针指向的一个实体的内存地址
- 可以通过取地址操作符“&”来获得一个变量的地址
- 为了避免在一个定义中定义多个指针变量时的麻烦,可用typedef来给一个指针类型取个名字,然后定义
typedef int* Pointer;
Pointer q,p;//等价于int *q,*p;
- 常量0除了表示一个整型常量外,还可以表示一个空指针,空指针属于所有的指针类型
- C++中使用NULL表示空指针
5.5.2 指针类型的基本操作
- 赋值操作
- 只能把定义指针类型变量时所指定类型的数据的地址赋给他
- 任何类型的指针都可以赋给void*类型的指针变量
- 间接访问操作(*和->)
- 通过间接访问操作符“*”来访问指针所指的变量,格式为:
- *<指针变量>
- 对于一个指向结构类型数据的指针变量,通过指针访问结构的成员,格式为:
- (*<指针变量>).<结构成员>或<指针变量>-><结构成员>
- 对于一个未赋值(未初始化)的指针变量,访问它所指向的数据很危险
- 通过间接访问操作符“*”来访问指针所指的变量,格式为:
int *p;
*p=1;//1赋值到一个不确定的内存单元里
struct A{
int i;
double d;
char ch;
};
A a;
int i=10;//定义一个int型变量i
int *p;//定义一个指向int型的指针p
p=&i;//将i的内存地址赋给p
cout<<"p(i的内存地址):"<<p<<"\n&p(指针p的内存地址):"<<&p
<<"\nsizeof(p)(指针p所占的内存大小):"<<sizeof(p)<<"\n*p(指针p所指的变量的值):"<<*p
<<"\nsizeof(*p)(指针p所指的变量所占内存大小):"<<sizeof(*p)<<endl;
A *st_p;//定义一个指向结构A的指针st_p
st_p=&a;//指针st_p指向结构体a
st_p->i=0;//或,(*st_p).i=0;
cout<<"st_p(a的内存地址):"<<st_p<<"\n&st_p(指针st_p的内存地址):"<<&st_p
<<"\nsizeof(st_p)(指针st_p所占的内存大小):"<<sizeof(st_p)<<"\n*st_p.i(指针st_p所指的结构a的成员i的值):"<<(*st_p).i
<<"\nsizeof(*st_p)(指针st_p所指的结构体a所占内存大小):"<<sizeof(*st_p)<<endl;
p(i的内存地址):0x7ffee54ee7e8
&p(指针p的内存地址):0x7ffee54ee7e0
sizeof(p)(指针p所占的内存大小):8
*p(指针p所指的变量的值):10
sizeof(*p)(指针p所指的变量所占内存大小):4
st_p(a的内存地址):0x7ffee24ce7d0
&st_p(指针st_p的内存地址):0x7ffee24ce7c0
sizeof(st_p)(指针st_p所占的内存大小):8
*st_p.i(指针st_p所指的结构a的成员i的值):0
sizeof(*st_p)(指针st_p所指的结构体a所占内存大小):24
- 指针的运算
指针可以进行加、减和比较等运算
- 一个指针加上或减去一个整型值,结果为该指针同类型的指针
- 用于以指针的方式来访问元素
- 两个同类型的指针相减,结果为整型值,其大小由指针所指向的类型来定
- 用于在以指针方式来访问数组元素时,计算两个指针所指向的数组元素之间有多少元素
- 两个同类型的指针比较,含义是比较它们所对应的内存地址的大小
- 指针类型可参加逻辑运算,空指针转为false,非空转为true
//一个指针减去/加上一个整型值
inta[10]={0,1,2,3,4,5,6,7,8,9};
int*p1=&a[0];//指针p指向数组a的第一个元素
p1++;//指向数组a的第二个元素
cout<<"*p:"<<*p1<<endl<<endl;
//两个同类型的指针相减
doubled1=9.7,d2=4.3;
double*q1=&d1,*q2=&d2;
doubleoffset=q1-q2;//offset的值为q1和q2所指向的内存范围内能存储的double型变量的最大个数,即offset=(q1的值-q2的值)/sizeof(double)
cout<<"&q1:"<<&q1<<"\t&q2:"<<&q2<<"\toffset:"<<offset<<endl<<endl;
//两个同类型的指针比较
int*p2=&a[7];
int*p3=0;//或int*p3=NULL
cout<<"p1:"<<p1<<"\tp2:"<<p2<<"\tp1>p2:"<<(p1>p2)<<endl;
cout<<"(bool)p2:"<<(bool)p2<<"\t(bool)p3:"<<(bool)p3<<endl<<endl;
*p:1
&q1:0x7ffee42e2780 &q2:0x7ffee42e2778 offset:1
p1:0x7ffee42e27a4 p2:0x7ffee42e27bc p1>p2:0
(bool)p2:1 (bool)p3:0
- 指针的输出
- 通过cout和插入操作符<<来实现
- 当输出字符指针(char*)时,输出的不是指针值,而是该指针所指向的字符串
- 如果要输出一个字符指针的值,应把该字符指针用强制类型转换操作转换成一个其它类型的指针
char str[]="ABCD";//字符数组定义并初始化时可以不用声明长度,以\0为结束符
char*p=&str[0];//定义一个字符指针p指向字符数组str的第一个元素
cout<<"输出第一个元素的地址:"<<(void*)p<<"\t输出第一个元素的值:"<<*p<<endl;
cout<<"直接输出p:"<<p;
输出第一个元素的地址:0x7ffee4af57cb 输出第一个元素的值:A
直接输出p:ABCD
5.5.3 指针作为参数类型(指针的主要用法)
- 通过指针类型的参数提高参数传递的效率
- 为了提高参数传递的效率,可将实参的地址传递给函数,这时行参应定义为指针类型
- 对于一些大型的结构类型数据可采用地址传递
- C++中,当数组作为参数传递时,默认方式是把实参数组的首地址地址传给函数,以提高效率。
- 以数组形式书写,可读性更好
- 为了提高参数传递的效率,可将实参的地址传递给函数,这时行参应定义为指针类型
struct A{
int i;
double d;
char ch;
};
void Pointer::operator5(int *a,int num){//等价于voidPointer::operator5(inta[],int num)
for(inti=0;i<num;++i){
*a=i;
a++;
}
}
void Pointer::operator6(A *p){//p为指向结构类型A的指针
p->i=10;//间接访问成员i
p->d=45.3;//间接访问成员d
}
int a[10];
Pointer::operator5(&a[0],10);//将数组a的首地址a[0]传给函数
for(int i=0;i<10;++i){
cout<<a[i]<<endl;
}
- 通过指针类型的参数返回函数的计算结果
- 将函数的行参定义为指针类,在函数调用时,把实参的地址传给函数的行参,通过参数把函数的多个结果返回给调用者
题1、交换两个变量的值
void Pointer::operator7(int*p1,int*p2){
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
题2、计算一元二次方程实根的函数
int Pointer::calculate_roots(double a,double b,double c,double *root1,double *root2){
if(a==0){
return -1;
}
doublet=b*b-4*a*c;
if(t<0){
return 0;
}else if(t==0){
*root1=*root2=-b/(2*a);
return 1;
}else{
t=sqrt(t);
*root1=(-b+t)/(2*a);
*root2=(-b-t)/(2*a);
return 2;
}
}
double a,b,c,r1,r2;
cout<<"输入一元二次方程的系数(a,b,c):"<<endl;
cin>>a>>b>>c;
switch(Pointer::calculate_roots(a,b,c,&r1,&r2)){
case -1:
cout<<"该方程不是一元二次方程\n";
break;
case 0:
cout<<"该方程没有实数根\n";
break;
case 1:
cout<<"该方程有两个等根:"<<r1<<endl;
break;
case 2:
cout<<"该方程有两个不等根:"<<r1<<"和"<<r2<<endl;
break;
}
- 指向常量的指针
- 将指针作为行参的两个效果:
- 提高参数传递效率
- 通过行参改变实参的值
- 当函数调用者想实现效果1的同时不实现效果2,则可将行参定义为指向常量的指针,使被调用函数无法改变实参的值
- 在C++中,指向常量的指针可以指向常量(const修饰的量),也可指向变量(实现上述效果)
- 但对于一个指向变量的指针(没有const修饰),则不能指向常量(有const修饰)
- 指向常量的指针:const <类型>*<指针变量>
- 指针类型的常量:<类型> * const <指针常量>=初始值
- 该指针所指的地址不能被修改,确保在函数执行中指针类型的行参一直指向的是实参数据
- 指向常量的指针常量:const <类型> * const <指针常量>=初始值
- 既不改变指针本身的值,也不改变指针所指向的地址
- 若行参为常量数组,它的类型就是指向常量的指针常量
- 将指针作为行参的两个效果:
const int NUM=10;//定义一个常量
int num=9;//定义一个变量
const int *p1;//定义一个指向常量的指针
int * const P1=#//定义一个指针类型的常量,不能指向常量,不能改变指针本身
const int * const P2=&NUM;//定义一个指向常量的指针常量
int *p2;//定义一个指针
p1=&NUM;
p1=#
p2=&NUM;//为防止常量被修改,ERROR
p2=#
- 指针作为返回值类型
- 函数的返回值可以是指针类型
- 不能把局部变量的地址作为指针返回给调用者
- 局部变量的生存期为自动生存期,被调用函数执行完毕后收回所占的内存空间(在程序栈区)
5.5.4 指针与动态变量
- C++是静态类型语言,程序中定义的每个变量的类型(所占内存大小)在编译时确定,如果对于数组的个数不确定,则可用动态变量(dynamic variable)解决该问题
- 动态变量是指在程序运行时刻根据需要由程序随时随地创建和撤销的变量,内存分配在程序的堆区。
- 动态变量没有标识符,只能通过指向动态变量的指针访问
- 动态变量的应用:动态数组和链表
- 动态变量的创建
- 两种方式:操作符new和库函数malloc
- 创建格式:
- new <类型名>
- 在程序的堆区创建一个类型由<类型名>指定的动态变量,结果为该动态变量的地址(或指针),其类型为:<类型名>*
- new <类型名>
int *p;//int类型的指针p
p = new int;//产生一个动态的整型变量,p指向该变量
*p = 1;//只能通过指针变量访问该动态的整型变量
p = new double;//ERROR该new操作结果为double*
- new <类型名> [<整型表达式 1>] ··· [<整型表达式 n>]
- 在程序的堆区创建一个动态的n(n≧1)维数组,数组元素的类型由<类型名>指定,每一维大小由 [<整型表达式 1>] ··· [<整型表达式 n>]指出,new操作返回数组的首地址,其类型由数组的维数决定
- 注意:除了第一维大小外,其他维的大小必须是常量或常量表达式
//创建一个元素个数为n的一维动态数组
int n;
p = new int[n];//创建一个由n个int型元素构成的动态一维数组,返回第一个元素的地址
p[0] = 0;//只能通过指针变量访问数组元素
//创建一个m行20列的二维动态数组
int (*q) [20];//创建一个指向20个int型元素构成的一维数组的指针,等价于:typedefintA[20];A*q;
int m;
q = new int [m][20];//创建一个n行20列的二维动态数组,返回第一行的地址,等价于:q=newA[n];
q[0][1] = 0;//只能通过指针q访问二维数组的第0行第1列元素
//创建一个c行d列(每一维大小都可变)的多维动态数组(通过一维数组实现)
int *r;//创建int型指针r
intc ,d;
r = new int[c*d];//创建一个由c*d个int型元素构成的一维动态数组,返回第一个元素的地址
*(r+2*d+3) = 0;//访问r指向的隐含的二维数组的第2行第3列元素
- void * malloc(unsigned int size);
- 在程序的堆中分配一块大小为size的内存空间,并返回该空间的首地址,类型为:void *
- 如果该空间用于存储某个具体类型的数据,则需对返回值类型进行强制转换
int *p1,*p2,*r1;//定义三个类型指针
typedef int A[20];
A *q1;//等价于int (*q1) [20];//q1为指向20个int型元素的构成的一维数组的指针
int m1,n1;
p1 = (int*)malloc(sizeof(int));//创建一个int型动态变量
p2 = (int*)malloc(sizeof(int)*n);//创建一个由n个int元素构成的一维动态数组变量
q1 = (A*)malloc(sizeof(int)*n*20);//创建一个n行20列的动态二维数组
r1 = (int*)malloc(sizeof(int)*m*n);//创建一个隐含的m行n列的二维动态数组
- new与malloc的区别:
- new自动计算所需分配的空间大小,malloc需要显式指出
- new自动返回相应类型的指针,malloc需要显示转换
- 如果堆区没有足够的空间分配,则产生bad_alloc异常(或返回空指针)
- 动态变量的撤销
- 动态变量在程序运行期间不会自动消亡(即使定义在函数中的局部动态变量,函数返回后依然存在(可以使用))
- new创建的动态变量,用delete撤销;malloc创建的动态变量,用free撤销
- 格式:
- delete<指针变量>
- delete[]<指针变量>
- 撤销动态数组时,指针变量必须指向数组的第一个元素
- void free(void *p)
- 撤销动态变量后,C++不会把指向该变量的指针的值赋值为NULL,这时就会出现悬浮指针(dangling pointer),它指向一个无效空间,这时若通过该悬浮指针访问相应的动态变量就会导致程序的语义错误
- delete和free不能撤销非动态变量
- 格式:
- 如果动态变量没有被撤销,而指向该动态变量的指针指向了别处,该动态变量就会成为孤儿,在程序中无法访问,却始终占着内存空间(内存泄漏(memory leak))
- Java提供了运行时刻的自动废区收集(garbage collection)功能,将程序不再使用的动态变量的空间自动收回,但在程序运行时影响效率
- 动态变量的应用——动态数组
题3、用动态数组实现对键盘输入的数(个数不限)进行排序
思路:不确定输入数的个数,先创建一个较小的动态数组接受输入数,如果超过,在创建一个较大的数组,把之前的数组复制过来,重复多次
void Pointer::operator10(){
const int NUM=5;
int length=10,n=0,count=0;
int *p;
p = new int [length];//定义一个动态数组,长度为5
cout<<"输入一组数,以-1结束\n";
cin>>n;//输入的第一个数
while(n!=-1){
p[count] = n;
count++;//输入个数加一
if(count>=length){
length = length+NUM;
int *bigger = new int[length];//创建较大的动态数组变量
for(int i=0;i<count;++i){//将小数组的数据转移到大数组
bigger[i]=p[i];
}
delete[]p;//将小动态数组删除
p = bigger;//指针p指向大数组
}
cin>>n;//输入的下一个数
}
Array::bubble_sort(p,count);
for(int j=0;j<count;++j){
cout<<p[j]<<"\t";
}
delete[]p;//删除大数组
}
与直接定义一维数组变量相比,可以在运行时刻动态释放较小数组占用的内存空间
但当数组空间不够时,申请空间、复制数组、释放原有空间会导致效率不高
- 动态变量的应用——链表
- 链表的数据结构
- 链表(linked list)是一种线性结构,由若干个节点组成。
- 单链表(singly linked list)也叫线性链表,表示线性表时,用指针表示结点间的逻辑关系,单链表的一个存储节点(node)包含两个部分(域,field):数据域(data)和指针域或链域(link)
- 链表的基本操作
- 插入、删除、查找、输出
- 链表的数据结构
题4、用链表实现对键盘输入的数(个数不限)进行排序
题5、用链表解决约瑟夫问题
5.5.5 指针与数组
- 访问数组元素的方式:
- 通过下标:单次访问或循环访问时需要根据计算数组元素的地址。对于a[i],其元素地址=a的首地址+i *sizeof(int)。(一次乘法,一次加法)
- 通过指针:首先指向数组首地址,循环访问时,只需将指针的值+1,不需重复计算,提高程序访问数组元素的效率
- 获得数组首地址的方式:
- 一维数组的首地址
- 通过它的第一个元素地址获得数组的首地址
- 通过整个数组变量获得数组首地址(常用于按行访问二维数组)
- 一维数组的首地址
- 获得数组首地址的方式:
typedef int A[10];
A a;//或者 int a[10];
A *p;//或者int (*p) [10] p是一个指向由10个int型元素构成的数组的指针
p=&a;//p指向数组a的首地址
注意:
- int(*p)[10]:定义了一个指向由10个int型元素构成的一维数组的指针变量;
- int *p[10]:定义了由10个int *型指针元素构成的一维数组变量p
- &a[0]:都表示数组的首地址,类型:int *,只能赋给int *指针
- &a:都表示数组的首地址,类型:A *,只能赋给A *指针
- 多维数组的首地址
- 对于一个n(n>1)维数组,可以看为一维数组,该一维数组的个数为第一维的大小,元素类型为去掉第一维后的n-1维数组
int b[20][10];// 或typedef int B[20][20]; B b; 理解为typedef int A[10];A b[20];
获取二维数组b的首地址
通过第一行第一列元素获得
int *p = &b[0][0];//类型int *
通过第一行的一维数组获得
A *p = &b[0];//类型 A*
通过整个数组获得
B *p = &b//类型 B*
题6、用指针实现字符串逆转功能
思路:采用两个指针,分别指向字符数组的头部和尾部,然后让他们相向往中间移动的同时交换它们所指向的字符
void Pointer::reverse(char *str){
char *p1=str;//指向字符串的头
char *p2=str+strlen(str)-1;//指向字符串的尾
for(;p1<p2;p1++,p2--){
chartemp=*p1;
*p1=*p2;
*p2=temp;
}
}
- 动态多维数组的创建
- 按一维动态数组创建,返回的首地址类型是去掉第一维后的数组指针类型
typedef int A[10];//A表示一个由10个int型元素组成的一维数组
A *p;//或int (*p)[10];
int n;
p=new int[n][10];//形式上创建一个元素类型为int的n*10的二维数组;实际上创建的是由n个A类型元素构成的一维动态数组
//返回值类型为A*
- 二维数组传给函数时,编译器将其转换为第一行的地址
用指针访问数组元素,有利于提高程序的运行效率
- 函数main的参数
- 函数main的参数定义格式:
- int main ( int argc , char *argv[] );
- argc:传给main的参数的个数
- argv:一维数组,其每个元素为一个指向字符串的指针
- 参数值由操作系统或其他程序提供
- int main ( int argc , char *argv[] );
- 函数main的参数定义格式:
5.5.6 函数指针
- 指向函数的指针
- 指向函数的指针称为函数指针,定义格式:
- <返回类型> (*<指针变量>) (<行参列表>)
- typedef <返回类型> (*<函数指针类型名>) (<参数表>);
- 可用取地址符&来获取它的内存地址,或直接用函数名表示
- 给函数指针赋值时,必须是定义函数指针时规定的函数类型
- 可通过函数指针调用它所指的函数,调用格式:
- (*<函数指针变量>) (<实在参数表>)
- 注意:区分函数指针与返回指针的函数
- 指向函数的指针称为函数指针,定义格式:
//定义一个可指向返回类型为double,参数类型为int的任何函数的函数指针
double (*fp) (int);//或typedef double (*FP) (int); FP fp;
double f(int x){...}
fp=&f;//或fp=f; 指向函数f
(*fp)(10);//或f(10);通过指针调用函数
- 向函数传递函数(函数指针的用法)
- 若一个函数A作为函数B的行参,则函数B的行参应定义为一个函数指针类型,调用时的实参为函数A的地址
- 匿名函数的实现——𝛌表达式
- 对于一些临时用一下的函数,C++11提供了一种匿名函数机制——𝛌表达式(lambda expression),可以将函数的定义和使用合二为一
- 𝛌表达式的格式:
[<环境变量使用说明>] <形式参数> -><返回值类型指定> <函数体>
- <环境变量使用说明> :指出函数体中对外层作用域中的自动变量的使用限制
- 空:不能使用外层作用域中的自动变量
- &:按引用方式使用外层作用域中的自动变量(可以改变这些变量的值)
- =:按值方式使用外层作用域中的自动变量(不能改变这些变量的值)
- 也可以单独指定可使用的外层自动变量(变量名前可以加“&”,默认为“=”)
- <形式参数>:指出函数的参数及类型
- <返回值类型>:指出函数的返回值类型
- <函数体>:复合语句
- 𝛌表达式通常用于把一个匿名函数作为参数传递给另一个函数的场合,传递的是该匿名函数的函数地址
- 𝛌表达式是通过函数对象来实现的
{
int k,m,n;
[ ] (int x)->int {return x;}//不能使用k,m,n
[&] (int x)->int{k++; m++;n++;return x+k+m+n;}//k,m,n可以被修改
[=] (int x)->int{k++; m++;n++;return x+k+m+n;}//k,m,n不可以被修改
[&,n] (int x)->int{k++; m++;return x+k+m+n;}//n不可以被修改
[=,&n] (int x)->int{n++;return x+k+m+n;}//n可以被修改
[&k,m] (int x)->int{k++;return x+k+m+n;}//只能使用k和m,k可以被修改
[=] {return x+k+m+n;}//没有参数,返回值为int
}
5.5.7 多级指针
- 如果一个指针变量所指向的变量类型还是指针类型,则为多级指针
int x=0;
int *p=&x;
int **q=&p;//q为多级指针
- 如果一个指针变量没有初始化或赋值,访问该指针指向的变量会很危险
题6、编写一个函数,交换两个指针变量的值
思路:行参定义为二级指针,实参传入指针类型实参的地址
void Pointer::swap(int **x, int **y) {
int *t;
t=*x;
*x=*y;
*y=t;
}
int main() {
int a=0,b=1;
int *p1=&a,*p2=&b;
cout<<*p1<<","<<*p2<<endl;
Pointer::swap(&p1,&p2);
cout<<*p1<<","<<*p2<<endl;
return 0;
}
0 1
1 0
- 指针的使用场景
- 指针作为形参
- 提高参数的传递效率(如数组等)
- 通过行参改变实参的结果,实现函数返回多个返回值
- 若不想改变实参的值,可将实参定义为指向常量的指针
- 访问动态变量
- 只能通过指针访问动态变量(动态数组,链表等)
- 使用访问数组
- 通过指针访问数组元素,提高运行效率
- 指向函数的指针
- 向函数传递函数,行参为函数指针类型
- 指针作为形参
5.6 引用类型——变量的别名
为了获得指针的效果,同时避免指针的问题,C++提供了引用类型,相比指针抽象、安全。
5.6.1 引用类型的定义
- 引用类型(reference type)可为已有变量取一个别名,引用类型没有自己的内存空间,与另一个变量占有相同的空间,定义格式:
- <类型> &<引用变量>=<变量>;
- <类型>:所引用的变量类型(除void以外的任意C++数据类型)
- <引用变量>:引用类型变量的名字,用标识符表示
- <变量>:被引用的变量
- <类型> &<引用变量>=<变量>;
- 使用引用类型注意:
- 定义时,在变量名前加&,以区分普通变量
- 定义时必须初始化
- 引用类型的变量在定义后,不能引用其他类型的变量
5.6.2 引用作为参数类型
- 可将函数的行参定义为引用类型,引用类型的行参与实参占用同一内存空间
- 行参为引用类型可以改变实参的值(函数的副作用)和提高参数的传递效率
- 为了防止函数的副作用,可在定义行参时加上关键字const,表示不能改变所引用的变量的值
- 行参为引用类型可以改变实参的值(函数的副作用)和提高参数的传递效率
- 函数的返回值也可以是引用类型
- 如果一个函数的返回值是引用类型,则该函数不应该返回对其局部量的引用
//交换两个int型变量
void Reference::swap(int &x,int &y){
intt;
t=x;
x=y;
y=t;
}
//交换两个int*指针变量的值
void Reference::swap(int *&x,int *&y){
int*t;
t=x;
x=y;
y=t;
}
void Reference::f(constint&x){
x=1;//Error
}
//返回数组中最大元素的引用
int &Reference::max(int *x,int num){
inti,j;
j=0;
for(i=1;i<num;i++){
if(x[i]>x[j]){
j=i;
}
}
returnx[j];
}
Reference reference;
int a=10,b=20;
int &A=a,&B=b;//A为a的引用,B为b的引用
int *pa=&a,*pb=&b;//pa指向a,pb指向b
//交换两个引用变量的值
reference.swap(A,B);
cout<<"a:"<<a<<",b:"<<b<<endl;
//交换两个指针变量的值
reference.swap(pa,pb);
cout<<"a:"<<a<<",b:"<<b<<endl;
intc[10]={0,1,2,3,4,5,6,7,8,9};
cout<<reference.max(c,10);
a:20,b:10
a:10,b:20
9
注意:交换指针变量的值,是指交换它们所指的内存地址
5.6.3 引用类型与指针类型功能上的区别
- 引用类型和指针类型都可以实现通过一个变量访问另一个变量,但访问的语法形式不同
- 引用类型:直接访问
- 指针类型:间接访问
- 作为行参时,引用类型参数的实参是一个变量的名字,指针类型的实参是一个变量的地址
- 引用类型定义后不能再引用其他变量,指针变量定义后可以指向其他变量
5.7 小结
- 除了提供基本数据类型外,C++还提供程序自定义类型(构造数据类型),用以描述复合类型的数据。
- 枚举类型是由用户自定义的一种简单数据类型,其值集由程序指定。枚举类型可以提高程序的易读性和可靠性。
- 数组类型用于描述由固定多个同类型的数据所构成的复合数据。对数组类型数据的访问和操作通过其元素实现,元素采用下标形式表示。常用的数组类型是一维数组和二维数组。
- 字符串用一维字符数组表示。
- 结构类型用于描述由固定多个类型可以不同的元素所构成的复合数据。对结构类型数据的访问和操作通过其成员进行,结构类型的成员采用“<结构变量>.<成员>”形式来表示。
- 联合类型是指用一种类型来表示多种类型的数据。利用联合类型可以实现多种类型的数据共享空间。
- 指针类型用于表示程序实体的内存地址。一个指针类型变量的值是另一个程序实体的内存地址。
- 通过取地址符&可以取得一个程序实体的内存地址。
- 通过间接访问操作符*可以访问一个指针所指向的程序实体。
- 指针用作函数的一种参数传递机制可以提高参数传递的效率。
- 通过指针来访问数组元素可提高效率。
- 指针也可指向函数,可以把函数作为一个参数传递给另一个参数
- 动态变量用于表示结构可变的数据,只能通过指针访问。
- 动态变量在程序运行时刻通过操作符new或函数malloc创建,用操作符delete或函数free撤销。
- 动态数组和链表是动态变量的典型应用。
- 引用类型是为已有变量取一个别名,主要用于函数的参数类型,其效果与指针类型的参数相同,但比指针更安全、简洁和易用。
https://github.com/zzq1996/ProgrameDesign
参考:《程序设计教程:用C++语言编程》 陈家骏,郑滔