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,
*/
}