8.1.4 与C语言的区别
常量引进是在C++的早期版本中,当时标准C规范正在制定。那时,尽管C委员会决定在C中引入const,但是,不知何故,对他们来说,C中const的意思是“一个不能被改变的普通变量”,const常量总是占用存储而且它的名字是全局符。这样,C编译器不能把const看成一个编译期间的常量。在C中,如果写:
尽管看起来好像做了一件合理的事,但这将得出一个错误。因为bufsize占用某块内存,所以C编译器不知道它在编译时的值。在C语言中可以选择这样书写:
这样写在C++中是不对的,而C编译器则把它作为一个声明,指明在别的地方有存储分配。因为C默认const是外部连接的,所以这样做是合理的。C++默认const是内部连接的,这样,如果在C++中想完成与C中同样的事情,必须用extern明确地把连接改成外部连接:
这行代码也可用在C语言中。
在C++中,一个const不必创建内存空间,而在C中,一个const总是需要创建一块内存空间。在C++中,是否为const常量创建内存空间依赖于对它如何使用。一般说来,如果一个const仅仅用来把一个名字用一个值代替(如同使用#define一样),那么该存储空间就不必创建。要是存储空间没有创建的话(这依赖于数据类型的复杂性以及编译器的性能),在进行完数据类型检查之后,为了代码更加有效,值也许会折叠到代码中,这和以前使用#define不同。不过,如果取一个const的地址(甚至不知不觉地把它传递给一个带引用参数的函数)或者把它定义成extern,则会为该const创建内存空间。
在C++中,出现在所有函数之外的const的作用域是整个文件(也就是它只是在该文件外不可见),也就是说,它默认为内部连接,这和C++中的所有其他默认为外部连接标识符很不一样(也与C中的const不一样)。因此,如果在两个不同文件中声明同名的const不取它的地址,也不把它定义成extern,那么理想的C++编译器就不会为它分配内存空间,而只是简单地把它折叠到代码中。因为const在一个文件范围内有效,所以可以把它放在C++头文件中,在连接时不会造成任何冲突。
因为C++中的const默认为内部连接,所以不能在一个文件中定义一个const,而在另外一个文件中又把它作为extern来引用。为了使const成为外部连接以便让另外一个文件可以对它引用,必须明确地把它定义成extern,如下面这样:
注意,通过对它进行初始化并指定为extern,我们强迫给它分配内存(虽然编译器在这里仍然可以选择常量折叠)。初始化使它成为一个定义而不是一个声明。在C++中的声明:
意味着在别处进行了定义(在C中,不一定这样)。现在明白为什么C++要求一个const定义时需要初始化:初始化把定义和声明区别开来(在C中,它总是一个定义,所以初始化不是必需的)。当进行了extern const声明时,编译器就不能够进行常量折叠了,因为它不知道具体的值。
在C语言中使用限定符const不是很有用的,如果希望在常数表达式里(必须在编译期间被求值)使用一个已命名的值,C总是迫使程序员在预处理器里使用#define。