const修饰符并不保证运行时的常数性质

∇ \nabla 联系方式:

e-mail: FesianXu@gmail.com

QQ: 973926198

github: https://github.com/FesianXu

知乎专栏: 计算机视觉/计算机图形理论与应用

微信公众号


constvolatile修饰符统称为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这个立即数了,而不是从内存里面取出常量再输出,这一点要注意。

PS: 注意,有些编译器中,依次定义的变量a,b,c的地址是降序的,而在有些编译器中是升序的,可以用以下代码检测下。如果是降序的,那么以上测试例子里面需要对地址操作进行取反,把+变为-,把-变为+

	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);

评论区有个朋友提到:

FX,const int b = 9;这个b是放在常量区,不可被修改。const int* pb = &b;这个中pb的指向是可以修改的,你间接修改的是pb的指向,不是常量b,我觉得是这样的

其实这个和编译器有关,如果你用g++,那么以下代码的输出是不一致的,b还是原值,*(pb)是修改后的值,这是因为c++会对const进行常量折叠,导致尝试修改const变量的地址这个行为失效;然而如果你采用gcc进行编译,那么以下输出是一致的,因为C语言的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);

Reference

[1]. https://blog.csdn.net/LoseInVain/article/details/103356324
[2]. https://blog.csdn.net/LoseInVain/article/details/103183829

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FesianXu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值