在c语言中,用const修饰的变量的值默认不可修改,
const局部变量其在内存中分配的地址空间与普通局部变量一样,分配在栈区,(所以const局部变量可以使用指针修改该变量所在地址的值)。
const全局变量其在内存中分配的地址空间在文字常量区,该内存区域的只读属性保证了const全局变量不可被修改(所以const全局变量不可使用指针直接修改其所在地址的值,编译可以通过,但是运行时会报段错误)。
在c++中,用const修饰的变量的值默认不可修改,
在c++中编译器会对const变量进行优化,把它放到符号表中,不为其分配存储空间,在编译时进行类似于宏定义的操作,在出现const变量的地方在编译时就替换成其对应的值,所以这里是不会为它分配内存的,只有对const变量使用extern关键字或者取地址操作时才对其分配内存。
const局部变量使用extern关键字或者取地址操作后分配的地址空间与普通局部变量一样,分配在栈区,(所以const局部变量可以使用指针修改该变量所在地址的值,由于在编译时出现const变量的地方都已经替换成其对应的值了,所以即使改变了地址的值,在最后输出const变量的值依然是改变前的,只是const变量所在地址的值改变了)
例子:
#include <iostream>
using namespace std;
int main()
{
const int a=1;
int *p=(int*)&a; //这里编译器为a在栈区分配了内存空间
*p=2;
cout<<"*p="<<*p<<endl<<"a="<<a<<endl; //这里其实在编译时已经使用1替换了a了,相当于cout<<"*p="<<*p<<endl<<"a="<<1<<endl,所以a输出的> 值不变,输出依然是1。
return 0;
}
输出结果:
*p=2
a=1
这里可以使用volatile关键字告诉编译器a是随时可能发生变化的,每次使用它的时候必须从内存中取出a的值,因而编译器生成的汇编代码会重新从a的地址处读取数据用于输出。
代码可以改为如下样子,则a可以输出改变后的值:
<span style="font-size:12px;"><span style="color:#666666;">#include <iostream>
using namespace std;
int main()
{
</span><span style="color:#ff0000;"> volatile const int a=1;</span><span style="color:#666666;">
int *p=(int*)&a; //这里编译器为a在栈区分配了内存空间
*p=2;
cout<<"*p="<<*p<<endl<<"a="<<a<<endl; //这里其实在编译时已经使用1替换了a了,相当于cout<<"*p="<<*p<<endl<<"a="<<1<<endl,所以a输出的> 值不变,输出依然是1。
return 0;
}</span></span>
输出结果:
<span style="font-size:12px;">*p=2
a=2</span>
const全局变量使用extern关键字或者取地址操作后分配的地址空间在文字常量区,该内存区域的只读属性保证了const全局变量不可被修改(所以const全局变量不可使用指针直接修改其所在地址的值,编译可以通过,但是运行时会报段错误)。
const变量与define宏定义的区别
(1) 编译器处理方式不同
define宏是在预处理阶段展开。
const变量在编译时确定其值。
(2) 类型和安全检查不同
define宏没有类型,不做任何类型检查,仅仅是展开。
const变量有具体的类型,在编译阶段会执行类型检查。
(3) 存储方式不同
define宏仅仅是展开,有多少地方使用,就展开多少次,宏本身不会分配内存,宏展开后的立即数是会占用内存的。(展开多少次就分配多少次内存)
编译器通常不为普通const变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,编译时对其进行类似于宏定义的操作,在出现const变量的地方就替换成其对应的值,没有了存储与读内存的操作,使得它的效率也很高。只有在对const变量使用extern关键字或者取地址操作时才会为它分配存储空间,且const变量从汇编的角度来看,操作const变量时只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const变量在程序运行过程中只有一份拷贝,不会多次分配存储空间。