1、什么叫常量
不可以写的叫常量
常量是一个恒定不变的值,它在内存中也是不可修改的。在程序中出现的1、2、3这样的数字或“Hello”这样的字符串,以及数组名称,都属于常量,程序在运行中不可修改这类数据。
——《C++反汇编与逆向分析技术揭秘》
常量被编译进可执行文件中,进程启动后,加载进内存。
2、定义方式
a、#define
b、const
#define修饰的符号名称是一个真量数值,而const修饰的栈常量,是一个“假”常量。在实际中,使用const定义的栈变量,最终还是一个变量,只是在编译期间对语法进行了检查,发现代码有对const修饰的变量存在直接修改行为则报错。
——《C++反汇编与逆向分析技术揭秘》
const 修饰的变量,根据实测,要区分是局部变量还是全局变量。如下图所示全局变量g_val,虽然我们通过指针转换,绕过了编译器检查,但实际在写入操作时,引发异常。
而,如果是局部的const变量,则可以看出,我们对a值修改,已经成功。
查看内存,也可以看到,const 定义的局部、全局变量地址并不相同。根据作用域决定其内存位置和属性。
3、编译器对const修饰的变量做了什么?
编译器检查修饰的变量是否被修改,并且由编译器来限制修改。但是可以被绕过,如之前给的两个图所示。
4、const修饰的内容可以不初始化吗
const修饰的内容可以不初始化吗? —— 不可以。const对象一旦"创建"后其值就不能再改变,所以const对象必须初始化。
先看一个例子:
我们定义未初始化的int变量g_val_0、b,以及指针变量g_ptr_2、p2 —— 编译错误。
但是为什么g_ptr_1(与g_ptr_2 一模一样,只是想说明不要被*与谁挨在一起影响)、p1没初始化就可以?
运行结果:ok
我们先理解创建:就是要分配内存。
const int *p;
const int* p;
a、const 修饰的是 *p
b、p是可以变的,*p是不能变的
c、p 是一个指针,指向的是一个(const int)类型的变量
d、此时,我们只是定义了一个指针p,而不是const对象p。而 const int val;此时就定义/创建了一个const 对象val,类型为int,而创建时没有初始化,因此报错。
可以看到,我们定义了一个指针 p1(没有被const修饰,const修饰的是 *p1),这个指针已经有申请了内存来放置“0x000000919d4ff888”,但是此时这个指向const int类型的指针,并没有初始化,去绑定到一个const int类型的对象。而p2,是一个const 指针,直接被const修饰,因此p2在创建的时候(分配在0x000000919d4ff8a8)必须给个值,也就是&val;
再从汇编的角度来理解:
我丑丑的画了一下:
可以看到,代码5-6行并没有分配内存,而是在后面8、9行赋值时,才在栈上创建出内存,并初始化。
其实const 的位置只有如下几种,我们来填空并举例:
可以看到,只有一个区分,const是不是挨着变量p。如果挨着变量p,修饰的p就是必须初始化,且p指向的位置不能变化。const 不挨着p的话,修饰的是“*p”,p的指向是可以变的,*p是不能变的。
5、const 和 static 能合起来用吗?
二者其实是在不同维度来描述属性:
-
const 是只读,创建变量必须初始化
-
static 限制作用域,在内存中的位置不同
从属性上看,二者是不矛盾的,按理也是可以合起来用的。但是对于C++ 函数,要具体分析