how do static variables inside functions work?

后端 未结 3 2302
花落未央
花落未央 2021-02-19 04:20

In the following code:

int count(){
    static int n(5);
    n = n + 1;
    return n;
}

the variable n is instantiated only once a

相关标签:
3条回答
  • 2021-02-19 05:05

    This is, of course, compiler-specific.

    The reason you didn't see any checks in the generated assembly is that, since n is an int variable, g++ simply treats it as a global variable pre-initialized to 5.

    Let's see what happens if we do the same with a std::string:

    #include <string>
    
    void count() {
        static std::string str;
        str += ' ';
    }
    

    The generated assembly goes like this:

    _Z5countv:
    .LFB544:
            .cfi_startproc
            .cfi_personality 0x3,__gxx_personality_v0
            .cfi_lsda 0x3,.LLSDA544
            pushq   %rbp
            .cfi_def_cfa_offset 16
            movq    %rsp, %rbp
            .cfi_offset 6, -16
            .cfi_def_cfa_register 6
            pushq   %r13
            pushq   %r12
            pushq   %rbx
            subq    $8, %rsp
            movl    $_ZGVZ5countvE3str, %eax
            movzbl  (%rax), %eax
            testb   %al, %al
            jne     .L2                     ; <======= bypass initialization
            .cfi_offset 3, -40
            .cfi_offset 12, -32
            .cfi_offset 13, -24
            movl    $_ZGVZ5countvE3str, %edi
            call    __cxa_guard_acquire     ; acquire the lock
            testl   %eax, %eax
            setne   %al
            testb   %al, %al
            je      .L2                     ; check again
            movl    $0, %ebx
            movl    $_ZZ5countvE3str, %edi
    .LEHB0:
            call    _ZNSsC1Ev               ; call the constructor
    .LEHE0:
            movl    $_ZGVZ5countvE3str, %edi
            call    __cxa_guard_release     ; release the lock
            movl    $_ZNSsD1Ev, %eax
            movl    $__dso_handle, %edx
            movl    $_ZZ5countvE3str, %esi
            movq    %rax, %rdi
            call    __cxa_atexit            ; schedule the destructor to be called at exit
            jmp     .L2
    .L7:
    .L3:
            movl    %edx, %r12d
            movq    %rax, %r13
            testb   %bl, %bl
            jne     .L5
    .L4:
            movl    $_ZGVZ5countvE3str, %edi
            call    __cxa_guard_abort
    .L5:
            movq    %r13, %rax
            movslq  %r12d,%rdx
            movq    %rax, %rdi
    .LEHB1:
            call    _Unwind_Resume
    .L2:
            movl    $32, %esi
            movl    $_ZZ5countvE3str, %edi
            call    _ZNSspLEc
    .LEHE1:
            addq    $8, %rsp
            popq    %rbx
            popq    %r12
            popq    %r13
            leave
            ret
            .cfi_endproc
    

    The line I've marked with the bypass initialization comment is the conditional jump instruction that skips the construction if the variable already points to a valid object.

    0 讨论(0)
  • 2021-02-19 05:09

    This is entirely up to the implementation; the language standard says nothing about that.

    In practice, the compiler will usually include a hidden flag variable somewhere that indicates whether the static variable has already been instantiated or not. The static variable and the flag will probably be in the static storage area of the program (e.g. the data segment, not the stack segment), not in the function scope memory, so you may have to look around about in the assembly. (The variable can't go on the call stack, for obvious reasons, so it's really like a global variable. "static allocation" really covers all sorts of static variables!)

    Update: As @aix points out, if the static variable is initialized to a constant expression, you may not even need a flag, because the initialization can be performed at load time rather than at the first function call. In C++11 you should be able to take advantage of that better than in C++03 thanks to the wider availability of constant expressions.

    0 讨论(0)
  • 2021-02-19 05:16

    It's quite likely that this variable will be handled just as ordinary global variable by gcc. That means the initialization will be statically initialized directly in the binary.

    This is possible, since you initialize it by a constant. If you initialized it eg. with another function return value, the compiler would add a flag and skip the initialization based on the flag.

    0 讨论(0)
提交回复
热议问题