徐土豆
认证:优质创作者
所在专题目录 查看专题
c语言运行时出现segment fault的原因
一文理解C语言中的volatile修饰符
C语言中的内存布局(memory layout)
do{}while(false)结构的妙用
const修饰符并不保证运行时的常数性质
引用与指针的区别
作者动态 更多
给定计算预算下的最佳LLM模型尺寸与预训练数据量分配
05-19 09:33
大模型推理时的尺度扩展定律
05-18 10:32
世界多胞体与世界模型
05-13 09:42
奖励模型中的尺度扩展定律和奖励劫持
05-12 08:41
MeCo——给预训练数据增加源信息,就能减少33%的训练量并且提升效果
05-08 09:13

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

本文转自徐飞翔的“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

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 2
收藏 2
关注 52
成为作者 赚取收益
全部留言
0/200
  • dy-J4n9lg5Q 2021-05-19 13:29
    思路清晰,受益匪浅
    回复