Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

变量、类型与表达式

复习

  • 名字、标签与地址:用名字和标签引用数据与跳转目标,不再手工数地址
  • 汇编器:通过两遍扫描,把符号汇编成机器码
  • 高级语言:再加一层:从“控制机器”转向“描述问题”

TL;DR

  • 变量就是“给一块存储起的名字”
  • 类型决定这块存储占多大、比特怎么解释、能参与哪些运算
  • 表达式像一棵运算树,先算优先级高的部分,再把结果往上合
  • 翻译表达式时,中间结果要用寄存器或临时位置暂存

正文

  高级语言里最常见的三样东西,是变量、类型和表达式。我们一个一个看它们到底“是什么”。

变量:给存储起个名字

  写 score = 95 时,我们其实在说两件事:

  1. 找一块存储,把 95 放进去
  2. 以后就用 score 这个名字来指代它

  所以, 变量 = 名字 + 一块存储 。这和我们前面给数据贴标签是一回事,只不过这一回是高级语言替我们管理。

  名字是给人看的。等程序真正运行,score 会变成某个地址或某个寄存器——这件事在高级语言里被藏了起来,但我们知道它一定发生过。

类型:这块存储的说明书

  光有名字还不够。一个叫 age 的变量,装的是整数还是字符?占一个字节还是四个字节?计算机得知道。

  这就轮到 类型 (type)出场。类型像一张说明书,至少回答三个问题:

  • 占多大:1 个字节?4 个字节?
  • 怎么解释:这串比特是整数,还是字符?(还记得前面说过,同一串比特,换个约定就换个意思)
  • 能做哪些运算:整数能加能减,字符通常不能直接做算术

  比如整数类型,可能占 4 个字节、按补码解释、支持加减乘除;字符类型,可能占 1 个字节、按编码解释(ASCII 或 UTF-8)、支持比较和拼接。类型错了,程序要么算出怪结果,要么干脆报错——这也是替我们挡掉错误的一层保护。

表达式:一棵运算树

  表达式看起来只是“一串算式”,但它骨子里是一棵树。看 a + b * c

      +
     / \
    a   *
       / \
      b   c

  乘法在下面,要先算;算完再把结果和 a 相加。这就是“先乘除、后加减”的由来—— 优先级决定了这棵树谁在底层、谁在上层。

  括号的作用,则是强行改变树的样子:(a + b) * c 会让加法先算。

表达式怎么落到机器上

  树是要一步一步算的。计算机一次只做一件小事,所以得把树“压平”成若干简单步骤。拿 total = a + b * c 来说:

t = b * c        ; 先算下面那棵子树
total = a + t    ; 再把顶层加起来

  翻译成汇编,大概是这样:

      LOAD A, [b]
      LOAD B, [c]
      MUL  R, A, B        ; 假想的乘法指令,可由加法器搭出
      STORE [t], R        ; 中间结果先存起来

      LOAD A, [a]
      LOAD B, [t]
      ADD  R, A, B
      STORE [total], R

  这里的 t 是一个 临时变量 ,专门用来暂存中间结果。你可能会问:为什么中间结果不一直留在寄存器里?因为寄存器数量有限,表达式一长、嵌套一深,就得把暂时不用的结果先放回存储器。翻译程序要做的,正是安排“哪一步用哪个寄存器、哪一步该存回内存”。

  表达式、类型、变量,就这样一层层落成了指令。接下来,我们看高级语言里那两样最像“控制”的东西——判断和循环——是怎么落到跳转指令上的。

思考题

  a + b * c(a + b) * c 结果通常不同。它们的运算树有什么差别?如果只能按“从左到右”一条线计算,能算对吗?

小结

知识点

  • 变量:名字加一块存储
  • 类型:占用大小、比特解释、可用运算
  • 表达式是一棵按优先级组织的运算树
  • 表达式落到机器时,中间结果要用寄存器或临时变量暂存

参考资料

  1. Wikipedia(zh):变量:程序中的变量
  2. Wikipedia(zh):数据类型:类型系统的基本概念
  3. Wikipedia(zh):逆波兰表示法:表达式如何被拆成顺序步骤

思考题答案(仅供参考)

  a + b * c 的树里,乘法在底层、先算;(a + b) * c 的树里,加法被括号拉到下层、先算。两棵树的形状不同,计算结果通常也不同。如果只按“从左到右”一条线计算,a + b * c 会被算成 (a + b) * c,结果就错了——所以机器必须先按优先级(或括号)把表达式组织成树,再决定计算顺序。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