c++ 不要假设连续定义的局部变量在栈中的地址是连续的

本文通过一个实例探讨了C++中连续定义的局部变量在栈上的地址可能不连续的情况,指出这与编译器设计和选项有关,标准并未规定顺序,并提醒程序员不应过分依赖这种假设。

突然想测一测电脑数据在内存中存放模式是小端的还是大端的,而且感觉自己对指针的掌握已经独步天下......

于是乎,挥笔写下如下程序:

int main()
{
     char a = 0;
     char b = 0;
     char c = 1;
     char d = 0;
     int * p = (int *)&d;
     printf("%d\n", *p);

/*
     printf("%d\n", &a);
     printf("%d\n", &b);
     printf("%d\n", &c);
     printf("%d\n", &d);
*/
     return 0;
}

咦?输出竟然不是256(小端)或者65536(大端)

在加入了注释的代码后,又神奇地正常显示256了!

什么鬼?难道printf还能改变变量的值不成?

a,b,c,d这四个变量不是在栈中连续排列的吗?地址不是应该连续的吗?为什么转为整型时出bug了?

反汇编看了一遍:

注释时是这样的:

0x0040134E	mov    BYTE PTR [esp+0x1f],0x0    //char a;
0x00401353	mov    BYTE PTR [esp+0x1e],0x0    //char b;
0x00401358	mov    BYTE PTR [esp+0x1d],0x1    //char c;
0x0040135D	mov    BYTE PTR [esp+0x17],0x0    //char d;
0x00401362	lea    eax,[esp+0x17]
0x00401366	mov    DWORD PTR [esp+0x18],eax   //int * p = (int *)&d;

取消注释时是这样的:

0x0040134E	mov    BYTE PTR [esp+0x1b],0x0
0x00401353	mov    BYTE PTR [esp+0x1a],0x0
0x00401358	mov    BYTE PTR [esp+0x19],0x1
0x0040135D	mov    BYTE PTR [esp+0x18],0x0
0x00401362	lea    eax,[esp+0x18]
0x00401366	mov    DWORD PTR [esp+0x1c],eax

excuse me ?

下面那个显示正常,上面那个直接错了,原因在于按顺序定义的变量在栈中竟然是不一定按顺序排的!

和编译器的设计和选项有关,标准并没有规定一定要按顺序!网传debug模式下,变量两次都会插入特殊变量

结论:虽然强制转型+指针有无穷的威力,但你永远不知编译器还干了什么......

<think>我们正在讨论C++中全局变量的生命周期和作用范围。根据引用内容,我们可以总结如下:1.**生命周期**:全局变量的生命周期从程序启动开始,到程序结束为止(引用[3])。这意味着全局变量在程序运行期间始终存在。2.**作用范围**:全局变量的作用范围是从其定义点开始,到文件结束(引用[1])。如果要在其他文件中使用,需要使用`extern`关键字进行声明(引用[2]和引用[4])。3.**初始化**:-全局变量会被默认初始化为零值(0、false、nullptr等)(引用[3])。-如果显式初始化,则在程序启动时(在进入main函数之前)进行初始化。4.**存储位置**:全局变量存储在静态存储区(StaticMemory)(引用[3])。另外,注意在同一个文件中,如果局部变量与全局变量同名,则局部变量会屏蔽全局变量(引用[1]中的例子:在main函数内部定义的`num`屏蔽了全局的`num`)。下面我们通过一个例子来详细说明:假设我们有两个文件:`global.cpp`和`main.cpp`。**global.cpp**:```cpp//定义一个全局变量,作用范围从定义处开始到文件结束intglobalVar=42;//显式初始化//另一个全局变量,没有显式初始化,将被初始化为0intdefaultInitGlobalVar;```**main.cpp**:```cpp#include<iostream>usingnamespacestd;//声明globalVar,告诉编译器在另一个文件中有定义externintglobalVar;externintdefaultInitGlobalVar;//同样声明另一个全局变量intmain(){cout<<"globalVar:"<<globalVar<<endl;//输出42cout<<"defaultInitGlobalVar:"<<defaultInitGlobalVar<<endl;//输出0return0;}```在这个例子中,`globalVar`和`defaultInitGlobalVar`都是在`global.cpp`中定义的全局变量。在`main.cpp`中通过`extern`声明后,就可以使用它们。**关于初始化顺序的注意事项**:不同编译单元(即不同.cpp文件)中的全局变量的初始化顺序是不确定的。因此,如果一个全局变量依赖于另一个编译单元中的全局变量,可能会导致问题(因为无法保证依赖的全局变量已经初始化)。总结:-生命周期:整个程序运行期间。-作用范围:定义它的文件内部(从定义处开始到文件结束);其他文件通过`extern`声明后可以访问。-初始化:默认初始化为零值,或者按照显式初始化进行,初始化在main函数执行之前完成。相关问题:1.C++中全局变量的初始化顺序问题如何解决?2.全局变量和静态局部变量在存储上有什么区别?3.如何避免不同文件中全局变量之间的依赖问题?</think>### C++ 全局变量在 cpp 文件中的生命周期及初始化过程 在 C++ 中,全局变量(在所有函数外部定义的变量)具有特定的生命周期和作用范围规则: #### 1. 生命周期 - **起始点**:从程序启动时开始(在 `main()` 函数执行之前) - **结束点**:到程序完全终止时结束 - 全局变量存储在**静态存储区**(Static Memory),其生命周期覆盖整个程序运行期间[^3]。 #### 2. 作用范围 - **文件内部**:从定义位置开始,到文件结束均可访问 - **跨文件访问**:需使用 `extern` 关键字声明(示例): ```cpp // File1.cpp int globalVar = 10; // 定义全局变量 // File2.cpp extern int globalVar; // 声明外部全局变量 void func() { cout << globalVar; } ``` - 若局部变量与全局变量同名,局部变量会**屏蔽**全局变量(作用域覆盖)[^1]。 #### 3. 初始化过程 - **默认初始化**:未显式初始化时,自动初始化为零值(0/false/nullptr)[^3] - **显式初始化**:支持常量或常量表达式初始化(不支持运行时表达式): ```cpp const int MAX = 100; int arr[MAX]; // 合法 ``` - **初始化时机**:在 `main()` 函数执行前完成初始化 - **初始化顺序**:同一文件内按定义顺序初始化,跨文件顺序**不确定** #### 4. 内存特性 | 特性 | 说明 | |--------------|-----------------------------| | 存储位置 | 静态存储区(非/堆)[^3] | | 内存分配时机 | 编译期确定地址,启动时初始化 | | 持久性 | 值在程序生命周期内持续有效 | #### 关键注意事项 1. **跨文件共享风险**:不同文件中的全局变量初始化顺序不可控,需避免相互依赖 2. **命名冲突**:建议使用命名空间管理全局变量 3. **替代方案**:优先考虑单例模式或静态局部变量(需要时才初始化) > 示例代码: > ```cpp > // global.cpp > int globalCount = 0; // 显式初始化 > > // main.cpp > extern int globalCount; > int main() { > globalCount++; // 跨文件修改 > int globalCount = 5; // 局部变量屏蔽全局变量 > cout << ::globalCount; // 使用作用域运算符访问 > } > ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值