1. const的最初动机是取代预处理器#define来进行值替代
#define只做些文本替代,它既没有类型检查概念,也没有类型检查功能,所以预处理器的值替代会产生一些问题。
这些问题在C++中可以通过使用const来避免。
2. C++中的const默认为内部连接(internal linkage)
const仅在被定义过的文件里才是可见的,而且在连接时不能被其他编译单元看到(默认情况)。
3. 当定义一个const时,必须赋一个值给它
也就是说,在定义的时候必须要进行初始化。
4. const要么保存在符号表中,要么由编译器为其分配存储空间
通常C++编译器并不为const创建存储空间,而是把这个定义保存在符号表里。
只有以下两种情况会为const创建存储空间:
1)进行取该const的地址的操作(包括把该const传递给一个带引用参数的函数)
2)该const被定义为extern。这时候表示要在其他编译单元中用到它,因此必须要用存储空间
5. 编译期间的const
通常const是一个编译期间的const,可以在编译期间使用。
比如一个const int用于确定另一个数组的大小:
const int bufsize = 100;
char buf[bufsize];
注意:这段代码在C++是正确的,但是在C中是错误的。C中const的意思是“一个不能改变的普通变量”,const在C中总是占用存储的。C编译器不能把const看成一个编译期间的常量。
下面几种情况const不能再编译期间使用:
1)其值在编译期间是不知道的。比如如下情况:
const char c = cin.get();
c的值在编译期间是不知道的,这意味着需要存储空间,而且编译器不会在符号表中保留它的任何信息。因此c在编译期间是不可用的。
2)当const用于集合(数组、结构体等)的时候。比如如下情况:
const int i[] = {1, 2, 3, 4};
编译器不会复杂到把一个集合保存到符号表中,因此必须分配内存。
在这种情况下,const意味着“不能改变的一块存储空间”,但是不能在编译期间使用它的值。比如下面语句对i的使用就是错误的:
float f[i[3]];
这里试图使用i中的元素来确定另一个数组的大小,然后编译期间i是不能使用的,因此这里编译器会报错。
6. const的外部引用
由于C++中的const默认为内部连接,所以不能在一个文件中定义一个const,而在另外一个文件中又把它作为extern来引用。
为了使const成为外部连接以便让另外一个文件可以对它引用,必须明确地把它定义成extern,如下:
extern const int x = 1; (1)
通过对它初始化并指定为extern,我们强迫给它分配内存。初始化使得它成为一个定义而不是一个声明。而下面的语句:
extern const int x; (2)
是在另外的文件中对该const的声明,表示要引用其他文件中的一个const变量x。
注意语句(1)和(2)的区别,(1)是对const的定义;(2)是在另外的文件中对const的声明,表示要引用其他文件中的const变量x。
还有一个要注意的地方,在4中说过,当一个const被定义为extern时,就会为它分配存储空间。那么之后对该const的使用不再通过符号表,而是通过其存储空间的地址。
7. 常量折叠(const folding)
当一个const没有被编译器分配存储空间时,编译器在编译时会通过必要的计算把一个复杂的常量表达式通过缩减简单化。这个操作称为“常量折叠”
比如:
const int i = 10 * 5;
编译器会在符号表中存储类似“i=50”的信息。这个是#define做不到的,预处理器只是简单地进行文本替换。
8. const与指针
1)指向const的指针(不需要初始化):
const int* u;
或者 int const* v;
u是一个指针,它指向一个const int。
2)const指针(需要初始化):
int d = 1;
int* const w = &d;
w是一个指针,这个指针是指向int的const指针。