在以下代码中:
int count(){
static int n(5);
n = n + 1;
return n;
}
变量n
仅在第一次调用函数时实例化一次。
应该有一个标志或其他东西,以便它只初始化变量一次。我试着查看从gcc那里生成的汇编代码,但没有任何线索。
编译器是如何处理这个问题的?
发布于 2011-10-12 13:30:43
当然,这是特定于编译器的。
在生成的程序集中看不到任何检查的原因是,由于n
是一个int
变量,g++
只是将其视为一个预初始化为5的全局变量。
让我们看看如果我们对std::string
执行同样的操作会发生什么
#include <string>
void count() {
static std::string str;
str += ' ';
}
生成的程序集如下所示:
_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
我用bypass initialization
注释标记的那一行是条件跳转指令,如果变量已经指向一个有效的对象,它会跳过构造。
发布于 2011-10-12 13:26:42
这完全取决于实现;语言标准没有说明这一点。
在实践中,编译器通常会在某处包含一个隐藏的标志变量,用于指示静态变量是否已被实例化。静态变量和标志可能在程序的静态存储区(例如,数据段,而不是堆栈段),而不是在函数作用域内存中,因此您可能需要在程序集中查找。(由于显而易见的原因,该变量不能放在调用堆栈中,因此它实际上就像一个全局变量。“静态分配”实际上涵盖了所有类型的静态变量!)
更新:正如@aix指出的那样,如果将静态变量初始化为常量表达式,您甚至可能不需要标志,因为初始化可以在加载时执行,而不是在第一次函数调用时执行。与C++03相比,在C++11中应该能够更好地利用这一点,这要归功于常量表达式的更广泛可用性。
发布于 2011-10-12 13:27:37
这个变量很可能会像普通的全局变量一样由gcc来处理。这意味着初始化将直接在二进制文件中静态初始化。
这是可能的,因为你用一个常量来初始化它。如果你初始化了它,例如。对于另一个函数返回值,编译器将添加一个标志,并根据该标志跳过初始化。
https://stackoverflow.com/questions/7740761
复制