当前位置: 首页 > news >正文

C++全局变量初始化流程详解

文章目录

  • 前言
  • 编译流程概述
    • 具体例子
  • 总结


前言

全局变量(包括static全局变量)的内存分配和初始化工作,是在main函数开始执行之前,由编译器、链接器和运行时库协同合作自动完成的。

编译流程概述

一个C++程序从源码到执行分为四个主要阶段:

  1. 预处理 (Preprocessing)

    • 处理内容: 处理以**#开头的预编译指令,如#include**(头文件展开)、#define(宏替换)、#ifdef(条件编译)等。
    • 输出: 一个纯粹的**.i.ii**文本文件,不含任何预编译指令。
  2. 编译 (Compilation)

    • 处理内容: 将预处理后的源代码进行词法分析、语法分析、语义分析、优化,最终生成对应目标平台的汇编代码
    • 输出.s汇编语言文件或直接生成**.o**目标文件。
  3. 汇编 (Assembly)

    • 处理内容: 将汇编代码翻译成机器指令,并生成目标文件(Object File),通常是.o.obj文件。目标文件包含了机器码、数据以及相关的元信息(符号表、重定位信息等)。
    • 关键点: 编译器会将在编译期已知初始值的全局/静态变量(如int g_var = 42;)放入目标文件的一个特定段(Section),通常是.data段。
  4. 链接 (Linking)

    • 处理内容: 将一个或多个目标文件(以及库文件)合并成一个最终的可执行文件(如.exe.out)。链接器的主要任务是符号解析(找到每个符号、变量、函数的定义)和重定位(根据符号的最终地址修正代码中的引用地址)。
    • 输出: 可执行文件。

关键阶段:在main之前发生了什么?
操作系统加载器(Loader)将可执行文件读入内存并开始执行时,最先运行的并不是你的main函数。它会先运行一段被称为启动例程(Startup Routine)运行时库(C Runtime Library, crt0) 的代码。这段代码是链接器在链接时悄悄添加到你的可执行文件开头的。

它的工作流程大致如下:

  1. 设置运行时环境: 初始化栈指针(SP)、帧指针(FP)等关键寄存器。
  2. 初始化静态数据这是最关键的一步!
  • 将来自可执行文件中.data段(已初始化的读写数据)的内容拷贝到对应的内存区域。
  • 将来自.bss段(未初始化或初始化为0的静态/全局数据)的对应内存区域清零。这就是为什么未初始化的全局变量默认是0。
  • 对于C++中更复杂的全局对象(如MyClass obj;),它们的构造函数也会在这一阶段被调用。
  1. 传递参数并调用main函数: 准备好argcargv参数,然后正式调用你的main函数。
  2. 处理main的返回值: 当main函数返回后,启动例程会使用其返回值作为参数调用exit函数,完成一些清理工作,最后通过系统调用结束进程。

具体例子

让我们用两个简单的例子来验证这个过程。

例子1:基础数据类型

// main.cpp
#include <iostream>int global_var = 42; // 已初始化的全局变量
static int static_global_var = 100; // 已初始化的static全局变量
int zero_var; // 未初始化的全局变量,默认在.bss段int main() {std::cout << "global_var: " << global_var << std::endl;std::cout << "static_global_var: " << static_global_var << std::endl;std::cout << "zero_var: " << zero_var << std::endl;return 0;
}

编译和流程分析:

  1. 编译/汇编: 编译器看到global_var = 42;和static_global_var = 100;,知道它们的初始值,于是将它们的位置和初始值信息都放入目标文件的.data段。zero_var没有初始值,被放入.bss段。

  2. 链接: 链接器将所有目标文件的.data段和.bss段合并到最终的可执行文件中。

  3. 运行前: 操作系统加载器将可执行文件加载到内存。启动例程(crt0)执行:

    • 将可执行文件中.data段的内容(42和100)拷贝到为global_var和static_global_var分配的内存地址上。
    • 将zero_var所在的内存区域清零
  4. 运行: 调用main函数。此时,所有全局变量都已经处于初始化后的状态,所以main可以直接使用它们。

例子2:C++全局对象

// main.cpp
#include <iostream>class MyClass {
public:MyClass(int x) : value(x) {std::cout << "MyClass Constructor called! Value: " << value << std::endl;}~MyClass() {std::cout << "MyClass Destructor called!" << std::endl;}int value;
};MyClass global_obj(100); // 全局对象int main() {std::cout << "main() function started." << std::endl;std::cout << "global_obj.value: " << global_obj.value << std::endl;return 0;
}

