本文转自徐飞翔的“const修饰符并不保证运行时的常数性质”
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
const和volatile修饰符统称为cv修饰符,用于指示编译器是否允许一个程序中的某个变量的内存是否在初始化后,仍允许被修改。其中的volatile我们已经在前文[1]中讨论过了,我们讨论下const的一些特性。我们知道,cv修饰符都是给编译器看的,用于指导编译过程中的优化(如volatile)或者用户非法变量修改行为(如const),因此这俩个修饰符只能保证编译时的操作合乎设计时候的需求,但是如果涉及到运行时(running time)的一些操作,是无法保证的。我们本文讨论const修饰后的变量,被运行时『修改』的例子。
如果我们正常使用const
,理应如下所示:
const int var = 10;
int var_b = 100;
var = 100; // 非法操作,因为var是const类型,初始化后不能被修改。
var_b = 10; // 合法操作,正常的赋值
然而,我们不能确保运行时,被const
修饰的变量内存,被其他程序,或者自行设计错误,或者黑客行为修改,举个例:
#include <stdio.h>
int main(void) {
int a = 10;
const int b = 9;
int c = 8;
printf("%d\t%d\t%d\n\r", a, b, c);
int* pa = &a;
const int* pb = &b;
int* pc = &c;
printf("%x\t%x\t%x\n\r", pa,pb,pc);
*(pa+1) = 100; // modify the first time
printf("%d\n\r", *(pa+1));
*(pc-1) = 300;
printf("%x\t%x\t\n\r", (pa+1), (pc-1));
printf("%d\n\r", (pa+1) == (pc-1));
printf("%d\n\r", (pa+1) == pb);
printf("%d\t%d\t%d\t%d\n\r",a,*(pa+1),c,*(pb));
return 0;
}
用g++ test_const.cpp编译,用./a.out输出结果,代码输出为:在这里插入图片描述
我们可以发现,通过栈[2]上相邻的变量的地址做偏移,可以实现间接地对const修饰的变量进行修改,这种行为会意料之外的修改常量,非常的危险,我们的代码中需要检查涉及到指针偏移的操作,检查是否会出现越界的情况。
注意到,如果代码中的输出从:
printf("%d\t%d\t%d\t%d\n\r",a,*(pa+1),c,*(pb));
改成
printf("%d\t%d\t%d\t%d\n\r",a,*(pa+1),c,b);
结果是不一样的,后者的结果仍然是输出b = 9,那是因为C++在编译时对常量进行折叠,因此直接输出3这个立即数了,而不是从内存里面取出常量再输出,这一点要注意。Reference
[1]. https://blog.csdn.net/LoseInVain/article/details/103356324
[2]. https://blog.csdn.net/LoseInVain/article/details/103183829