const变量的文件作用域
以前从来没有注意到的一个知识点: const 修饰的对象默认只有当前文件中有效。这就表明了:
- 在不同的.cpp文件内可以定义相同名称的const 对象。
- 如果要使const 我修饰的变量具有全局使用域,在定义该变量时,需要加上extern 关键字。
不使用extern关键字定义const变量
在1.cpp文件中,定义如下:
const int a = 100;
在2.cpp文件中,定义如下:
extern const int a;
int main() {
cout << a << endl;
return 0;
}
我们进行编译, 会报链接错误,原因是:在2.cpp文件找不到变量a的定义。
使用extern关键字定义const 变量
在1.cpp文件中,定义如下:
extern const int a = 100;
在2.cpp文件中,定义如下:
extern const int a;
int main() {
cout << a << endl;
return 0;
}
编译正常,代码运行正常:
进一步分析原因
我猜测当定义一个const变量时,使用与不使用extern关键字时,生成的变量的符号不相同。下面进行验证。
-
第一步:新建一个1.cpp文件,定义如下:
const int value1 = 100; extern const int value2 = 100; int value3 = 100; extern int value4 = 100;
-
第二步, 编译成.o文件。(报了一个编译警告,不管它)
- 第三步:使用readelf工具看一下.o文件的符号表
我们看到:当定义一个const变量时,如果不使用extern 关键字进行修饰,默认生成的变量的符号为local 属性,而使用extern 关键字时,生成的符号为global属性。 对应local属性的符号,在链接的时候是找不到的。所以,const 对象默认只在文件内有效。
修改const修饰的变量会发生什么
修改const修饰的全局变量
看如下代码:
1 #include <iostream>
2 using namespace std;
3
4 const double a = 10.5;
5
6 int main() {
7 double* p = const_cast<double*>(&a);
8 *p = 110.5;
9 return 0;
10 }
编译可过,运行时,段错误。
原因是全局的const变量分配到了只读内存区。可以写如下代码验证一下:
1 #include <iostream>
2 using namespace std;
3
4 const int a = 10.5;
5 const int b = 10.5;
6
7 int c = 100;
8 int d = 100;
9
10 int main() {
11 cout << &a << endl;
12 cout << &b << endl;
13 cout << &c << endl;
14 cout << &d << endl;
15 return 0;
16 }
输出内容如下所示。内存地址差了很多的。
修改const修饰的局部变量
代码如下:
1 #include <iostream>
2 using namespace std;
3
4 int main() {
5 const double a = 10.5;
6 double* p = const_cast<double*>(&a);
7 *p = 20.5;
8 cout << a << endl;
9 cout << *p << endl;
10 return 0;
11 }
编译后,输出如下图:
输出符合你的预期没? 继续执行下面代码:
1 #include <iostream>
2 using namespace std;
3
4 int main() {
5 volatile const double a = 10.5;
6 double* p = const_cast<double*>(&a);
7 *p = 20.5;
8 cout << a << endl;
9 cout << *p << endl;
10 return 0;
11 }
编译后,输出为如下图:
原因:编译器对const变量进行了优化,读取它的值时,直接从寄存器里拿。当加上volatile
修饰后,编译器指示每次从内存中读取。
c++的const属性为bitwise
什么是bitwise-const
为了说明什么是bitwise-const, 直接上代码, 让你惊讶一下。
1 #include <iostream>
2
3 using namespace std;
4
5 struct Info{
6 int& a;
7 int* p;
8 };
9
10 void Modify(const Info& input) {
11 input.a += 100;
12 *input.p += 100;
13 }
14
15 int main() {
16 int num1 = 100;
17 int num2 = 100;
18 Info input{num1, &num2};
19 cout << input.a << endl;
20 cout << *input.p << endl;
21
22 Modify(input);
23
24 cout << input.a << endl;
25 cout << *input.p << endl;
26 return 0;
27 }
编译成功, 输出如下:
上面的结果,是否有点不习惯呢? bitwise-const, 就是修饰的对象的物理BIT位不可以修改。但是呢,如果结构体的成员为指针或者引用, 你是限制不住通过该const变量修改指针指向的变量的值或者引用的值。
如何取某一个成员变量的const属性
使用mutable修饰符可以取消一个const修饰的结构体或类对象内某一个变量的const属性。 mutable变量在类内经常使用,目的是可以让const修饰的成员函数修改成员变量。
使用mutable修改前,代码如下,编译错误。
1 #include <iostream>
2
3 using namespace std;
4
5 struct Info{
6 int a;
7 int b;
8 };
9
10 int main() {
11 const Info data{10, 20};
12 data.a = 100;
13 data.b = 100;
14 cout << data.a << " " << data.b << endl;
15 return 0;
16 }
使用mutable修改后,代码如下,编译OK,运行OK。
1 #include <iostream>
2
3 using namespace std;
4
5 struct Info{
6 mutable int a;
7 mutable int b;
8 };
9
10 int main() {
11 const Info data{10, 20};
12 data.a = 100;
13 data.b = 100;
14 cout << data.a << " " << data.b << endl;
15 return 0;
16 }