静态变量可以分为全局静态变量,和局部静态变量,先来说说全局的吧

全局静态变量和全局变量的区别并不大,只是全局静态变量只能在当前文件中使用,而在反汇编中二者并无区别,只可以在当前文件中使用,不过是编译器做出的限制。

局部静态变量,会有些特殊,它不会随着作用域结束而消失,在未进入作用于之前就已经存在。

局部静态变量和全局变量都保存在二进制文件的数据区,而在代码中的限制,不过是编译器限制而已。

那么当某个函数频繁调用局部静态变量时,C++的语法规定局部静态变量只能初始化一次,那么编译器是怎么做到的呢。

来看代码:

void ShowStatic(int nNum)
{
static int gnNumber = nNum;
printf("%dn", gnNumber);
}void main()
{ShowStatic(99);
}

汇编代码:

00E51738  mov         eax,dword ptr ds:[00E5A148h]
00E5173D  and         eax,1
00E51740  jne         ShowStatic+47h (0E51757h)
00E51742  mov         eax,dword ptr ds:[00E5A148h]
00E51747  or          eax,1
00E5174A  mov         dword ptr ds:[00E5A148h],eax
00E5174F  mov         eax,dword ptr [nNum]
00E51752  mov         dword ptr [gnNumber (0E5A144h)],eax  

可以看出,静态变量的赋值比普通变量赋值多了很多步骤,我们来分析下。

首先在地址00E5A148h中保存了局部静态变量的标志,这个标志占1个字节。

通过位运算,将标志中的一位数据置1,来判断局部静态变量是否初始化过。

而这个标志可以同时保存8个局部静态变量的初始状态。

通常这个标志出现在最先定义的局部静态变量的附近,例如此例局部变量应出现在 00E5A144h 或 00E5A14Ch中。

当同一个作用域内超过了8个静态局部变量,下一个标记将会除了现在第9个定义的局部静态变量地址的附近。

现在再来看上面的汇编代码就很清晰了:

00E51738  mov         eax,dword ptr ds:[00E5A148h]
00E5173D  and         eax,1
00E51740  jne         ShowStatic+47h (0E51757h)  

判断是否已经初始化,如果已经初始化就跳转到printf输出内容,否则不跳转继续执行。

00E51742  mov         eax,dword ptr ds:[00E5A148h]
00E51747  or          eax,1
00E5174A  mov         dword ptr ds:[00E5A148h],eax
00E5174F  mov         eax,dword ptr [nNum]
00E51752  mov         dword ptr [gnNumber (0E5A144h)],eax  

未初始化的情况,将标志位置位为1,并初始化gnNumber。

结束了?并没有

还有这样一个问题,编译器让其他作用域对局部静态变量不可见,这是怎么做到的?

在编译的过程中,编译器会对变量,函数等进行名称粉碎,也就是静态变量被重新命名了。

读者可将上面的代码编译链接,然后找到编译期结束后生成的obj文件,在这个文件中搜索静态变量的名字(本文用HxD软件打开obj文件),搜索结果如下图:

名称粉碎后,在原有名称中加加入了一些额外信息,入作用域,类型等。

像C++重载也是名称粉碎的原理。

下面的汇编是在C++11中编译的结果,显然和上文的有些差距:

static int gnNumber = nNum;
00C11818  mov         eax,dword ptr [_tls_index (0C1B190h)]
00C1181D  mov         ecx,dword ptr fs:[2Ch]
00C11824  mov         edx,dword ptr [ecx+eax*4]
00C11827  mov         eax,dword ptr ds:[00C1B150h]
00C1182C  cmp         eax,dword ptr [edx+104h]
00C11832  jle         ShowStatic+6Fh (0C1185Fh)
00C11834  push        0C1B150h
00C11839  call        __Init_thread_header (0C110DCh)
00C1183E  add         esp,4
00C11841  cmp         dword ptr ds:[0C1B150h],0FFFFFFFFh
00C11848  jne         ShowStatic+6Fh (0C1185Fh)
00C1184A  mov         eax,dword ptr [nNum]
00C1184D  mov         dword ptr [gnNumber (0C1B14Ch)],eax
00C11852  push        0C1B150h
00C11857  call        __Init_thread_footer (0C11177h)
00C1185C  add         esp,4

前三行代码:

00C11818  mov         eax,dword ptr [_tls_index (0C1B190h)]
00C1181D  mov         ecx,dword ptr fs:[2Ch]
00C11824  mov         edx,dword ptr [ecx+eax*4]  

TLS?怎么还多了两个函数?__Init_thread_header和_Init_thread_footer

这两个函数是用来保证局部的静态对象的初始化线程安全。

但局部变量的互斥还是老样子,只不过被封装进上述的两个函数之中了。

有兴趣的读者可以自己上机调试一番。

