C++ static 关键字 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hackpro
V2EX    C

C++ static 关键字

  •  
  •   hackpro 2019-06-04 16:55:24 +08:00 3346 次点击
    这是一个创建于 2375 天前的主题,其中的信息可能已经有所发展或是发生改变。

    标准里说对于 function 里面 static 变量只说了两点:

    1. 分配在静态存储区
    2. 第一次遇到的时候进行初始化,之后每次跳过。

    Variables declared at block scope with the specifier static have static storage duration but are initialized the first time control passesthrough their declaration (unless their initialization is zero- or constant-initialization, which can be performed before the block is first entered). On all further calls, the declaration is skipped.

    https://en.cppreference.com/w/cpp/language/storage_duration

    比如

    int f() { static int a = 0; } int main() { f(); std::cout << "---------------" << std::endl; f(); return 0; } 
    1. 程序起来,静态存储区创建 int a; 此时并未运行到 f(),这时静态存储区的值 a 是否 undefined?还是说类似全局变量初始化,数值类型给 0 值,字符串给空?
    2. 第一次进入 f()运行到 static int a = 0 时,静态存储区的 a 被初始化为 0 ?之后每次进入 f()这个语句直接被忽略掉?编译器是如何实现的?类似于一个 loop ?初始值 cnt=1,用完 cnt--,下次 check 值是否为 0 进行跳转?
    11 条回复    2019-06-05 01:56:06 +08:00
    zycpp
        1
    zycpp  
       2019-06-04 17:31:54 +08:00 via iPhone
    #include <iostream>

    class A{
    public:
    A(){printf("create\n");}
    ~A(){printf("destroy\n");}
    };

    void f(){
    static A a;
    }

    int main(){
    printf("before call f\n");
    f();
    printf("after call f\n");
    f();
    printf("the end\n");
    return 0;
    }
    wutiantong
        2
    wutiantong  
       2019-06-04 17:45:04 +08:00 via iPhone
    问题 1,如果你没运行到 f,a 是不存在的(未分配内存),更不必讨论初始化。这点你在引文里其实已经提到了。

    编译器如何实现的我也不清楚,但一定不是你说的这样
    Akiyu
        3
    Akiyu  
       2019-06-04 17:57:49 +08:00
    你分析汇编就知道了
    static 具有线程安全性, 因为访问的时候会加锁
    我记得我写过笔记, 我找找啊 = =
    yianing
        4
    yianing  
       2019-06-04 18:10:18 +08:00 via Android
    static 变量在程序装载到内存时就会被初始化,有初值的赋初值,没有的是 0,程序员的自我修养书上有
    Akiyu
        5
    Akiyu  
       2019-06-04 18:19:58 +08:00
    找不到了, 直接分析一下吧 = =

    原始代码 :
    void func(int a) {
    static int i = a;
    cout << i << endl;
    i++;
    }

    汇编代码:
    -- 这是调用的:
    movl $100, %edi
    call _Z4funci
    -- 这是 func 函数的:
    _Z4funci:
    .LFB3222:
    pushq %rbp
    movq %rsp, %rbp
    subq $16, %rsp
    movl %edi, -4(%rbp) // 参数入栈
    movl $_ZGVZ4funciE1i, %eax
    movzbl (%rax), %eax // 这里就是某个标志位, 标识了静态变量是否被初始化了, 当翻译成机器码时, 这是一个绝对地址
    testb %al, %al // 检测某个位
    jne.L2
    movl $_ZGVZ4funciE1i, %edi
    call __cxa_guard_acquire // 关键字 guard_acquire, 获取锁
    testl %eax, %eax
    setne %al
    testb %al, %al
    je .L2
    movl -4(%rbp), %eax // 参数放入寄存器 eax
    movl %eax, _ZZ4funciE1i(%rip) // 经过前面的检测后, 这里才会设置值
    movl $_ZGVZ4funciE1i, %edi
    call __cxa_guard_release // 关键字 guard_release, 释放锁
    .L2:
    movl _ZZ4funciE1i(%rip), %eax // 这里是直接使用的, 前面的 jne 会跳转到这里
    movl %eax, %esi
    movl $_ZSt4cout, %edi
    call _ZNSolsEi
    movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, %esi
    ovq %rax, %rdi
    call _ZNSolsEPFRSoS_E
    movl _ZZ4funciE1i(%rip), %eax
    addl $1, %eax
    movl %eax, _ZZ4funciE1i(%rip)
    leave
    ret

    简单来说, 内存中有一个绝对地址(由编译器提供并维护)的某个位, 它标志了静态变量是否被初始化
    然后如果未初始化, 先会获得锁, 初始化, 设置完后再释放锁
    Akiyu
        6
    Akiyu  
       2019-06-04 18:20:54 +08:00
    当然, 这视编译器而异
    有些的实现是直接初始化了
    sky2017
        7
    sky2017  
       2019-06-04 19:11:33 +08:00
    局部 static 变量是第一次执行到的时候才初始化,并且存在线程安全问题
    zmj1316
        8
    zmj1316  
       2019-06-04 19:20:52 +08:00 via Android
    @Akiyu 好像是 c++ 11 开始才是线程安全的
    Akiyu
        9
    Akiyu  
       2019-06-04 19:33:44 +08:00
    @zmj1316
    这个我就不太清楚了, 我直接学的 C++ 11
    zzjas98
        10
    zzjas98  
       2019-06-05 01:37:04 +08:00
    Q1: static 的东西会被存到 bss 去,看下这里可能有帮助 https://en.wikipedia.org/wiki/.bss

    TL;DR
    “ On some platforms, some or all of the bss section is initialized to zeroes. Unix-like systems and Windows initialize the bss section to zero, allowing C and C++ statically-allocated variables initialized to values represented with all bits zero to be put in the bss segment.”

    Q2:
    static int a = 0 这一句第一次执行之后,以后再遇到会被跳过。具体怎么实现不知道,可能因编译器而异吧

    借用 5 楼的例子多写了一些:
    https://imgur.com/RNdB7vL
    https://imgur.com/3ZRnxxN
    zzjas98
        11
    zzjas98  
       2019-06-05 01:56:06 +08:00
    啊不对才注意到到楼主的例子是 static int a = 0,我 Q1 说的是针对“ static int a;”的。还真没找到 function scoped,initialized,static variable 被 initialize 之前的值是什么。。。但是它肯定会在第一次 function invocation 时候被 intialize,以后执行会直接跳过那句。
    这个链接或许有帮助:
    https://web.archive.org/web/20100328062506/http://www.acm.org/crossroads/xrds2-4/ovp.html

    另外,这个或者 1 楼的回复可以测试那个 declaration 被执行了几次

    #include <iostream>
    using namespace std;

    void func(int a) {
    static int i = (printf("declaration\n")) ? a:a;
    cout << i << endl;
    i++;
    }

    int main() {
    func(5);
    func(10);
    }
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3299 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 00:42 PVG 08:42 LAX 16:42 JFK 19:42
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86