学习这么长时间,一直在C语言这一层面上钻研和打拼,日积月累,很多关于C的疑惑在书本和资料中都难以找到答案。程序员是追求完美的一个种群,其头 脑中哪怕是存在一点点的思维黑洞都会让其坐卧不宁。不久前在itput论坛上偶得《Computer Systems A Programmer's Perspective》(以下称CS.APP)这本经典好书,中文有翻译的《深入理解计算机系统》。是遂连夜拜读以求解惑。虽说书中没有能正面的回答我的一些疑惑,但是它却为我指明了一条通向 “无惑”之路 -- 这就是打开汇编之门。
汇编语言是一门非常接近机器语言的语言,其语句与机器指令之间的对应关系更加简单和清晰。打开汇 编之门不仅仅能解除高级语言给你带来的疑惑,它更能让你更加的理解现代计算机的运行体系,还有一点更加重要的是它给你带来的是一种自信的感觉,减少了你在 高处摇摇欲坠的恐惧,响应了侯捷老师的“勿在浮沙筑高台”的号召。现在学习汇编的目的已与以前大大不同了。正如CS.APP中所说那样“程序员学习汇编的 需求随着时间的推移也发生了变化,开始时是要求程序员能直接用汇编编写程序,现在则是要求能够阅读和理解优化编译器产生的代码”。能阅读和理解,这也恰恰 是我的需求和目标。
以前接触过汇编,主要是Microsoft MASM宏汇编,不过那时的认识高度不够加上态度不端正,错失了一个很好的学习机会。现在绝大部分时间是使用GCC在Unix系列平台上工作,选择汇编语 言当然是GNU汇编了,恰好CS.APP中使用的也是GNU的汇编语法。由于学习汇编的主要目的还是“解惑”,所以形式上多是以C代码和汇编代码的比较。
1、汇编让你看到更多
随 着你使用的语言的层次的提高,你眼中的计算机将会越来越模糊,你的关注点也越来越远离语言本身而靠近另一端“问题域”,比如通过JAVA,你更多看到的是 其虚拟机,而看不到真实的计算机;通过C,你看到的也仅仅是内存一层;到了汇编语言,你就可以深入到寄存器一层自由发挥了。汇编程序员眼里的“独特风景” 包括:
a) “程序计数器(%eip)” -- 一个特殊寄存器,其中永远存储下一条将要执行的指令的地址;
b) 整数寄存器 -- 共8个,分别是%eax、%ebx、%ecx、%edx、%esi、%ebi、%esp和%ebp,它们可以存整数数据,可以存地址,也可以记录程序状态 等。早期每个寄存器都有其特殊的用途,现在由于像linux这样的平台多采用“平面寻址[1]”,寄存器的特殊性已经不那么明显了。
c) 条件标志寄存器 -- 保存最近执行的算术指令的状态信息,用来实现控制流中的条件变化。
d) 浮点数寄存器 -- 顾名思义,用来存放浮点数。
虽说寄存器的特殊性程度已经弱化,但是实际上每个编译器在使用这些寄存器时还是遵循一定的规则的,以后再说。
2、初窥汇编
下面是一个简单的C函数:
void dummy() {
int a=1234;
int b=a;
}
我们使用gcc加-S选项将之转换成汇编代码如下(省略部分内容):
movl $1234, -4(%ebp)
movl -4(%ebp), %eax
movl %eax, -8(%ebp)
看 了一眼又一眼,还是看不懂,只是发现些熟悉的内容,因为上面提过如%ebp、%eax等。这只是个引子,让我们感性的认识一下汇编的“容貌”。我们一点点 地来看。咋看一眼汇编代码长得似乎很相似,没错,汇编代码就是一条一条的“指令+操作数”的语句的集合。汇编指令是固定的,每条指令都有其固定的用途,而 操作数表示则有多种类型。
1) 操作数表示
大部分汇编指令都有一个或多个操作数,包括指令操作中的源和目的。一条标准的指令格式大 致是这样的:“指令 + 源操作数 + 目的操作数”,其中源操作数可以是立即数、从寄存器中读出的数或从内存中读出的数;而目的操作数则可以是寄存器或内存。按这么一分类,操作数就大致有三 种:
a) 立即数表示法 -- 如“movl $1234, -4(%ebp)”中的“$1234”,就是一个立即数作为操作数,按照GNU汇编语法,立即数表示为“$+整数”。立即数常用来表示代码中的一些常数, 如上例中的“$1234”。注意一点的是立即数不能作为目的操作数。
b) 寄存器表示法 -- 这种比较简单,它就是表示寄存器之内容。如上面的“movl -4(%ebp), %eax”中的%eax就是使用寄存器表示法作源操作数,而“movl %eax, -8(%ebp)”中的%eax则是使用寄存器表示法作目的操作数。
- 下一篇: 玩家必备手机麻将开挂教程【抖音视频】
- 上一篇: 赢家经验分享一定要牛有挂吗【好看视频】