C++ 全局常量 静态常量 常量存储区 只读

 c++全局变量与静态变量_wusimpl的博客-CSDN博客_c++静态全局变量 
C++常识之——C++中堆和栈的区别,自由存储区、全局/静态存储区和常量存储区 - likebeta 
const变量通过指针修改问题_Supaopao的博客-CSDN博客 
对栈区、堆区、bss区、代码区的理解jikuo_wang_vast_wang的博客-CSDN博客_bss区

C规定,未初始化变量的初值为0,这个清0的操作是由启动代码完成的,还有已初始化变量的初值的设置,也是由启动代码完成的。
为了启动代码的简单化,编译链接器会把已初始化的变量放在同一个段:.data,这个段的映像(包含了各个变量的初值)保存在“只读数据段”,这样启动代码就可以简单地复制这个映像到 .data 段,所有的已初始化变量就都初始化了。
而未初始化变量也放在同一个段:.bss,启动代码简单地调用 memset 就可以把所有未初始化变量都清0。

C++ 程序内存空间分为:代码存储区、堆、栈、全局/静态存储区(全局变量和静态变量存储区)、常量存储区。在VS2015中上图中的3和4合并在一起了,可能是在程序预处理时就直接将未初始化变量赋值为0了。

简单数据类型的全局常量和静态常量(全局变量声明中含有const ,和局部变量声明中含有static const),编译器会给常量分配常量存储区的内存空间,该内存空间是只读的,所以对应的常量也是只读的。

对于复杂数据类型,如果数据类型sizeof(type)大于1(也就是类型中有非静态数据成员或者虚表没,注意复杂数据类型中的静态数据成员不计入sizeof 中),则编译器会为他们的全局常量和静态常量分配 全局/静态存储区 的内存空间。


下面测试使用(使用vs2015 x86)

struct VV {};

struct NN {
	const int a = 0;
};

struct MM
{
	static int a;
};
int MM::a = 0;

struct ZZ {
	int a;
	int b;
};

//下面几种方式声明全局常量和静态常量,编译器会为一些常量名赋予常量存储区的内存空间,常量存储区的内存空间是只读的,所以全局常量和静态常量是只读的。
class TT {
public:
	static const int t0 = 10;  //类内静态常量(只读)
};
//const int TT::t0 = 0; //静态常量初始化,必须初始化,否则编译器会报错
const int t1 = 5;//全局常量
const volatile int t2 = 4;//全局常量(volatile已经失去了意义)
static const int t3 = 9;//全局静态常量(只读)
static const volatile int t4 = 8;//全局静态常量(只读,volatile已经失去了意义)
int t6 = 3; //普通全局变量
volatile int t7 = 10;//动态全局变量, 与普通全局变量一样分配 全局/静态存储区 内存空间。
int t8 = 10;//普通全局变量

const NN nn1;//全局常量

