c/c++的const说明符——深入挖掘



const变量的文件作用域

以前从来没有注意到的一个知识点: const 修饰的对象默认只有当前文件中有效。这就表明了:

  • 在不同的.cpp文件内可以定义相同名称的const 对象。
  • 如果要使const 我修饰的变量具有全局使用域,在定义该变量时,需要加上extern 关键字。

原因: 在未声明为 extern 的非局部非 volatile 非模板 (C++14 起)非 inline (C++17 起)变量声明上使用 const 限定符,会给予该变量内部连接。这有别于 C,其中 const 文件作用域对象拥有外部连接。

不使用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 }

posted @ 2021-05-04 17:00  殷大侠  阅读(283)  评论(0编辑  收藏  举报