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

【静态初始化与动态初始化】基础介绍

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
      • 一、前置概念:静态存储期(Static Storage Duration)
      • 二、静态初始化(Static Initialization):编译期确定,程序启动前完成
        • 1. 定义
        • 2. 两类静态初始化(零初始化 → 常量初始化)
        • 3. 示例(各类静态存储期变量的静态初始化)
      • 三、动态初始化(Dynamic Initialization):运行期计算,时机分场景
        • 1. 定义
        • 2. 动态初始化的触发时机(核心区分)
        • 3. 示例(各类静态存储期变量的动态初始化)
      • 四、关键误区纠正
        • 1. 动态初始化 ≠ 仅局部静态变量
        • 2. 初始化时机 ≠ 初始化类型
      • 五、补充规则(结合 C++17 inline 静态成员)
        • 1. constexpr 静态成员必然是静态初始化
        • 2. 零初始化的兜底作用
        • 3. 静态初始化顺序问题(SSIO)
      • 六、核心总结表格

前言

问题来源

去年也碰到过这个静态初始化与动态初始化,当时看几篇文章也一知半解
今天再记录下


程序启动前的初始化就属于静态初始化,如全局变量、全局静态变量、类的静态成员变量,第一次使用前的初始化就叫作动态初始化如局部静态变量,是这样吗

要彻底理解「静态初始化」和「动态初始化」,首先需要纠正你的核心误区:这两个概念的划分依据是「初始化值是否能在编译期确定」,而非变量的存储位置(全局/类静态/局部静态)或初始化时机;「静态存储期」(全局、类静态、局部静态变量的共同属性)是变量的生命周期特征,和「静态初始化」是完全不同的概念。

下面从「核心定义」「分类详解」「误区纠正」「实战示例」四个维度展开,结合 C++17 inline 静态成员场景讲透:

一、前置概念:静态存储期(Static Storage Duration)

先明确范围:所有属于「静态存储期」的变量,生命周期从程序启动程序结束(存储在数据段.data/.bss),包括:

  1. 命名空间作用域的全局变量(如int global_val = 10;);
  2. 类的静态成员变量(如class Test { inline static int val = 20; };);
  3. 函数内的局部静态变量(如void func() { static int local_val = 30; });
  4. namespace内的static变量(如namespace N { static int n_val = 40; })。

这些变量都可能是「静态初始化」或「动态初始化」—— 关键看初始化表达式的性质,而非变量属于哪一类。

二、静态初始化(Static Initialization):编译期确定,程序启动前完成

1. 定义

静态初始化是指:变量的初始化值能在编译/链接阶段完全确定(无需运行时计算),初始化在「程序启动后、main()执行前」的「静态初始化阶段」完成(是最早的初始化步骤)。

2. 两类静态初始化(零初始化 → 常量初始化)

所有静态存储期变量都会先经历「零初始化」,若有显式的常量表达式初始化,则覆盖为「常量初始化」(均属于静态初始化):

类型说明示例
零初始化(默认)编译器自动将变量初始化为“零值”(与类型相关),无显式初始化时兜底static int a;→ 初始化为 0
常量初始化(覆盖)显式初始化且表达式是「常量表达式(constant expression)」,覆盖零初始化static int b = 42;→ 42
3. 示例(各类静态存储期变量的静态初始化)
// 1. 全局静态变量(静态初始化)staticintglobal_static=10;// 常量表达式 → 常量初始化staticintglobal_uninit;// 无显式初始化 → 零初始化(值为0)// 2. 类inline静态成员(静态初始化,C++17)classTest{// constexpr隐式inline,常量表达式 → 静态初始化staticconstexprintmax_val=100;// inline + 常量表达式 → 静态初始化inlineconststaticdoublepi=3.14159;};// 3. 局部静态变量(静态初始化,易被误解为“动态”)voidfunc(){// 常量表达式 → 静态初始化(初始化在程序启动前完成,而非第一次调用func时)staticintlocal_static=30;}

三、动态初始化(Dynamic Initialization):运行期计算,时机分场景

1. 定义

动态初始化是指:变量的初始化值无法在编译期确定(初始化表达式不是常量表达式),需要运行时计算,初始化在「程序启动后」完成(具体时机分场景)。

2. 动态初始化的触发时机(核心区分)

这是你之前疑惑的“第一次使用前”的本质—— 动态初始化的时机分两种,而非“动态初始化=局部静态”:

变量类型动态初始化时机核心特点
非局部静态存储期变量main()执行前的「pre-main 阶段」跨编译单元初始化顺序未定义
局部静态存储期变量变量第一次被使用时(函数首次执行到该变量)C++11 后线程安全
3. 示例(各类静态存储期变量的动态初始化)
// 辅助函数:运行时才能确定值(非常量表达式)intget_runtime_val(){returnrand();// rand()是运行时函数,无法编译期确定}// 1. 全局静态变量(动态初始化:pre-main 阶段)staticintglobal_dyn=get_runtime_val();// 动态初始化(pre-main 阶段执行)// 2. 类inline静态成员(动态初始化:pre-main 阶段)classTest{// inline静态成员,初始化值非常量表达式 → 动态初始化inlinestaticintdyn_val=get_runtime_val();};// 3. 局部静态变量(动态初始化:第一次使用时)voidfunc(){// 第一次调用func()时初始化,后续调用复用已初始化的值(C++11后线程安全)staticintlocal_dyn=get_runtime_val();}