怎么调出全局搜索_局部静态变量只能初始化一次?它是怎么实现的相关推荐

  1. python局部静态变量_全局变量、局部变量和静态变量

    全局变量和局部变量在写代码时需要区分清楚,不然会出大问题.不同语言定义不同范围的变量的写法有很大的区别. 那么静态变量是在什么场景下用到呢?我们来假设这样一个场景:在函数内部定义的变量,当程序执行到它 ...

  2. 2020-11-28(全局变量和局部静态变量)

    常量和全局变量有着相似的特征,都是在程序执行前就存在了.在大多数情况下,在PE文件中的只读数据节中常量的节属性被修饰为不可写:而全局变量和静态变量则在属性为可读写的数据节中. 具有初始值的全局变量,其 ...

  3. 局部静态变量和全局静态变量

    静态变量的类型说明符是static. 静态变量当然是属于静态存储方式,但是属于静态存储方式的量不一定就是静态变量,例如外部变量虽属于静态存储方式,但不一定是静态变量,必须由 static加以定义后才能 ...

  4. 多线程中局部静态变量初始化的陷阱

    C++当中常常需要一个全局唯一的对象实例,这时候,我们就会想到单件模式.如何实现这一模式?全局变量当然是一个简单可行的方法,然而,这太丑陋.嗯,其实,丑陋倒也罢了,最严重的是它将引诱程序员滥用全局变量 ...

  5. Python实现局部静态变量

    python没有局部静态变量,但可以通过以下几种方式实现类似于C语言的函数内局部静态变量. 1.定义函数属性 在python中一切皆对象,函数也是一个对象,因此可以给函数定义属性: def func( ...

  6. java静态变量需要初始化吗_» Java静态变量初始化顺序浅谈

    Java 类初始化顺序在网上已经有很多文章了,这里不再谈那么多,仅仅谈下Java静态变量的初始化顺序,如果你是Java高手,并且自认为对这个顺序已经掌握到了炉火纯青的境界,请忽视这篇文章. 前天看了Y ...

  7. php+静态变量的初始值,php 静态变量的初始化

    php 静态变量的初始化 发布于 2014-10-17 13:15:45 | 79 次阅读 | 评论: 0 | 来源: 网友投递 PHP开源脚本语言PHP(外文名: Hypertext Preproc ...

  8. java中的静态初始化是什么意思,Java中static静态变量的初始化完全解析

    静态变量初始化顺序 1.简单规则 首先先看一段最普遍的JAVA代码: ? 这里先猜下控制台输出结果是什么? OK, 或许你已经猜到下面了结果了,那么你还是熟悉Java的. 复制代码 代码如下: 0 1 ...

  9. Java静态变量的初始化

    Java静态变量的初始化 C/C++在脑海中的印象太深了,以至于一看到 static 的变量(Java中叫做:域),本能的以为它一旦初始化就不能改变了. 原来Java在还有 final . 转自:ht ...

最新文章

  1. 刻意练习:LeetCode实战 -- 不同的二叉搜索树
  2. PPDE英雄帖!广邀全球开发者执开源之桨,汇百川成海
  3. 贵阳市计算机智能考试,白云区教育局关于对计算机智能考试考点考场建设开展专项检查的通知(白教通字〔2020〕79号)...
  4. 运维与自动化系列③自动化部署基础与shell脚本实现
  5. linux仿真速度快吗,Linux上安装使用最快的GPU加速的终端仿真器Alacritty
  6. 精简jdk包_在JDK 12精简数字格式中使用最小分数数字
  7. RabbitMq下载和安装linuxcenteros安装
  8. 中国提高表现的药物行业市场供需与战略研究报告
  9. 带项目的学问,如何带半路项目
  10. Java打印条码,使用热敏条码打印机
  11. 西威变频器avo下载调试资料_图解变频器的应用与接线,电气工程师必备
  12. Linux:网络测试工具之 iperf3
  13. js调用android手写输入法,jQuery手写输入法代码
  14. p6spy mysql8_druid数据源集成p6spy踩坑
  15. 程序员玩游戏之四--娱网棋牌大连打滚子记牌器
  16. 泡芙噶的计算机网络(3)-扑朔迷离的Cisco Packet Tracer实验
  17. 分享一个吐血三升的GNS3 ASAv9.X直连ping不通的原因
  18. 毒蘑菇导航,感觉不像是毒蘑菇,更像是个树莓了。
  19. J2SE - super
  20. redis服务器 本地连接

热门文章

  1. 如何帮助企业把风控做得更好?(续篇)
  2. CentOS安装nginx方法命令教程
  3. 小胖说事30------iOS 强制转成横屏的方式
  4. css 实现居中的五种方式
  5. Linux学习之七--mysql的安装使用
  6. 组内Linq培训记录
  7. 从SourceForge上获取CVS管理的开源代码
  8. IE8下JQuery clone 出的select元素使用append添加option异常解决记录
  9. 第五篇:数据预处理(二) - 异常值处理
  10. 07: redis分布式锁解决超卖问题