从 C 向 C++ 进阶系列导航
1. const 关键字
在 C 中,const 关键字可以改变变量的属性以及存储空间。不同的判别标准如下:
- 修饰局部变量:变量具有只读属性,仍保存在内存的栈中,可通过指针访问地址进行修改。
- 修饰全局变量或静态变量:变量存储空间由静态存储区改变为只读存储区(.rodate段),且不能用指针来修改。
在 C++ 中,对 const 关键字的作用进行了优化。编译器编译过程中遇到 const 修饰的标识符时,会将 const 修饰的标识符放入符号表中。如果后续编译过程中发现 const 修饰的标识符时,直接使用符号表中 const 修饰的标识符对应的值直接替换。
- 示例:
int main(void)
{
const int var_con = 3;
int arr[var_con] = {0}; // 等价于 int arr[3] = {0};
}
以上在 C++ 是合法的,但在 C 中会发生编译错误,因为在 C 中只读变量不能作为左值。
2. C++ 的 const 常量
const 常量的判别标准:
- 用字面量初始化的 const 常量会进入符号表,因为在编译期间常量值已确定。
- 用已存在的变量初始化的 const 常量仍为只读变量,因为在编译期间常量值无法确定。
- 被 volatile 修饰的 const 常量不会进入符号表。
C++ 想是把 const 修饰的变量从 C 中的只读变量优化为真正意义上的常量,但为了兼容 C 语言的做法,在以下情况下,编译器会给 const 常量分配空间:
- const 常量为全局(extern修饰),并且需要在其它文件中使用。
- 使用 & 操作符对 cosnt 常量取地址。
虽然编译器对 const 常量分配了空间,但不会使用其存储空间的值。
- 实验
int main()
{
int *p = NULL;
/* 用字面量初始化 const 常量 */
const int var_con_A = 2; // 进入符号表
cout << "sizeof(var_con_A) = " << sizeof(var_con_A) << endl; // 由于 sizeof 发生在预处理阶段,因此可以得到对应的内存大小
cout << "var_con_A = " << var_con_A << endl; // var_con_A = 2
p = (int*)&var_con_A;
*p = 3;
cout << "*p = " << *p << endl; // *p = 3
cout << "p = " << p << endl; // p = 0x7fff84f1a070
cout << "var_con_A = " << var_con_A << endl; // var_con_A = 2,编译过程中已替换
cout << "&var_con_A = " << &var_con_A << endl; // &var_con_A = 0x7fff84f1a070
int arr_A[var_con_A] = {0}; // no error
/* 用已存在的变量初始化 const 常量 */
int num = 5;
const int var_con_B = num; // 未进入符号表
cout << "var_con_B = " << var_con_B << endl; // var_con_B = 5
p = (int*)&var_con_B;
*p = 6;
cout << "*p = " << *p << endl; // *p = 6
cout << "p = " << p << endl; // p = 0x7fff84f1a074
cout << "var_con_B = " << var_con_B << endl; // var_con_B = 6
cout << "&var_con_B = " << &var_con_B << endl; // &var_con_B = 0x7fff84f1a074
int arr_B[var_con_B] = {0}; // no error
/* volatile 修饰的 const 常量 */
volatile const int var_con_vol = 8; // 未进入符号表
cout << "var_con_vol = " << var_con_vol << endl; // var_con_vol = 8
p = (int*)&var_con_vol;
*p = 9;
cout << "*p = " << *p << endl; // *p = 9
cout << "p = " << p << endl; // p = 0x7fff84f1a078
cout << "var_con_vol = " << var_con_vol << endl; // var_con_vol = 9
cout << "&var_con_vol = " << &var_con_vol << endl; // &var_con_vol = 1
int arr_vol[var_con_vol] = {0}; // no error
}