C++中局部静态变量的调用问题

来源:互联网 发布:济南易途java培训骗局 编辑:程序博客网 时间:2024/06/08 11:10

问题背景是这样的,在学习《C++ Primer》的时候,学到 “6.1.1 局部对象”中“局部静态变量”知识,例子:
size_t count_called()
{
    static size_t ctr = 0;
    return ++ctr;
}
int main()
{
    for(size_t i=0; i!=10; i++)
        cout<<count_called() <<endl;
    return 0;
}
程序将输出1到10的数字。
问题:
    第二次调用count_called()函数的时候,“static size_t ctr = 0;”语句会不会发生“error:redeclaration of ...”之类的重声明错误呢?答案是:不会的。那么为什么不会重复执行该语句,对象ctr的值每次重置为0呢?答案是:第二次调用函数时,局部静态变量ctr不会再次初始化;它仅在第一次的时候做唯一的一次初始化,并在静态存储去设置一个guard变量(目测可以认为该变量是个布尔类型);当第一次之后再次调用函数遇见该语句,判断guard变量的值,如标记已经初始化,则直接使用对象。简单来说就是局部静态变量只会初始化一次,后面调用其所在函数直接使用对象值,不会再初始化。

其它补充:
(1)
关于静态变量存储区的概念可以参考:C/C++堆、栈及静态数据区详解
(2)
在“c++函数局部静态变量第二次被访问的时候具体做了些什么?”问题中也有回答,比如附链接中的答案:
(a)

2.8 Initialization Guard Variables

If a function-scope static variable or a static data member with vague linkage (i.e., a static data member of a class template) is dynamically initialized, then there is an associated guard variable which is used to guarantee that construction occurs only once. The guard variable's name is mangled based on the mangling of the guarded object name. Thus, for function-scope static variables, if multiple instances of the function body are emitted (e.g., due to inlining), each function uses the same guard variable to ensure that the function-scope static is initialized only once. Similarly, if a static data member is instantiated in multiple object files, the initialization code in each object file will use the same guard variable to ensure that the static data member is initialized only once.

The size of the guard variable is 64 bits. The first byte (i.e. the byte at the address of the full variable) shall contain the value 0 prior to initialization of the associated variable, and 1 after initialization is complete. Usage of the other bytes of the guard variable is implementation-defined.

See Section 3.3.2 for the API for references to this guard variable.

根据“3.3.2 One-time Construction API”可以发现,

  if (obj_guard.first_byte == 0) {              //zi如果obj_guard为0,表示还没有被初始化    if ( __cxa_guard_acquire (&obj_guard) ) {   //zi获取锁,避免多线程初始化      try {... initialize the object ...;      } catch (...) {        __cxa_guard_abort (&obj_guard);        throw;      }      ... queue object destructor with __cxa_atexit() ...;      __cxa_guard_release (&obj_guard);          //zi释放锁    }  }
上面的源代码分析可以参考:深入理解函数内静态局部变量初始化
在初始化过程中,guard_object的值设为1,“防止同一线程对对象多次初始化”;初始化结束后,guard_object的第一个字节设为非0值,取最低字节作为是否已初始化的标”。

"Returns 1 if the initialization is not yet complete......"、“Sets the first byte of the guard object to a non-zero value. This function extern "C" void __cxa_guard_release ( __int64_t *guard_object ) is called after initialization is complete.....

(b)

“说说msvc的实现。编译器自动创建两个全局变量,一个指向静态局部变量的指针,一个布尔值,用于标记是否第一次使用。下面就很简单了,第一次进函数时创建对象,以后都直接使用该对象。”


0 0