运行输出:

MyClass Constructor called! Value: 100
main() function started.
global_obj.value: 100
MyClass Destructor called!

分析
输出顺序完美证明了我们的理论。

  1. 在进入main函数之前,启动例程不仅为global_obj分配了内存,还调用了它的构造函数。这就是“运行”的体现——运行了构造函数代码。
  2. main函数正常执行。
  3. 在main函数返回、程序结束之后,启动例程还负责调用全局对象的析构函数进行清理。

总结

全局和static全局变量的内存分配和初始化过程(对于简单类型是拷贝/清零,对于C++对象还包括构造函数调用)由系统在main函数启动前自动完成的,而不是变量自己“运行”了

这个机制保证了程序员在进入main函数时,所有全局资源都已经处于一个确定的可用的状态。


文章转载自:

http://CD1TePZw.gmmyt.cn
http://Esj7NFEV.gmmyt.cn
http://MJQbZ7aj.gmmyt.cn
http://46TA3VI6.gmmyt.cn
http://JIAdHCoG.gmmyt.cn
http://d4Mw74p1.gmmyt.cn
http://vcRJsoS1.gmmyt.cn
http://bPZAGQBP.gmmyt.cn
http://V6mLX5bz.gmmyt.cn
http://AIcSIouf.gmmyt.cn
http://o9hlHH6g.gmmyt.cn
http://zJxaSQlr.gmmyt.cn
http://C9GLOrK2.gmmyt.cn
http://78uDq1NY.gmmyt.cn
http://6PtvL3cI.gmmyt.cn
http://WlXQHjbL.gmmyt.cn
http://pjmBP87E.gmmyt.cn
http://YLnwN9xX.gmmyt.cn
http://ZpqLaZ8X.gmmyt.cn
http://EbAGhsWp.gmmyt.cn
http://wQW6Kb59.gmmyt.cn
http://8GYoCtRq.gmmyt.cn
http://URtOTmtn.gmmyt.cn
http://UGs2EUaD.gmmyt.cn
http://08j7ipxV.gmmyt.cn
http://k61EhgFA.gmmyt.cn
http://N5al3d4p.gmmyt.cn
http://jxjE2tZT.gmmyt.cn
http://JXx5je7G.gmmyt.cn
http://eQFMAh3d.gmmyt.cn
http://www.dtcms.com/a/372662.html

相关文章:

  • Promise 实现原理:手写一个符合 Promises/A+ 规范的 Promise
  • vue3的选项式与组合式
  • 新增用户管理权,20+项功能优化更新,zyplayer-doc 2.5.2 发布啦!
  • 二叉树算法题——拆分自然数
  • Jakarta EE课程扩展阅读(一)
  • 【基于YOLO和Web的交通工具识别系统】
  • Python跳过可迭代对象前部元素完全指南:从基础到高并发系统实战
  • vue2(7)-单页应用程序路由
  • 布隆过滤器:快速判断某个元素是否存在
  • 信号衰减中的分贝到底是怎么回事
  • [光学原理与应用-461]:波动光学 - p光,s光; o光,e光;分别是什么意思,有什么关联?
  • python---静态方法和类方法
  • Nature子刊-香港大学研发新型陡峭山坡巡检无人机,破解密林细小障碍物规避难题
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘setuptools’问题
  • 基于 Django+Vue3 的 AI 海报生成平台开发(海报模块专项)
  • 机器学习-K-means聚类
  • 维度跃迁:当万物皆成电路,智能将从“拥有”变为“存在”
  • 前端:最新最全的JavaScript知识汇总,持续更新......
  • PO BAPI bapi_po_create1
  • 当前车载测试的难点分析
  • Pyhton基础之多继承、多态
  • AOSP Framework开发的一些超方便的快捷命令
  • 移动高清盒子CM311-5-内存大小区分参考指南
  • 在 VirtualBox 虚拟机中安装 Fedora CoreOS 操作系统
  • 【SLAM论文笔记】SplaTAM论文小结
  • shell编程之文本三剑客grep、sed、awk
  • 开始 ComfyUI 的 AI 绘图之旅-文生图(一)
  • 有哪些任务可以使用无监督的方式训练深度学习模型?
  • 从零开始构建图注意力网络:GAT算法原理与数值实现详解
  • FastAPI基础