int main()
{
	static const int t5 = 10; //第六种方式声明只读变量。
	static int t9 = 10;
	const int t10 = 10;
	const int t11 = 10;
	const volatile int t12 = 10;
	int t13 = 10;
	int t14 = 10;
	int t15 = 10;
	double t16 = 10;
	double t17 = 10;

	int x = t1;
	x = t3;
	int tx = 2;
	x = tx;
	int *p1 = const_cast<int*>(&TT::t0);
	//*p1  = 2;//error。vs 编译器报错:写入位置 0x0098FDA8 时发生访问冲突
	int *p2 = const_cast<int*>(&t1);
	//*p2 = 2;//error 。vs 编译器报错:写入位置 0x0098FDA8 时发生访问冲突
	int *p3 = const_cast<int*>(&t2);
	//*p3 = 2;//error 。vs 编译器报错:写入位置 0x0098FDA8 时发生访问冲突
	int *p4 = const_cast<int*>(&t5);
	//*p3 = 2;//error 。vs 编译器报错:写入位置 0x0098FDA8 时发生访问冲突
/*反汇编:
	int x = t1;//t1为全局静态只读量,在这里编译器直接将t1替换成5。
	00D7851C  mov         dword ptr [x],5
	x = t6;
	00D78523  mov         eax,dword ptr ds:[00D85008h]
	00D78528  mov         dword ptr [x],eax
	int x1 = 2;
	00D7852B  mov         dword ptr [x1],2
	x = x1;
	00D78532  mov         eax,dword ptr [x1]
	00D78535  mov         dword ptr [x],eax

	int *p1 = const_cast<int*>(&TT::t);//虽然能将全局静态只读量的地址赋予给指针p1,但是不能通过*p1修改地址中的内容。因为该地址处的内存是只读的。
	000BB7DA  mov         dword ptr [p1],0BFDA8h
	int *p2 = const_cast<int*>(&t1);
	000BB7E1  mov         dword ptr [p2],0BFDB0h
	int *p3 = const_cast<int*>(&t2);
	000BB7E8  mov         dword ptr [p3],0BFDB4h
*/

	cout << *p1 << endl; //输出0
	cout << *p2 << endl; //输出5
	cout << *p3 << endl; //输出4

	static const TT tt0;// = { 4,5 };
	static const TT tt1;// = { 5,6 };
	static const VV vv0;
	static const NN nn0;
	static const ZZ zz0;
	static const MM mm0;
	static const ZZ zz1;
	//输出地址测试
	cout << "t0:" << &TT::t0 << endl;
	cout << "t1:" << &t1 << endl;
	int *p = const_cast<int*>(&t2);
	cout << "t2:" << p << endl;
	cout << "t3:" << &t3 << endl;
	p = const_cast<int*>(&t4);
	cout << "t4:" << p << endl;
	cout << "t5:" << &t5 << endl << endl;
	cout << "t6:" << &t6 << endl;
	p = const_cast<int*>(&t7);
	cout << "t7:" << p << endl;
	cout << "t8:" << &t8 << endl;
	cout << "t9:" << &t9 << endl << endl;
	cout << "t10:" << &t10 << endl;
	cout << "t11:" << &t11 << endl;
	p = const_cast<int*>(&t12);
	cout << "t12:" << p << endl;
	cout << "t13:" << &t13 << endl;
	cout << "t14:" << &t14 << endl;
	cout << "t15:" << &t15 << endl;
	cout << "t16:" << &t16 << endl;
	cout << "t17:" << &t17 << endl << endl;

	cout << "tt0:" << &tt0 << endl;
	cout << "tt1:" << &tt1 << endl;
	cout << "vv0:" << &vv0 << endl;
	cout << "nn0:" << &nn0 << endl;
	cout << "nn1:" << &nn1 << endl;
	cout << "zz0:" << &zz0 << endl;
	cout << "mm0:" << &mm0 << endl;
	cout << "zz1:" << &zz1 << endl;
	/*
	10
	5
	4
	t0:004F1D58  //常量数据存储空间 只读
	t1:004F25A8  //常量数据存储空间 只读
	t2:004F25AC
	t3:004F25B0
	t4:004F25B4
	t5:004F25B8

	t6:004F5000   //全局和静态数据存储空间  可读写
	t7:004F5004
	t8:004F5008
	t9:004F500C

	t10:010FF808   //数据栈空间 可读写
	t11:010FF7FC
	t12:010FF7F0
	t13:010FF7E4
	t14:010FF7D8
	t15:010FF7CC
	t16:010FF7BC
	t17:010FF7AC

	tt0:004F25BC  //常量数据存储空间  可读写
	tt1:004F25BD  //常量数据存储空间  可读写
	vv0:004F25BE  //常量数据存储空间 只读
	nn0:004F533C  //全局静态数据存储空间  可读写
	nn1:004F5300  //全局静态数据存储空间  可读写
	zz0:004F25C0  //常量数据存储空间 只读
	mm0:004F25BF  //常量数据存储空间 只读
	zz1:004F25C8  //常量数据存储空间 只读
	*/
	return 0;
}

struct ZZ {
	int a = 0;
	int b = 0;
};

struct CC {};

struct VV {
	void fun() {}
};

struct BB {
	virtual void fun(){}
};

//const volatile int t2 = 0;
struct NN {
	const int a = 0;
	void fun() {}
};

struct MM
{
	static int a ;
};
int MM::a = 0;


struct QQ
{
	const int a = 0;
	int b = 0;
};

struct WW
{
	static int a;
	int b = 0;
};
int WW::a = 0;


const ZZ zz0;
const CC cc0;
const VV vv0;
const BB bb0;
const NN nn0;
const MM mm0;
const QQ qq0;
const WW ww0;

const int t0 = 0;
int t1 = 0;

