⚙️ 第十章 通过汇编语言认识程序的真面目
📄 一句话概括

全局变量地址固定、谁都能改、活到最后;局部变量住在栈上、靠 ebp 偏移定位、函数一返回就消失——两类变量活法完全不同。

📝 本节和前面几节的关系

10.8 讲清了栈帧,这一节对比住在栈帧里的变量和住在数据段的变量。

一、两类变量,四个对比

住在哪儿:全局在数据段(地址固定);局部在栈上(靠 ebp 偏移定位)。 怎么写:全局是 movl $5, _total;局部是 movl $5, -4(%ebp)。 活多久:全局活到程序结束;局部函数一返回就没了。 谁能改:全局谁都能改;局部只有这个函数自己。 四条一比,就能看出为什么两类变量解决的问题完全不一样。

二、地址固定 vs 随栈浮动

全局变量的地址在编译时就定死了——每次运行、每次调用都一样。 局部变量的地址是【ebp 减几】——同一个变量,在不同时刻栈帧位置不同,绝对地址就不同。 所以两种变量的写法必须区分:一个直接写名字,一个必须带偏移。 这不是编译器随心所欲,是被它们的【住址性质】逼出来的。

三、为什么局部变量不危险,全局变量危险

局部变量只有一个函数能碰——小范围、短生命,出了问题范围也小。 全局变量谁都能写、一直活着——改了它,你根本不知道是谁改的、什么时候改的。 所以有那条经验:能用局部就用局部,全局变量越少越好。 这句话背后不是风格问题,是寻址方式带来的现实后果。

四、那句老话的出处

【不要把局部变量的地址传出去】——这句话在汇编这一层有了硬解释。 局部变量住的那个格子,是【函数进出时临时立起来的一间屋子】。 函数一返回,栈帧拆掉,那间屋子就没了——传出去的地址就变成悬空的。 再用它,读到的就是别人的数据,或者直接崩掉——这类问题在高级语言里极难查,在汇编里一眼就看明白。

五、动手 Demo:全局变量和局部变量的工作原理

全局变量和局部变量,活法完全不同。 把两种变量的地址摆在一起对比,差别一目了然。

rows = [
    ("全局 _total", "movl $5, _total",   "固定地址,每次运行都一样"),
    ("局部 a",      "movl $5, -4(%ebp)",  "ebp - 4,随栈帧浮动"),
    ("局部 b",      "movl $6, -8(%ebp)",  "ebp - 8,紧挨着 a"),
]
for name, how, addr in rows:
    print(name, "|", how, "|", addr)

实跑输出:

--------------------------------------------------------------------
全局变量和局部变量的工作原理
--------------------------------------------------------------------

**全局变量和局部变量,活法完全不同。**

  对比项       全局变量                   局部变量
  ------------------------------------------------------------------
  住在哪儿     数据段,**地址固定**       栈上,**靠 ebp 偏移定位**
  怎么写       movl $5, _total            movl $5, -4(%ebp)
  活多久       整个程序运行期间           函数一返回就没了
  谁能改       谁都能改(所以要小心)     只有这个函数自己

  把两种变量的地址摆在一起,差别就一目了然:

  变量            写法                  地址是什么样的
  ---------------------------------------------------------------
  全局 _total     movl $5, _total       固定地址,每次运行都一样
  局部 a          movl $5, -4(%ebp)     ebp - 4,随栈帧浮动
  局部 b          movl $6, -8(%ebp)     ebp - 8,紧挨着 a

  三条推论:
    **局部变量在栈上【上下浮动】,所以只能用相对偏移来指。**
    **全局变量地址固定,所以谁都能直接写它——这也是它危险的地方。**
    **函数一返回,栈帧就没了,局部变量自然也跟着消失。**

  所以那句老话是有出处的:
  **【不要把局部变量的地址传出去】——它住的那间屋子,**
  **函数一返回就被拆了。**

实测对比:全局变量写死地址,局部变量是 ebp 减 4、减 8。 函数一返回栈帧就没了,局部变量自然跟着消失——所以不能把它的地址传出去。

30 秒实验

  1. 在纸上写出一个全局变量的地址和一个局部变量的地址,说明它们为什么写法不同。
  2. 想一想:删掉一个函数的返回语句之后,局部变量会怎样。

常见误区

  1. 以为全局变量【方便】所以更好。它的代价是随时可能被别人改掉。
  2. 以为局部变量的地址可以随便传出去。函数一返回那间屋子就被拆了。
自测三问(点开对答案) 1. 全局变量和局部变量的地址性质有什么不同? —— 全局变量地址固定,编译时就定死;局部变量以 ebp 为基准按偏移定位,随栈帧浮动。 2. 为什么全局变量更危险? —— 谁都能改、且一直活着,出了问题很难查出是谁、在什么时候改的。 3. 为什么不能把局部变量的地址传出去? —— 局部变量住在栈帧里,函数一返回栈帧就被拆掉,那个地址就悬空了。

相关笔记