四、关键误区纠正

你之前的理解「程序启动前=静态初始化,第一次使用前=动态初始化(局部静态)」存在两个核心偏差:

1. 动态初始化 ≠ 仅局部静态变量

非局部静态变量(全局、类静态)也会有动态初始化,只是时机在main()前(pre-main 阶段),而非第一次使用时;
反过来,局部静态变量也可能是静态初始化(如static int a = 10;)—— 它的初始化值是常量表达式,因此仍在程序启动前完成,只是作用域局限于函数内。

2. 初始化时机 ≠ 初始化类型

「静态初始化/动态初始化」的核心是“值是否编译期确定”,而非“何时初始化”:

  • 静态初始化:必然在程序启动前完成(pre-main 阶段);
  • 动态初始化:可能在 pre-main 阶段(非局部)或第一次使用时(局部)完成。

五、补充规则(结合 C++17 inline 静态成员)

1. constexpr 静态成员必然是静态初始化

C++17 中constexpr static成员隐式inline,其初始化表达式必须是常量表达式,因此必然属于静态初始化,无需运行时计算:

classTest{staticconstexprintmax=100;// 静态初始化(编译期确定)};
2. 零初始化的兜底作用

即使静态存储期变量未显式初始化,编译器也会先执行零初始化(静态初始化的一种):

classTest{inlinestaticintuninit_val;// 零初始化→0(静态初始化)};
3. 静态初始化顺序问题(SSIO)

跨编译单元的非局部静态变量,动态初始化的顺序未定义,可能导致“未初始化访问”:

// file1.cppstaticinta=get_runtime_val();// 动态初始化(pre-main)// file2.cppstaticintb=a+1;// 危险:a可能未初始化(顺序不确定)

解决方法:用局部静态封装(Meyers单例),利用“第一次使用时初始化”的特性。

六、核心总结表格

维度静态初始化动态初始化
核心判断初始化表达式是常量表达式初始化表达式非常量表达式
计算时机编译/链接期运行期
执行时机程序启动前(pre-main)非局部:pre-main;局部:第一次使用
示例(inline静态)inline static constexpr int a=10;inline static int b=rand();
线程安全无(pre-main 单线程)局部静态:C++11后线程安全;非局部:不安全

简言之:

  • 只要初始化值能“写死在编译产物里”,就是静态初始化(程序启动前完成);
  • 只要需要运行时计算(如调用函数、读取运行时状态),就是动态初始化(时机分非局部/局部);
  • C++17 inline 静态成员只是简化了定义语法,其初始化类型(静态/动态)仍遵循上述规则。

http://www.jsqmd.com/news/184255/

相关文章:

  • AUTOSAR OS入门完整指南:从配置到运行
  • Sonic能否用于身份冒充?技术本身中立但需防范滥用
  • 从零实现有源蜂鸣器和无源区分功能测试
  • Sonic在公益领域的应用案例:为听障人士生成手语翻译
  • Sonic能否驱动虚拟偶像演唱会?离线渲染+后期合成可行
  • 人类能分辨Sonic视频真假吗?盲测实验结果显示85%识破
  • Sonic生成宠物拟人化视频?虽不精准但趣味性强
  • Sonic与Dify结合使用?构建企业知识库问答数字人助手
  • 提升真实感技巧:添加微表情与随机头部轻微晃动
  • 如何清理Sonic缓存文件?释放磁盘空间的小技巧
  • 腾讯联合浙大推出Sonic数字人口型同步技术,支持音频+图片驱动
  • Java SpringBoot+Vue3+MyBatis 研究生调研管理系统系统源码|前后端分离+MySQL数据库
  • motion_scale控制在1.0-1.1,避免Sonic动作僵硬或夸张
  • Conda环境安装Sonic依赖包:避免版本冲突问题
  • 大面积冷板在高功率芯片散热中的热阻表现
  • 长时间运行Sonic服务崩溃?建议定期重启防内存泄漏
  • Sonic能否理解所说的内容?仅为语音驱动无语义认知
  • PCB原理图与硬件接口设计:完整指南
  • Star一下再下载?鼓励用户支持Sonic持续开发
  • LTspice电源稳压电路仿真:从零实现完整示例
  • YouTube创作者使用Sonic注意事项:避免违反社区准则
  • TFT-LCD垂直同步与撕裂效应解决方案
  • 介绍 tmap 用于可视化和数据分析
  • Java Web 药品管理系统系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】
  • GDPR合规性考量:Sonic在欧洲使用的法律适应性
  • Proteus仿真软件助力高校电类课程改革:项目应用
  • 介绍单变量样本推荐系统:如何在一个向量中描述客户行为
  • 小镜AI开放平台:Sora 2 API 低价高并发解决方案评测整理
  • STM32低功耗模式下运行ModbusRTU的实践方法
  • 多路复用select