int main()
{
	const int t2 = 0;
	int t3 = 0;
	const ZZ zz1;
	const CC cc1;
	const VV vv1;
	const BB bb1;
	const NN nn1;
	const MM mm1;
	const QQ qq1;
	const WW ww1;
	static const ZZ zz2;
	static const CC cc2;
	static const VV vv2;
	static const BB bb2;
	static const NN nn2;
	static const MM mm2;
	static const QQ qq2;
	static const WW ww2;


	

	cout << "t0:" << &t0 <<" :全局常量"<< endl;
	cout << "t1:" << &t1 <<" :全局普通"<< endl;
	cout << "t2:" << &t2 <<" :栈"<< endl;
	cout << "t3:" << &t3 << endl;
	cout << "zz0:" << &zz0 << endl;
	cout << "cc0:" << &cc0 << endl;
	cout << "vv0:" << &vv0 << endl;
	cout << "bb0:" << &bb0 << endl;
	cout << "nn0:" << &nn0 << endl;
	cout << "mm0:" << &mm0 << endl;
	cout << "qq0:" << &qq0 << endl;
	cout << "ww0:" << &ww0 << endl;
	cout << "zz1:" << &zz1 << endl;
	cout << "cc1:" << &cc1 << endl;
	cout << "vv1:" << &vv1 << endl;
	cout << "bb1:" << &bb1 << endl;
	cout << "nn1:" << &nn1 << endl;
	cout << "mm1:" << &mm1 << endl;
	cout << "qq1:" << &qq1 << endl;
	cout << "ww1:" << &ww1 << endl;
	cout << "zz2:" << &zz2 << endl;
	cout << "cc2:" << &cc2 << endl;
	cout << "vv2:" << &vv2 << endl;
	cout << "bb2:" << &bb2 << endl;
	cout << "nn2:" << &nn2 << endl;
	cout << "mm2:" << &mm2 << endl;
	cout << "qq2:" << &qq2 << endl;
	cout << "ww2:" << &ww2 << endl;

	/*输出:
	t0:00991E44 :全局常量
	t1:0099571C :全局普通
	t2:00CFF800 :栈
	t3:00CFF7F4 :栈
	zz0:0099572C :全局普通
	cc0:00991E48 :全局常量
	vv0:00991E49 :全局常量
	bb0:00995728 :全局普通
	nn0:00995734 :全局普通
	mm0:00991E4A :全局常量
	qq0:00995738 :全局普通
	ww0:00995724 :全局普通
	zz1:00CFF7E4  :栈
	cc1:00CFF7DB  :栈
	vv1:00CFF7CF  :栈
	bb1:00CFF7C0  :栈
	nn1:00CFF7B4  :栈
	mm1:00CFF7AB  :栈
	qq1:00CFF798  :栈
	ww1:00CFF78C  :栈
	zz2:00995778 :全局普通
	cc2:00991E4B :全局常量
	vv2:00991E4C :全局常量
	bb2:00995784 :全局普通
	nn2:0099578C :全局普通
	mm2:00991E4D :全局常量
	qq2:00995794 :全局普通
	ww2:009957A0 :全局普通
	*/
    getchar();
    return 0;
}

局部变量中的const 只读变量如果未声明为volatile类型,vs会在编译阶段就将变量对应的值直接替换const变量。虽然程序依然会在栈中给const变量分配空间!!

const int a =10;
........
int t = a; //编译器会在编译阶段直接将a用10替换掉。不管中间对a所在的内存做任何修改,t不会知道修改后的内容。如果需要让t知道修改后的内容,需要这样:const volatile int a = 10;

const 与constexpr 区别:
C++11 constexpr和const的区别详解 
const 有只读变量 和 常量 的双重语义。为了区分开双重语义引入constexpr,constexpr用于专门表示常量的语义。

const int tt = 10; //const的常量语义
constexpr int tt0 = 10;

//void fun(constexpr int a) //error: vs 编辑器报错:参数的说明符无效
void fun(const int a)  //const的只读变量语义,
{
	cout << a << endl;
}

constexpr int funt(const int a) //constexpr声明返回值是常量,const 表变变量只读
{
	return a+a;
}

