2.5 const常量
2.5.1 常量的定义
变量实质上是在程序运行过程中其值可以改变的内存单元的名字。常量就是在程序执行过程中其值固定不变的内存单元的名字。在C++中,常用const修饰符定义常量,方法如下:
const 常量类型 常量名=常量值;
例如:
const i=10; //L1 const int i=10; //L2 const char c='A'; //L3 const char s[]="C++ const !"; //L4
语句L1和L2等价,C++将没有指定类型的常量定义默认为int类型(在Visual C++ .NET中不允许默认值,L1是错误的),L3定义了字符常量c,L4定义了字符常量数组s。
说明:① const常量必须在定义时初始化,且常量一经定义就不能修改(常量名不能出现在赋值符“=”的左边),例如:
const int n; //错误,常量n未被初始化 const int i = 5; //定义常量i i = 10; //错误,修改常量 i++; //错误,修改常量
② 在C++中,表达式可以出现在常量定义语句中。如果定义的常量是整型,则类型关键字int可以省略。例如:
int j,k=9; //L1 const i1=10+k+6; //L2 const int i1=10+k+6; //L3 const i2=j+10; //L4
当常量定义语句中出现表达式时,C++将首先计算表达式的值,然后把计算结果指定给常量。语句L2省略了类型关键字int,它与L3是完全等价的,相当于“const int i1=25;”。语句L4尽管在有的编译环境下不会出错误,但没有意义,因为j的取值是未知的。
③ 在C++中,仍然可以采用#define定义常量,但#define采用宏代换方式进行常量的处理,不具有类型检查机制,存在不安全性。
2.5.2 const与指针
const可以与指针结合,由于指针涉及“指针本身和指针所指的对象”,因此它与常量的结合也比较复杂,可分为三种情况,如图2-1所示。
图2-1 const与指针的三种关系
图2-1中的type代表C++中的任意数据类型,p是一个指针变量。其中,第一种是常量指针,即指针是常量,不能被修改,但其所指内存区域是变量,可修改;第二种是指向常量的指针,即指针是变量,可再指向其他内存单元,但其所指单元是常量,不能修改;第三种是指向常量的常指针,指针及其所指内存单元都为常量,皆不能被修改。
【例2-7】 const与指针的关系。
//Eg2-7.cpp #include <iostream.h> int main(){ char *const p0; //L1 错误,p0是常量,必须初始化 char *const p1="dukang"; //L2 正确 char const *p2; //L3 正确 const char *p3="dukang"; //L4 正确 const char *const p4="dukang"; //L5 正确 const char *const p5; //L6 错误,p5是常量,必须初始化 p1="wankang"; //L7 错误,p1是常量,不可改 p2="wankang"; //L8 正确,p2是变量,可改 p3="wankang"; //L9 正确,p3是变量,可改 p4="wankang"; //L10 错误,p4是常量,不可改 p1[0]='w'; //L11 正确 p2[0]='w'; //L12 错误,*p2是常量,不可改 p3[0]='w'; //L13 错误,*p3是常量,不可改 p4[0]='w'; //L14 错误,*p4是常量,不可改 return 0; }
例中,*p2和*p3的定义形式等价,只是p2没有初始化,p3被初始化了。请结合图2-1和本例中的注释理解各语句正确和错误的原因。
const对指针和变量之间的相互赋值具有一定影响:const对象的地址只能赋给指向const对象的指针,指向const对象的指针可以被赋予一个非const对象的地址,否则引起编译错误。例如:
int x=9; //L1 const int y=9; //L2 int *p1; //L3 const int *p2; //L4 p1=&y; //L5,错误 p2=&x; //L6,正确
语句L5将引起编译错误,若改为“p2=&y;”则是正确的,请结合上述说法理解其中原因。
2.5.3 const与引用
在定义引用时,可以用const进行限制,使它成为不允许被修改的常量引用。例如:
int i=9, int &rr=i; const int &ir=i; rr=8; ir=7; //错误
最后一条语句是错误的,因为ir是const引用,不允许通过它修改对应的变量i。
const引用可以用常量初始化,但非const引用不能用常量初始化。例如:
int i=2; //L1 const double &ff=10.0; //L2 const int &ir=i+10; //L3 int &ii=3; //L4,错误
语句L4错误,语句L2、L3正确的原因与编译器的处理方式有关。编译器在实现常量引用时生成了一个临时对象,然后让引用指向这个对象。但该对象对用户而言,是隐藏不可知的,不能访问。例如,对于
const double &ff=10.0;
编译器将其转换为类似于下面的形式:
double temp=10.0; const double &ff=temp;
编译器首先产生不为用户所知的临时变量temp,然后将引用指向它。temp将保持到引用的生命期结束。