编程语言
首页 > 编程语言> > 不懂汇编也可以从底层理解C/C++中各种变量(下篇)

不懂汇编也可以从底层理解C/C++中各种变量(下篇)

作者:互联网

这篇博客主要是记录关于静态局部变量的内容。

在上篇中已经提到了静态局部变量是和全局变量放在一个地方的也就是全局区内部,照理说他们的属性和作用域应该是一样的,现在提出以下两个问题:

1. 静态局部变量是如何限定其作用域在局部函数内部的?

2. 静态局部变量是如何做到只初始一次之后即使在进入也不会初始化的?

首先是第一个问题, 来看一下下面这段代码:

用WinHex打开将其编译之后的obj文件, 看到如下内容:

把这些内容全部复制过来:

上图中注释中的字符串都是对应静态局部变量在编译后文件中的名字,这也差不多解释了为何能够限定作用域

首先我们可以观察到一下几点情况:

1. 变量名称发生改变的仅仅是被限定作用域的静态局部变量还有常量字符串,至于全局变量以及静态全局都没有发生变化。

2. 静态局部变量的名称发生了变化, 其他的内容无需关心,主要是发现其中加入了自身函数名的名字

这种方法叫做名称粉碎

在源代码中需要被限定作用域的变量名会在编译后生成被粉碎后的名称,被粉碎后的名称中会加入自身的函数名以及联级等

信息,这里只要关心函数名即可,所以这就导致了我在局部函数内声明的变量名如果在main函数内使用就无法找到,因为如

果在main函数内要找到那就必须是带有main函数自身函数名的变量才行,这种方法也就成功限定了作用域。

接下来说说联级, 将test函数中加入了一个块作用域后在内部声明一个局部静态变量j,其粉碎后的名称我已经通过注释放上去了

其中最关键的是观察被粉碎后的两个变量中带有的数字1和2。这就是联级即嵌套的层数,变量i只嵌套了一层所以为1而变量j

嵌套了2层所以变成了2。这种方法限定了在同一局部函数不同块作用域内的静态变量的作用域

查看名称粉碎有一个VC++6.0自带的工具叫undname,该工具帮我们解析了静态局部变量被粉碎后的名字

接下去是第二个问题, 静态局部变量是如何做到只初始一次之后即使在进入也不会初始化的?

上篇已经说明了静态局部变量会在编译期间就被初始化了,所以在运行时不会执行其初始化代码,但没有说这是如何做到的。

实际上实现的方法非常简单,就是通过一个标志位, 如果进行了赋值那该标志位会被置位,这样,下次就不会在进行初始化了

来看下面这段代码:

单步运行如下,看吧初始化语句还没有执行,i就有了数值。

来关注其内存部分:

接下来把文件从.c改成.cpp文件后,调试如下代码,一个非常奇怪的现象出现了,静态局部变量是存在的,原因是

它可以被打印出地址但是在调试中我们却无法通过名称来找到静态局部变量i和i的地址。

其原因就是名称粉碎,名称粉碎不是标准,所以不同编译器,甚至是不同语言的名称粉碎方法都不一样,由于VC++

依旧使用C的名称粉碎方法来寻找该i变量,但是编译期间用的名称粉碎是C++的方式,所以导致符号无法找到。

不信的话可以做个试验:

看吧,当我们以C语言形式进行编译时,调试即可显示静态局部变量i的内容以及地址。这就是名称粉碎导致的,接下去用

WinHex来查看下cpp下的名称粉碎:

这里我多加了一个变量j:

看见了把,C与C++的名称粉碎方法是不一样的。

现在抛出一个问题,既然C语言是在编译期间执行初始化语句的,那如果我们给静态局部赋予变量会如何,这种情况

下就必须要执行因为变量在运行时才能确定。这里就不做试验了,直接给出结论,感兴趣可以自己去试试。结论是

VC++6.0中.c文件时不允许的而.cpp是可以的。

为了回答关于静态局部变量是如何做到只初始一次之后即使在进入也不会初始化的问题。必须要在cpp下进行,原因

往下看就知道了:

之前已经说了,为了达到这个目的,需要一个标志位(注意这标志位只有在初始化值为变量才会出现,原因很简单,

初始化为常量只要编译期间进行并且不把对应机器码放入可执行文件即可,然后运行时自然跳过了,但是变量初始化

就必须要执行该语句所以需要标志位来确定),现在来找到这个标志位,先来看这段代码:

调试进入test函数, 看这种情况下是不会跳过初始化语句的:

标志位就在变量值之前:

现在知道那条(&i)[-1] = 0是什么意思了吧,就是为了把标志位置0,这样就不会有初始化一次就不会初始化的现象了:

看看去掉置零标志位语句的结果:

(完)

标签:汇编,下篇,初始化,静态,局部变量,C++,作用域,名称,粉碎
来源: https://blog.csdn.net/qq_37232329/article/details/110731198