You*_*usf 15 c c++ compiler-construction static
在以下代码中:
int count(){
static int n(5);
n = n + 1;
return n;
}
Run Code Online (Sandbox Code Playgroud)
n在第一次调用函数时,变量仅实例化一次.
应该有一个标志或其他东西,所以它只将变量初始化一次..我试着从gcc查看生成的汇编代码,但没有任何线索.
编译器如何处理这个?
NPE*_*NPE 17
当然,这是特定于编译器的.
您没有在生成的程序集中看到任何检查的原因是,因为它n是一个int变量,g++只需将其视为预先初始化为5的全局变量.
让我们看看如果我们对以下内容做同样的事情会发生什么std::string:
#include <string>
void count() {
static std::string str;
str += ' ';
}
Run Code Online (Sandbox Code Playgroud)
生成的程序集如下所示:
_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
Run Code Online (Sandbox Code Playgroud)
我用bypass initialization注释标记的行是条件跳转指令,如果变量已指向有效对象,则跳过构造.
这完全取决于实施; 语言标准对此没有任何说明.
在实践中,编译器通常会在某处包含一个隐藏的标志变量,指示静态变量是否已经实例化.静态变量和标志可能位于程序的静态存储区域(例如数据段,而不是堆栈段),而不是在函数范围内存中,因此您可能需要在程序集中查看.(由于显而易见的原因,变量不能进入调用堆栈,因此它实际上就像一个全局变量."静态分配"实际上涵盖了各种静态变量!)
更新:正如@aix指出的那样,如果将静态变量初始化为常量表达式,则可能甚至不需要标志,因为初始化可以在加载时执行,而不是在第一次函数调用时执行.在C++ 11中,由于常量表达式的广泛可用性,您应该能够比在C++ 03中更好地利用它.