函数静态变量

原文http://www.endlessbright.org/?p=51


这是之前公司简称我司的一位已经离职同事赵工曾写文章,此文章在上面做一定扩展,因为是公司内部论坛,因此无法贴出原文链接。

有一段有趣的代码:

#include <stdio.h>

int func_a();

int func_b();

int func_c();

int func_a() {

static int a = func_b();

return a;

}

int func_b() {

static int b = func_c();

return b;

}

int func_c() {

static int c = func_a();

return c;

}

int main()

{

printf("%d\n", func_a());

return 0;

}

上面程序在Linuxwindows下面输出的结果会是什么?

 

 

 

Windows下面输出为0,而在Linux下面则编译失败。

Linux环境下面用gcc编译直接失败,部分错误信息:

/tmp/ccnpmELe.o:In function `func_a()':

main.cpp:(.text+0x19):undefined reference to `__cxa_guard_acquire'

main.cpp:(.text+0x3d):undefined reference to `__cxa_guard_release'

main.cpp:(.text+0x5a): undefined reference to`__cxa_guard_abort'

g++则是在运行时coredumpgdb查看汇编可以看到锁(汇编知识缺乏,后面学习了再补详细说明):

0x000000000040074b<+7>:   mov    $0x601058,%eax

   0x0000000000400750 <+12>:   movzbl (%rax),%eax

   0x0000000000400753 <+15>:   test  %al,%al

   0x0000000000400755 <+17>:   jne   0x400785 <_Z6func_av+65>

   0x0000000000400757 <+19>:   mov   $0x601058,%edi

   0x000000000040075c <+24>:   callq 0x400600 <__cxa_guard_acquire@plt>

   0x0000000000400761 <+29>:   test  %eax,%eax

   0x0000000000400763 <+31>:   setne %al

   0x0000000000400766 <+34>:   test  %al,%al

   0x0000000000400768 <+36>:   je    0x400785 <_Z6func_av+65>

   0x000000000040076a <+38>:   mov   $0x0,%r12d

   0x0000000000400770 <+44>:   callq 0x4007ad <_Z6func_bv>

=>0x0000000000400775 <+49>: mov    %eax,0x2008fd(%rip)        # 0x601078 <_ZZ6func_avE1a>

   0x000000000040077b <+55>:   mov   $0x601058,%edi

   0x0000000000400780 <+60>:   callq 0x400620 <__cxa_guard_release@plt>

   0x0000000000400785<+65>:   mov    0x2008ed(%rip),%eax        # 0x601078 <_ZZ6func_avE1a>

windows下面可以运行,返回0。在静态变量后面有一个int(地址378154h)保存着此静态变量是否初使化标志,根据这个标示是否为1判断是否已经初使化。

    static int a = func_b();

0037140D  mov        eax,dword ptr [$S1 (378154h)]

00371412  and        eax,1

00371415  jne        func_a+6Ch (37143Ch)

00371417  mov        eax,dword ptr [$S1 (378154h)]

0037141C  or         eax,1

0037141F  mov        dword ptr [$S1 (378154h)],eax

00371424  mov        dword ptr [ebp-4],0

0037142B  call       func_b (3710F0h)

00371430  mov        dword ptr [a (378144h)],eax

00371435  mov        dword ptr [ebp-4],0FFFFFFFFh

    return a;

0037143C  mov         eax,dword ptr [a (378144h)]

 

Linux加入锁从而保证在多线程情况下此静态变量一定被初使化,而在windows多线程临界情况可能出现此静态变量未初使化情况。

Windows存在未初使化问题,当类中存在动态内存分配时就会出现未定义行为。但此类函数本身就不保证线程安全,可以理解。

Gcc使用锁,严格保证使用时被初使化,当存在这种循环锁还能够编译错误。G++则相对的弱一点。

 

但并不是所有的函数static变量都有初使化过程,例如:

int func_test()

{

static int a = 0;

a++;

Return a;

}

这捉情况下并没有a初使化编译代码,其值在.data段中。程序运行时只需要直接映射到内存即可。

理论上只要此静态变量类型中的所有数据都可以在编译时期确定的话(一般是该类型不需要动态分配堆上面的内存),就没有必要在运行期在去做初使化过程。

例如:

struct Test_A

{

    int a;

    int b;

 

    Test_A()

    {

        a = 0;

        b = 1;

    }

};

 

void func_a()

{

static Test_A a;

}

这种情况下a应该可以不用再做初使化,直接将数据写入到.data即可,从windows测试程序来还是进行了初使化过程。

这只能算是一个优化需求,不清楚开启优化选项是否能优化掉。

 

静态变量地址在编译链接时期就已经确定,其地址上面内容则是根据其初使化而定,一般基本数据类型都不需要运行期再初使化,而自定义类型则需要(可以去思考哪些情况其实是可以优化成不需要运行期去实现的)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值