int main(){
    constexpr int tt1 = 10;
	//constexpr volatile int tt1_0 = 10;//error:“tt1_0”: 非法初始化了含非常量表达式的“constexpr”实体
	const int tt2 = 10; //const的常量语义
	const volatile int tt3 = 10;
	int *p1 = const_cast<int*>(&tt1);
	*p1 = 20;
	int *p2 = const_cast<int*>(&tt2);
	*p2 = 20;
	int *p3 = const_cast<int*>(&tt3);
	*p3 = 20;
	int xx1 = tt1;
	int xx2 = tt2;
	int xx3 = tt3;
/*反汇编:
	constexpr int tt1 = 10;
007D7AC8  mov         dword ptr [tt1],0Ah  
	//constexpr volatile int tt1_0 = 10;//error:“tt1_0”: 非法初始化了含非常量表达式的“constexpr”实体
	const int tt2 = 10;
007D7ACF  mov         dword ptr [tt2],0Ah  
	const volatile int tt3 = 10;
007D7AD6  mov         dword ptr [tt3],0Ah  
	int *p1 = const_cast<int*>(&tt1);
007D7ADD  lea         eax,[tt1]  
007D7AE0  mov         dword ptr [p1],eax  
	*p1 = 20;
007D7AE3  mov         eax,dword ptr [p1]  
007D7AE6  mov         dword ptr [eax],14h  
	int *p2 = const_cast<int*>(&tt2);
007D7AEC  lea         eax,[tt2]  
007D7AEF  mov         dword ptr [p2],eax  
	*p2 = 20;
007D7AF2  mov         eax,dword ptr [p2]  
007D7AF5  mov         dword ptr [eax],14h  
	int *p3 = const_cast<int*>(&tt3);
007D7AFB  lea         eax,[tt3]  
007D7AFE  mov         dword ptr [p3],eax  
	*p3 = 20;
007D7B01  mov         eax,dword ptr [p3]  
007D7B04  mov         dword ptr [eax],14h  
	int xx1 = tt1;
007D7B0A  mov         dword ptr [xx1],0Ah  //注意这里直接将0Ah赋值给xx1,相当于tt1在编译时就被0Ah替换掉了。
	int xx2 = tt2;
007D7B11  mov         dword ptr [xx2],0Ah  //注意这里直接将0Ah赋值给xx2,相当于tt2在编译时就被0Ah替换掉了。
	int xx3 = tt3;
007D7B18  mov         eax,dword ptr [tt3]  //因为tt3是volatile类型,tt3访问内存过程被禁止优化,没有在编译过程中将tt3用0Ah替换。
007D7B1B  mov         dword ptr [xx3],eax  
*/

    cout << &tt << endl;
	cout << &tt0 << endl;
	cout << &tt1 << endl;
	cout <<(void*)  &tt2 << endl;
	cout << xx << endl;
	cout << xx1 << endl;
	cout << *p << "  " << tt1 << endl;
	cout << *p1 << "  " << tt2 << endl;

    int xa = 2;
	int a[funt(2)] = { 10,20,30,40 };
	for_each(a, a + 4, [](int a) {cout << a << ","; }); 
	int xa1 = funt(xa);
	constexpr int xa2 = funt(2);//此处使用了constexpr 的常量语义。
	int aa[xa2] = { 10,20,30,40 };
    const int xa3 = funt(2);//也可以初始化数组。此处使用了const 的常量语义。
	int ab[xa3] = { 10,20,30,40 };

//vs编辑器报错:函数调用在常量表达式中必须具有常量值
//vs编译器报错:表达式的计算结果不是常数
    //int a[funt(ax)] = {10,20,30,40};

	int a[funt(2)] = {10,20,30,40};//说明funt[2]在编译过程就能获取到结果
/*反汇编:
	int xa = 2;
002E8350  mov         dword ptr [xa],2  
	int a[funt(2)] = { 10,20,30,40 }; //并没有函数调用过程,说明编译阶段就已经计算出funt(2)的值了。
002E8357  mov         dword ptr [a],0Ah  
002E8361  mov         dword ptr [ebp-8Ch],14h  
002E836B  mov         dword ptr [ebp-88h],1Eh  
002E8375  mov         dword ptr [ebp-84h],28h  
	for_each(a, a + 4, [](int a) {cout << a << ","; }); 
002E837F  xor         eax,eax  
002E8381  mov         byte ptr [ebp-1ADh],al  
002E8387  movzx       ecx,byte ptr [ebp-1ADh]  
002E838E  push        ecx  
002E838F  lea         edx,[ebp-80h]  
002E8392  push        edx  
002E8393  lea         eax,[a]  
002E8399  push        eax  
002E839A  lea         ecx,[ebp-1B9h]  
002E83A0  push        ecx  
002E83A1  call        std::for_each<int *,<lambda_78a7621cf94db7eec68ce48a3baa3026> > (02E173Ah)  
002E83A6  add         esp,10h  
	int xa1 = funt(xa); //有调用函数过程
002E83A9  mov         eax,dword ptr [xa]  
002E83AC  push        eax  
002E83AD  call        funt (02E10B9h)  
002E83B2  add         esp,4  
002E83B5  mov         dword ptr [xa1],eax  
	constexpr int xa2 = funt(2);//有调用函数过程
002E83BB  push        2  
002E83BD  call        funt (02E10B9h)  
002E83C2  add         esp,4  
002E83C5  mov         dword ptr [xa2],eax  
	int aa[xa2] = { 10,20,30,40 };正常初始化aa。
002E83CB  mov         dword ptr [aa],0Ah  
002E83D5  mov         dword ptr [ebp-0BCh],14h  
002E83DF  mov         dword ptr [ebp-0B8h],1Eh  
002E83E9  mov         dword ptr [ebp-0B4h],28h  
*/

/*输出:
007E0C40//全局常量存储区
007E0C44//全局常量存储区
006FFB7C//栈空间
006FFB70
006FFB64
20  10  10
20  10  10
20  20  20
10,20,30,40,
*/
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值