V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iqoo
V2EX  ›  程序员

C 中 local static 变量初始化是零开销的吗

  •  
  •   iqoo · 2023-07-17 11:32:15 +08:00 · 1381 次点击
    这是一个创建于 474 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如下代码,第一次调用 fn 时会执行 get_value ,后续调用不会执行:

    void fn() {
        static int x = get_value();
        // ...
    }
    

    如果代码里显示调用该函数,编译器可能会优化,在第一次调用前初始化。

    但如果通过函数指针动态调用,或者将该函数对外导出,那么每次调用 fn 时是否都要判断一下有没有初始化?

    7 条回复    2023-07-17 14:22:46 +08:00
    BingoXuan
        1
    BingoXuan  
       2023-07-17 11:46:06 +08:00
    op 的例子编译出错,initializer element is not constant 。

    不如改成
    static int x;
    static int is_initialized = 0;

    if (!is_initialized) {
    x = get_value();
    is_initialized = 1;
    }
    Inn0Vat10n
        2
    Inn0Vat10n  
       2023-07-17 12:05:20 +08:00
    每次调用 fn 都有额外开销的,比较常见的场景是用这种方式实现的单例模式
    ?t=1143
    tool2d
        3
    tool2d  
       2023-07-17 12:11:20 +08:00
    @BingoXuan 我看 VC 反编译汇编代码,编译器有自动包含 is_initialized ,就在 static int x 内存地址的前面。

    如果调用了 get_value 后,这个匿名地址( u8 )会被赋值为 1 ,证明这个 x 已经被初始化过一次了。

    每次 fn 进入后,都会先判断这个地址是 0 (变量未初始化),还是 1 (已初始化)
    codehz
        4
    codehz  
       2023-07-17 13:53:38 +08:00
    @BingoXuan
    这个代码 C++才能用,编译器会确保即使多线程调用这个函数也只执行一次初始化,因此也被人拿来当作 lazy 用
    当然这里也会有生成 mutex 来做线程同步*(
    lakehylia
        5
    lakehylia  
       2023-07-17 14:11:40 +08:00
    @codehz Starting in C++11, a static local variable initialization is guaranteed to be thread-safe
    Shatyuka
        6
    Shatyuka  
       2023-07-17 14:13:11 +08:00
    “如果代码里显示调用该函数,编译器可能会优化,在第一次调用前初始化。” 举个例子?初始化成常量?
    8.8.4: Dynamic initialization of a block-scope variable with static storage duration (6.7.5.1) or thread storage
    duration (6.7.5.2) is performed the first time control passes through its declaration; such a variable is
    considered initialized upon the completion of its initialization.
    mogg
        7
    mogg  
       2023-07-17 14:22:46 +08:00   ❤️ 1
    1. static 对象的存储在程序开始时分配,并在程序结束时解分配
    2. 控制流首次经过变量声明时才会被初始化,之后所有调用都会跳过
    3. c++的话,c++11 之前这样不是线程安全的(即多个线程试图同时初始化),c++11 之后线程安全。c 的话不太确定
    4. 不确定你的“零开销” 是什么含义,c++里如果你的 get_value 是 consteval 的,会在编译期执行完,但是 c 里吗应该是在运行时执行一次
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1088 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 19:35 · PVG 03:35 · LAX 12:35 · JFK 15:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.