<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>汇编入门 on 菜鸟程序员</title><link>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/</link><description>Recent content in 汇编入门 on 菜鸟程序员</description><generator>Hugo</generator><language>zh-cn</language><atom:link href="https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/index.xml" rel="self" type="application/rss+xml"/><item><title>汇编语言入门一：环境准备</title><link>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A801%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A801%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87/</guid><description>&lt;p>现阶段，找个方便好使的编程环境还是比较蛋疼的，对于部分想过瘾或者想从学习实践中学习的小伙伴来说，略显蛋疼。不过，仔细琢磨，还是能够自己折腾出一个好用的环境来的。开搞。&lt;/p>
&lt;h2 id="环境">环境&lt;a class="td-heading-self-link" href="#%e7%8e%af%e5%a2%83" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;ul>
&lt;li>Ubuntu&lt;/li>
&lt;li>gcc/nasm&lt;/li>
&lt;/ul>
&lt;p>也就是说，你先安装一个能正常使用的Ubuntu再说吧，然后顺便熟悉一些相关的概念和操作。&lt;/p>
&lt;p>后面若没有特殊说明，那我们讨论的问题都是在这个软件环境下。&lt;/p>
&lt;h3 id="环境检查">环境检查&lt;a class="td-heading-self-link" href="#%e7%8e%af%e5%a2%83%e6%a3%80%e6%9f%a5" aria-label="Heading self-link">&lt;/a>&lt;/h3>
&lt;p>先打开终端，安装所需软件（注意$开头的才是命令，并且$并不属于命令的一部分）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>$ sudo apt-get install gcc nasm vim gcc-multilib -y
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在终端中分别执行which nasm和which gcc，得到如下结果，则表示环境已经安装完毕。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>$ which nasm
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>/usr/bin/nasm
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ which gcc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>/usr/bin/gcc
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="开始第一个程序">开始第一个程序&lt;a class="td-heading-self-link" href="#%e5%bc%80%e5%a7%8b%e7%ac%ac%e4%b8%80%e4%b8%aa%e7%a8%8b%e5%ba%8f" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>在汇编语言环境下，我们先别急着搞什么Hello World，在这里要打印出Hello World还不是一个简单的事情，这也算是初入汇编比较让人不解的地方，成天都在扯什么寄存器寻址啥的，说好的变量分支循环函数呢？&lt;/p>
&lt;p>别说话，先按照我的套路把环境配好，程序跑起来了再说。注意，不是Hello World。先亮出第一个程序的C语言等价代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>int main() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return 0;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>不好意思，大括号没换行。你以为接下来我要gcc -S吗？Too naive。我这可是正宗手工艺，非机械化生产。&lt;/p>
&lt;p>说正事，先一股脑啥都不知道地把代码敲完，跑起来再说：&lt;/p>
&lt;p>首先准备个文件，暂且叫做first.asm吧，然后把下面的代码搞进去：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>global main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mov eax, 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>好了程序写完了，你能感受到这里的0就是上面C代码里的0，说明你有学习汇编的天赋。&lt;/p>
&lt;p>OK接下来就要编译运行了。来一堆命令先：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>$ nasm -f elf first.asm -o first.o
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ gcc -m32 first.o -o first
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这下，程序就编译好了，像这样：&lt;/p></description></item><item><title>汇编语言入门二：环境有了先过把瘾</title><link>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A802%E7%8E%AF%E5%A2%83%E6%9C%89%E4%BA%86%E5%85%88%E8%BF%87%E6%8A%8A%E7%98%BE/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A802%E7%8E%AF%E5%A2%83%E6%9C%89%E4%BA%86%E5%85%88%E8%BF%87%E6%8A%8A%E7%98%BE/</guid><description>&lt;p>上回说到，咱们把环境搭好了，可以开始玩耍汇编了。&lt;/p>
&lt;h2 id="寄存器是啥玩意儿">寄存器是啥玩意儿？&lt;a class="td-heading-self-link" href="#%e5%af%84%e5%ad%98%e5%99%a8%e6%98%af%e5%95%a5%e7%8e%a9%e6%84%8f%e5%84%bf" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>开始学C的时候，有没有一种感觉，变量？类型？我可是要改变世界的男人，怎么就成天在跟i++较劲啊？这黑框程序还只能用来算数学，跟说好的不一样呢？？？想必后来，见得多了，你的想法也不那么幼稚了吧。&lt;/p>
&lt;p>好了，接下来汇编也会给你同样一种感觉的。啥玩意儿？寄存器？寻址？说好的变量类型循环函数呢？。&lt;/p>
&lt;p>好了，我想先刻意避开这些晦涩难懂的东西，找到感觉了，再回头来研究条条框框。在此先把基本的几个简单的东西弄熟练，过早引入太多概念容易让人头昏眼花。&lt;/p>
&lt;p>这里就说寄存器，通俗地来解释一下。回到上小学的时候，现在有一大堆计算题：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>99+10=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>32-20=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>14+21=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>47-9=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>87+3=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>86-8=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>正常来讲，要算出这个么多题目，你需要一支笔，一边计算的同时一边把结果写下来。&lt;/p>
&lt;p>好了，到这里，我就来做个类比，助你大致理解寄存器是干啥用的。首先，我们把CPU和大脑做一个类比。&lt;/p>
&lt;p>你在纸上进行计算的时候，需要不断往纸上写下计算结果，一边在脑子里进行计算。大致过程就像：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>1. 在纸上找一个题目，先看清两个数字，并迅速记下来
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2. 在脑子里对这两个数字进行计算，计算出的结果也是记在脑子里的
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>3. 将计算结果写在纸上，继续做下一个题目
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>好了，这个过程就和计算机执行的过程有几分相似。草稿纸就相当于是个内存，脑子就是CPU。&lt;/p>
&lt;p>在计算的时候，你需要知道计算的两个数字，而且还得知道是做什么运算，这些信息都是从草稿纸上看见之后，短暂记忆在脑子里的。&lt;/p>
&lt;p>CPU在计算的时候也是一样，需要知道要计算的数据是什么，还得知道是做什么运算，这些信息也需要临时保存在CPU的某个地方。这个地方就是寄存器。&lt;/p>
&lt;p>好了到这里，不知道你有没有看明白？也就是说CPU里头的寄存器的作用，就像我们在做计算的时候会临时在脑子里记住数字一样。当然你的脑子能记住不止一个数据，CPU也不止一个寄存器。&lt;/p>
&lt;h2 id="为啥c语言里没有说这些">为啥C语言里没有说这些？&lt;a class="td-heading-self-link" href="#%e4%b8%ba%e5%95%a5c%e8%af%ad%e8%a8%80%e9%87%8c%e6%b2%a1%e6%9c%89%e8%af%b4%e8%bf%99%e4%ba%9b" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>就是因为写汇编语言的时候，要在有限的寄存器情况下，编写复杂的程序，还要考虑灵活性、性能、正确性等等乱七八糟的问题，对于程序员是一个超级大的负担。&lt;/p>
&lt;p>因此有人专门发明了许多更方便好用的“高级语言”，然后还专门写了个配套的程序能够把用这个“高级语言”写的东西翻译成汇编语言，再将汇编语言翻译成机器能执行的指令。其中之一就是C语言。也就是C语言发明出来就是奔着比汇编语言好用的目标去的。所以啊，相比汇编这种繁琐复杂的编程方式，高级语言不知道高级到哪里去了。&lt;/p>
&lt;h3 id="那学习汇编语言有用吗">那学习汇编语言有用吗？&lt;a class="td-heading-self-link" href="#%e9%82%a3%e5%ad%a6%e4%b9%a0%e6%b1%87%e7%bc%96%e8%af%ad%e8%a8%80%e6%9c%89%e7%94%a8%e5%90%97" aria-label="Heading self-link">&lt;/a>&lt;/h3>
&lt;p>没有。&lt;/p>
&lt;h2 id="开始一顿乱写">开始一顿乱写&lt;a class="td-heading-self-link" href="#%e5%bc%80%e5%a7%8b%e4%b8%80%e9%a1%bf%e4%b9%b1%e5%86%99" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>好了，先介绍个程序，运行完了能够开心一下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>global main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mov eax, 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mov ebx, 2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> add eax, ebx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>老套路，保存成文件，比如叫做nmb.asm，然后编译运行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>$ nasm -f elf nmb.asm -o nmb.o
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ gcc -m32 nmb.o -o nmb
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ ./nmb ; echo $?
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果你能看出来这里面的端倪，说明你是一个聪明伶俐的天才。不就是做了个算术题1+2=3么。&lt;/p></description></item><item><title>汇编语言入门三：是时候上内存了</title><link>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A803%E6%98%AF%E6%97%B6%E5%80%99%E4%B8%8A%E5%86%85%E5%AD%98%E4%BA%86/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A803%E6%98%AF%E6%97%B6%E5%80%99%E4%B8%8A%E5%86%85%E5%AD%98%E4%BA%86/</guid><description>&lt;h1>&lt;a class="td-heading-self-link" href="#" aria-label="Heading self-link">&lt;/a>&lt;/h1>
&lt;p>上回说到了寄存器和指令，这回说下内存访问。开始之前，先来复习一下。&lt;/p>
&lt;h1 id="回顾">回顾&lt;a class="td-heading-self-link" href="#%e5%9b%9e%e9%a1%be" aria-label="Heading self-link">&lt;/a>&lt;/h1>
&lt;h2 id="寄存器">寄存器&lt;a class="td-heading-self-link" href="#%e5%af%84%e5%ad%98%e5%99%a8" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;ul>
&lt;li>寄存器是在CPU里面&lt;/li>
&lt;li>寄存器的存储空间很小&lt;/li>
&lt;li>寄存器存放的是CPU马上要处理的数据或者刚处理出的结果（还是热乎的）&lt;/li>
&lt;/ul>
&lt;h2 id="指令">指令&lt;a class="td-heading-self-link" href="#%e6%8c%87%e4%bb%a4" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;ul>
&lt;li>传送数据用的指令mov&lt;/li>
&lt;li>做加法用的指令add&lt;/li>
&lt;li>做减法用的指令sub&lt;/li>
&lt;li>函数调用后返回的指令ret&lt;/li>
&lt;/ul>
&lt;h1 id="指针和内存">指针和内存&lt;a class="td-heading-self-link" href="#%e6%8c%87%e9%92%88%e5%92%8c%e5%86%85%e5%ad%98" aria-label="Heading self-link">&lt;/a>&lt;/h1>
&lt;h2 id="高能预警">高能预警&lt;a class="td-heading-self-link" href="#%e9%ab%98%e8%83%bd%e9%a2%84%e8%ad%a6" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>高能预警，后面会涉及到一些高难度动作，请提前做好以下准备：&lt;/p>
&lt;ul>
&lt;li>精通2进制和16进制加减法&lt;/li>
&lt;li>精通2进制表示与16进制表示之间的关系&lt;/li>
&lt;li>精通8位、16位、32位、64位二进制数的16进制表示&lt;/li>
&lt;/ul>
&lt;p>举个例子，一个16进制数0BC71820，其二进制表示为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>00001011 11000111 00011000 00100000
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>你能快速地找到它们之间的对应关系吗？不会的话快去复习吧。&lt;/p>
&lt;h2 id="寄存器宽度">寄存器宽度&lt;a class="td-heading-self-link" href="#%e5%af%84%e5%ad%98%e5%99%a8%e5%ae%bd%e5%ba%a6" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>现在，为了简便，我们只讨论32位宽的寄存器。也就是说，目前我们讨论的寄存器，它的宽度都是32位的，也就是里面存放了一个32位长的2进制数。&lt;/p>
&lt;p>通常，一个字节为8个二进制比特位，那么一个32位长的二进制数，那么它的大小就应该是4个字节。也就是把32位长的寄存器写入到内存里，会覆盖掉四个字节的存储空间。&lt;/p>
&lt;h2 id="内存">内存&lt;a class="td-heading-self-link" href="#%e5%86%85%e5%ad%98" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>想必内存大家心里都比较有数，就是暂时存放CPU计算所需的指令和数据的地方。&lt;/p>
&lt;p>诶？那前面说好的寄存器呢？寄存器也是类似的功能啊。对的，寄存器有类似功能，理论上一个最小的计算系统只需要寄存器和CPU的计算部件（ALU）就够了。不过，实际情况更加复杂一些，还是拿计算题举例，这次更复杂了：&lt;/p>
&lt;p>（这里的例子只够说明寄存器和内存的角色区别，而非出现内存和寄存器这样角色的根本原因）&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>( 847623785 * 12874873 + 274632 ) / 999 =
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>好了，这个题目就不像前面的那么简单了，首先你肯定没法直接在脑子里三两下就算出来，还是得需要一个草稿纸了。&lt;/p>
&lt;p>计算过程中，你还是会把草稿纸上正在计算的几个数字记在脑子里，然后快速地算完并记下来，然后往草稿纸上写。&lt;/p>
&lt;p>最后，在草稿纸上演算完毕后，你会把最终结果写到试卷上。&lt;/p>
&lt;p>好了，这里的草稿纸就相当于是内存了。它也充当一个临时记录数据的作用，不过它的容量就比自己的脑子要大得多了，而且一旦你把东西写下来，也就不那么担心忘记了。&lt;/p>
&lt;p>诶？我不能多做点寄存器，就不需要单独的内存了呀？是的，理论上是这样，然而，实际上如果多做一点寄存器的话，CPU就要卖$9999999一片了，贵啊（具体原因可以了解SRAM与DRAM）。&lt;/p>
&lt;p>也就是说，在计算机系统里，寄存器和内存都充当临时存储用，但是寄存器太小也太少了，内存就能帮个大忙了。&lt;/p>
&lt;h2 id="指针">指针&lt;a class="td-heading-self-link" href="#%e6%8c%87%e9%92%88" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>在C语言里面，有个神奇的东西叫做指针，它是初学者的噩梦，也是高手的天堂。&lt;/p>
&lt;p>这里不打算给不明白指针的人讲个明白，直接进入正题。首先，内存是一个比较大的存储器，里面可以存放非常非常多的字节。&lt;/p>
&lt;p>好了，现在我们来为整个内存的所有字节编号，为了方便，咱们首先考虑按照字节为单位连续编号：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span> 0 1 2 3 4 5 6 7 ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>......................... ......................
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>|12|b7|33|e8|66|4c|87|3c| ... |cc|cc|cc|cc|cc|cd|cd|
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>````````````````````````` ``````````````````````
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>大概意思一下，你可以想象每一个格子就是一个字节，每个格子都有编号，相邻的格子的编号也是相邻的。这个编号，你就可以理解为所谓的指针或者地址（这里不严格区分指针与地址）。那么当我需要获取某个位置的数据时，那么我们只需要一个编号（也就是地址）就知道在哪些格子里获取数据了，当然，写入数据也是一样的道理。&lt;/p></description></item><item><title>汇编语言入门四：打通C和汇编语言</title><link>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A804%E6%89%93%E9%80%9Ac%E5%92%8C%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A804%E6%89%93%E9%80%9Ac%E5%92%8C%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/</guid><description>&lt;h2 id="回顾">回顾&lt;a class="td-heading-self-link" href="#%e5%9b%9e%e9%a1%be" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>上回我们把汇编里涉及到的寄存器和内存访问相关的内容说了。先来梳理一下：&lt;/p>
&lt;ul>
&lt;li>寄存器是一些超级小的临时存储器，在CPU里面，存放CPU马上就要用到的数据或者刚处理完的结果&lt;/li>
&lt;li>要处理的数据太多，寄存器装不下了，需要更多寄存器，但是这玩意贵啊&lt;/li>
&lt;li>内存可以解决上述问题，但是内存相比寄存器要慢，优点是相对便宜，容量也大&lt;/li>
&lt;/ul>
&lt;h1 id="插曲c语言与汇编语言的关系">插曲：C语言与汇编语言的关系&lt;a class="td-heading-self-link" href="#%e6%8f%92%e6%9b%b2c%e8%af%ad%e8%a8%80%e4%b8%8e%e6%b1%87%e7%bc%96%e8%af%ad%e8%a8%80%e7%9a%84%e5%85%b3%e7%b3%bb" aria-label="Heading self-link">&lt;/a>&lt;/h1>
&lt;p>还有一些疑虑，先暂时解释一下。首先，C语言里编程里，我们从来没有关心过寄存器。汇编语言里突然冒出这么一个东西，学起来好难受。接下来的内容，我们先把C语言和汇编语言的知识，来一次大一统，帮助理解。&lt;/p>
&lt;p>首先我们来看一个C语言程序：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>int x, y, z;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>int main() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> x = 2;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> y = 3;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> z = x + y;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return z;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>考虑到我们的汇编教程才刚开始，我这里尽可能先简化C程序，这样稍后涉及到等价的汇编内容时所需的知识都是前面介绍过的。&lt;/p>
&lt;p>保存为test01.c文件，先编译运行这个程序：&lt;/p>
&lt;p>（注意，这里的gcc带了一个参数-m32，因为我们要编译出32位（x86）的可执行文件）&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>$ gcc -m32 test01.c -o test01
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ ./test01 ; echo $?
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>5
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>好了，在这里，我们的程序返回了一个值：5。&lt;/p>
&lt;p>好的，接下来我们看看如果我们要用汇编实现几乎相同的过程，该怎么做？&lt;/p>
&lt;p>首先，三个全局变量：&lt;/p>
&lt;p>int x, y, z;&lt;/p>
&lt;p>总得有吧。（这里之所以会用全局变量，是考虑到局部变量相关的汇编知识还未介绍，先将就一下，后续再说局部变量的内容）&lt;/p>
&lt;p>首先，在C语言里，你可以认为每个变量都会占用一定的内存空间，也就是说，这里的x、y、z分别都占用了一个“整型”也就是4字节的存储空间。&lt;/p>
&lt;p>上次我们介绍过在汇编里面访问内存的知识，当然，我们也知道了怎么在数据区划出一定的空间，这次我们就照搬前面提及的方法：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>global main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mov eax, 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>section .data
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>x dw 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>y dw 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>z dw 0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个程序就等价于下面的C代码：&lt;/p></description></item><item><title>汇编语言入门五：流程控制（一）</title><link>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A805%E6%B5%81%E7%A8%8B%E6%8E%A7%E5%88%B6%E4%B8%80/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A805%E6%B5%81%E7%A8%8B%E6%8E%A7%E5%88%B6%E4%B8%80/</guid><description>&lt;h2 id="回顾">回顾&lt;a class="td-heading-self-link" href="#%e5%9b%9e%e9%a1%be" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>前面说到过这样几个内容：&lt;/p>
&lt;ul>
&lt;li>几条简单的汇编指令&lt;/li>
&lt;li>寄存器&lt;/li>
&lt;li>内存访问&lt;/li>
&lt;/ul>
&lt;p>对应到C语言的学习过程中，无非就是这样几个内容：&lt;/p>
&lt;ul>
&lt;li>超级简单的运算&lt;/li>
&lt;li>变量&lt;/li>
&lt;/ul>
&lt;p>好了，到这里，我们继续接下来的话题，程序中的流程控制。&lt;/p>
&lt;p>文中涉及一些汇编代码，建议读者自行编程，通过动手实践来加深对程序的理解。&lt;/p>
&lt;h2 id="顺序执行">顺序执行&lt;a class="td-heading-self-link" href="#%e9%a1%ba%e5%ba%8f%e6%89%a7%e8%a1%8c" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>首先，最简单也最好理解的程序流程，便是从前往后的顺序执行。这个非常简单，还是举出前面的例子：&lt;/p>
&lt;p>现在有1000个计算题：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>99+10=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>32-20=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>14+21=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>47-9=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>87+3=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>86-8=
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>需要你一个个地从前往后计算，计算结果需要写在专门的答题卡上。当你每做完一个题，你需要继续做下一个题（这不是废话么）。&lt;/p>
&lt;p>那么问题来了，我每次计算完一个题目，回头寻找下一个题目的时候，到底哪一个题是我接下来要计算的呢？&lt;/p>
&lt;p>你可能会说：瞄一眼答题卡就知道了呀。这就尴尬了，计算机其实是比较傻的，它可没有“瞄一眼”这样的功能。&lt;/p>
&lt;p>那这样的话，如果是自己做1000个题目，为了保证做题的时候每一个动作都不是多余的，有一个比较好的办法，就是强行在脑子里记住刚刚那个题目的位置。一会儿回头的时候，就立马知道该继续做哪个题了。&lt;/p>
&lt;p>好了，那对于计算机来说呢？前面说到，你做计算题的时候临时留在脑子里的东西，就对应CPU里寄存器的数据。寄存器就充当了临时记住一些东西的功能。那么，在这里，CPU也是用的这个套路，在内部有一个寄存器，专门用来记录程序执行到哪里了。&lt;/p>
&lt;h3 id="cpu中的顺序执行过程">CPU中的顺序执行过程&lt;a class="td-heading-self-link" href="#cpu%e4%b8%ad%e7%9a%84%e9%a1%ba%e5%ba%8f%e6%89%a7%e8%a1%8c%e8%bf%87%e7%a8%8b" aria-label="Heading self-link">&lt;/a>&lt;/h3>
&lt;p>前面已经有了一个初步的结论，CPU里有一个寄存器专门存放“程序执行到哪里了”这样一个信息，而且这么做也是说得过去的，那就是：必须有一个东西记录当前程序执行到的位置，否则CPU执行完一条指令之后，就不知道接下来该干什么了。&lt;/p>
&lt;p>在x86体系结构的CPU里面，这个执行位置的信息，是保存在叫做eip的寄存器中的。不过很遗憾，这个寄存器比较特殊，无法通过mov指令进行修改，也就是说，这么写mov eip, 0x233是行不通的。&lt;/p>
&lt;p>（不要问我为什么，我也不知道，这都是人做出来的东西，支不支持就看人家的心情。反正Intel的CPU做出来就是这个样子的，你可以认为，Intel在做CPU的时候压根就没支持这个功能，他们觉得做了也没什么卵用。虽然你可能觉得有这个功能不是更好么，但是实际上，有时候刻意对功能施加一些限制，可以减少程序员写代码误操作的机会，eip这个东西，很关键）&lt;/p>
&lt;p>好了，介绍完eip的作用之后，再说一下细节的东西。在执行一条指令的时候，eip此时代表的是下一条指令的位置，eip里保存的就是下一条指令在内存中的地址。这样，CPU在执行完成一条指令之后，就直接根据eip的值，取出下一条指令，同时还要修改eip，往eip上加一个指令的长度，让它继续指向后一条指令。&lt;/p>
&lt;p>有了这样一个过程，CPU就能自动地去从前往后执行每一条指令了。而且，上述过程是在CPU中自动发生的，你写代码的时候根本不需要关心这个东西，只需要按照自己的思路从前往后写就是了。&lt;/p>
&lt;p>好了，这一段更多的是讲故事，明白CPU里面有个eip寄存器，它的功能很专一，就是用来表示程序现在执行到哪儿了。说得精确一点，eip一直都指向下一个要执行的指令，这一点是由CPU自己保证的。总之，只要CPU没坏，它就能给你保证eip的精确。&lt;/p>
&lt;h2 id="事情没那么简单">事情没那么简单&lt;a class="td-heading-self-link" href="#%e4%ba%8b%e6%83%85%e6%b2%a1%e9%82%a3%e4%b9%88%e7%ae%80%e5%8d%95" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>前面说了eip能记住程序执行的位置，那么CPU就能顺溜溜地一路走下去了。然而，世界并不是这么美好。因为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>if( a &amp;lt; 1 ){
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // some code ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>} else if( a &amp;gt;= 10 ) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> // yi xie dai ma ...
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>实际上有时候我们需要程序有一定的流程控制能力。就是有时候它不是老老实实按照顺序来执行的，中间可能会跳过一些代码，比如上述C代码中的a的值为100的时候。&lt;/p></description></item><item><title>汇编语言入门六：流程控制（二）</title><link>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A806%E6%B5%81%E7%A8%8B%E6%8E%A7%E5%88%B6%E4%BA%8C/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A806%E6%B5%81%E7%A8%8B%E6%8E%A7%E5%88%B6%E4%BA%8C/</guid><description>&lt;h2 id="回顾">回顾&lt;a class="td-heading-self-link" href="#%e5%9b%9e%e9%a1%be" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>前面说到在汇编语言中实现类似C语言if-else if-else这样的结构，&lt;/p>
&lt;p>实际上，在汇编里面，我们并不关心if了，取而代之的是两种基本的指令：&lt;/p>
&lt;ul>
&lt;li>比较&lt;/li>
&lt;li>跳转&lt;/li>
&lt;/ul>
&lt;p>这两种指令即可组成最基本的分支程序结构，虽然跳转指令非常多，但是我们已经有套路了，怎么跳转都不怕了。当然，在编程环境中仅有分支还不够的，我们知道C语言中除了分支结构之外，还有循环这个最基本也是最常用的形式。正好，这也是本节话题的主角。&lt;/p>
&lt;p>文中涉及一些汇编代码，建议读者自行编程，通过动手实践来加深对程序的理解。&lt;/p>
&lt;h2 id="拆散循环结构">拆散循环结构&lt;a class="td-heading-self-link" href="#%e6%8b%86%e6%95%a3%e5%be%aa%e7%8e%af%e7%bb%93%e6%9e%84" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>上回说到C语言中if这样的结构，在汇编里对应的是怎么回事，实质上，这就是分支结构的程序在汇编里的表现形式。&lt;/p>
&lt;p>实际上，循环结构相比分支结构，本质上，没有多少变化，仅仅是比较合跳转指令的组合的方式与顺序有所不同，所以形成了循环。&lt;/p>
&lt;p>当然，这个说法可能稍微拗口了一点。说得简单一点，循环的一个关键特点就是：&lt;/p>
&lt;ul>
&lt;li>程序在往回跳转&lt;/li>
&lt;/ul>
&lt;p>细细想，好像有道理哦，如果程序每到一个位置就往前跳转，那就是死循环，如果是在这个位置根据条件决定是否要向前跳转，那就是有条件的循环了。&lt;/p>
&lt;p>口说无凭，还是先来分析一下一个C语言的while循环：&lt;/p>
&lt;p>(Talk is chip, show your code!)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>int sum = 0;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>int i = 1;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>while( i &amp;lt;= 10 ) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sum = sum + i;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i = i + 1;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>想必这段程序多数人都非常熟悉了，当年自己第一次学习循环的时候就碰到这个题目，脑子短路了，心里总想着这不就是一个等差数列公式么，题目却强行出现在循环一章的后面，最后结果让人大跌眼睛，这是要我老老实实像SHAB一样去加啊。&lt;/p>
&lt;p>跑题了，先大致总结一下这个程序的关键部分到底在干什么：&lt;/p>
&lt;ul>
&lt;li>1. 比较i和10的大小&lt;/li>
&lt;li>2. 如果i &amp;lt;= 10则执行代码块，并回到(1)&lt;/li>
&lt;li>3. 如果不满足 i &amp;lt;= 10，则跳过代码块&lt;/li>
&lt;/ul>
&lt;p>好了，按照这个逻辑，在C语言中不使用循环怎么实现？其实也非常简单：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>int sum = 10;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>int i = 1;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>_start:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>if( i &amp;lt;= 10 ) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sum = sum + i;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> i = i + 1;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> goto _start;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这还不够，我们还得做一次变形，为什么呢？回想一下前面说的分之程序在汇编里的情况：&lt;/p></description></item><item><title>汇编语言入门七：函数调用（一）</title><link>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A807%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E4%B8%80/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A807%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E4%B8%80/</guid><description>&lt;p>最近忙了一阵，好几天没更了，不好意思，我来晚了。&lt;/p>
&lt;p>转入正题，当在汇编中进行函数调用，是一种什么样的体验？&lt;/p>
&lt;h1 id="想象">想象&lt;a class="td-heading-self-link" href="#%e6%83%b3%e8%b1%a1" aria-label="Heading self-link">&lt;/a>&lt;/h1>
&lt;p>想象你在计算一个非常复杂的数学题，在算到一半的时候，你需要一个数据，而这个数据需要套用一个比较复杂的公式才能算出来，怎么办？&lt;/p>
&lt;p>你不得不把手中的事情停下来，先去套公式、代入数值然后&amp;hellip;最后，算出结果来了。&lt;/p>
&lt;p>这时候你继续开始攻克这个困难题目的剩下部分。&lt;/p>
&lt;h2 id="用脑子想">用脑子想&lt;a class="td-heading-self-link" href="#%e7%94%a8%e8%84%91%e5%ad%90%e6%83%b3" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>刚刚说的这个过程，可能有点小问题，尤其是对脑子不太好使的人来说。想象你做题目做到一半的时候，记忆力已经有点不好使了，中间突然停下来去算一个复杂的公式，然后回来，诶？我刚刚算到哪了？我刚刚想到哪了？我刚刚算了些什么结果？&lt;/p>
&lt;p>在你工作切换的时候，很容易回头来就忘记了刚刚做的部分事情。这时候，为了保证你套完复杂的公式，把结果拿回来继续算题目的时候不会出差错，你需要把刚才计算题目过程中的关键信息写在纸上。&lt;/p>
&lt;h2 id="用cpu想">用CPU想&lt;a class="td-heading-self-link" href="#%e7%94%a8cpu%e6%83%b3" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>刚刚去套用一个复杂的公式计算某个数据的情景，就类似在计算机里进行函数调用的情景。&lt;/p>
&lt;p>程序需要一个结果，这个结果需要通过一个比较复杂的过程进行计算。这时候，编程人员会考虑将这个独立的复杂过程提取为单独的函数。&lt;/p>
&lt;p>而在发生函数调用的时候，CPU就像是先暂停当前所做的事情，转去做那个复杂的计算，算完了之后又跳回来继续整个计算。就像你做题的过程中去套了一个公式计算数据一样。&lt;/p>
&lt;p>但是在去套用公式之前，你需要做一些准备。首先，默默记下现在这个题目算到哪一步了，一会套完公式回来接着做；默默记下现在计算出来的一些结果，一会可能还会用到；套用公式需要些什么数据，先记下来，代公式的时候直接代入计算，算出来的结果也需要记在脑子里，回头需要使用。&lt;/p>
&lt;p>在CPU里面，也需要这几个过程。&lt;/p>
&lt;p>第一个，记下自己现在做事情做到哪里了，一会儿套完公式回来接着做，这也就是CPU在进行函数调用时的现场保存操作，CPU也需要记下自己当前执行到哪里了。&lt;/p>
&lt;p>默默记下一些在套用公式的时候需要用到的数据，然后去套公式了。这也就是程序中在调用函数的时候进行参数传递的过程。&lt;/p>
&lt;p>然后开始执行函数，等函数执行完了，就需要把结果记下来，回去继续刚才要用到数据的那个地方继续算。这也就是函数调用后返回的动作，这个记下的结果就是返回值。&lt;/p>
&lt;h1 id="开撸">开撸&lt;a class="td-heading-self-link" href="#%e5%bc%80%e6%92%b8" aria-label="Heading self-link">&lt;/a>&lt;/h1>
&lt;p>说了那么多故事，那么函数调用要干些啥应该就说清楚了。总结一下大概就这么几个事：&lt;/p>
&lt;ul>
&lt;li>保存现场（一会好回来接着做）&lt;/li>
&lt;li>传递参数（可选，套公式的时候需要些什么数据）&lt;/li>
&lt;li>返回（把计算结果带回来，接着刚才的事）&lt;/li>
&lt;/ul>
&lt;p>到这里，我们先来一个事例代码，就着代码去发现函数调用中的套路：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>global main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>eax_plus_1s:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> add eax, 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ebx_plus_1s:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> add ebx, 1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>main:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mov eax, 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mov ebx, 0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> call eax_plus_1s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> call eax_plus_1s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> call ebx_plus_1s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> add eax, ebx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ret
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>首先，运行程序，得到结果：3。&lt;/p></description></item><item><title>汇编语言入门八：函数调用（二）</title><link>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A808%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E4%BA%8C/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A808%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E4%BA%8C/</guid><description>&lt;h2 id="回顾">回顾&lt;a class="td-heading-self-link" href="#%e5%9b%9e%e9%a1%be" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>上回说道，x86汇编中专门提供了两个指令call和ret，用于实现函数调用的效果。实际上函数调用就是程序跳转，只是在跳转之前，CPU会保存当前所在的位置（即返回地址），当函数返回时，又可以从调用的位置恢复。返回地址保存在一个叫做“堆栈”的地方，堆栈中可以保存很多个返回地址，同时借助于堆栈的进出逻辑，还能实现函数嵌套、递归等效果。&lt;/p>
&lt;p>同时前面还简单地提到了函数调用过程中的参数和返回值的传递过程。实际上，在汇编语言中，函数调用的参数和返回值均可以通过寄存器来传送，只要函数内外相互配合，就可以精确地进行参数和返回值传递。&lt;/p>
&lt;h1 id="没那么简单">没那么简单&lt;a class="td-heading-self-link" href="#%e6%b2%a1%e9%82%a3%e4%b9%88%e7%ae%80%e5%8d%95" aria-label="Heading self-link">&lt;/a>&lt;/h1>
&lt;p>到这里，看起来好像函数调用的基本要素都有了，但实际上还是有一些问题的。比如说递归调用这样的场景。通过对递归的研究，你也就能明白前面说到的函数调用机制存在什么样致命的问题。&lt;/p>
&lt;p>好了，先说下，这部分内容，很关键。&lt;/p>
&lt;p>举个例子，通过递归调用来计算斐波那契数列中的某一项，用高级语言编写已经非常容易：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>int fibo(int n) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> if(n == 1 || n == 2) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return 1;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return fibo(n - 1) + fibo(n - 2);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们来进行一波改造，改造成接近汇编的形式：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>int fibo(int n) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> if(n == 1) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return 1;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> if(n == 2) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return 1;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int x = n - 1;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int y = n - 2;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int a = fibo(x);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int b = fibo(y);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int c = a + b;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return c;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>拆分成这样之后，就能够比较方便地和汇编对应起来了，再改造一下，把变量名全都换成寄存器名，就能够看得更清楚了（先约定eax寄存器作为函数的第一个参数，通过eax也用来传递返回值）：&lt;/p></description></item><item><title>汇编语言入门九：总结与后续（闲扯）</title><link>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A809%E6%80%BB%E7%BB%93%E4%B8%8E%E5%90%8E%E7%BB%AD%E9%97%B2%E6%89%AF/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.azhw.com.cn/%E7%BC%96%E7%A8%8B/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/%E6%B1%87%E7%BC%96/%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A809%E6%80%BB%E7%BB%93%E4%B8%8E%E5%90%8E%E7%BB%AD%E9%97%B2%E6%89%AF/</guid><description>&lt;h2 id="回顾">回顾&lt;a class="td-heading-self-link" href="#%e5%9b%9e%e9%a1%be" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>前面扯了一些个汇编语言的内容，想必读者也应该有了大致的了解。笔者比打算写全面的汇编相关的内容，毕竟目前已经有不少相关的资料了。本入门系列的目的就在于：入门。&lt;/p>
&lt;p>完成了入门的任务，入门系列就暂告一段落了。在此，先来对前面提及的内容做一些回顾。前面说到的各项内容大概涉及：&lt;/p>
&lt;ul>
&lt;li>环境配置&lt;/li>
&lt;li>寄存器&lt;/li>
&lt;li>内存访问&lt;/li>
&lt;li>流程控制&lt;/li>
&lt;li>函数调用&lt;/li>
&lt;li>反汇编&lt;/li>
&lt;li>调试&lt;/li>
&lt;/ul>
&lt;p>我想，学习汇编中比较容易犯难的几个环节，大致也都覆盖到了。并且教程中也提供了可运行的实例，供读者在学习之后用于验证。&lt;/p>
&lt;h1 id="学汇编到底学什么">学汇编到底学什么&lt;a class="td-heading-self-link" href="#%e5%ad%a6%e6%b1%87%e7%bc%96%e5%88%b0%e5%ba%95%e5%ad%a6%e4%bb%80%e4%b9%88" aria-label="Heading self-link">&lt;/a>&lt;/h1>
&lt;p>我想，很多新手在了解汇编语言的时候，难免会遇到各种蛋疼的问题。大致有这样一些情况：&lt;/p>
&lt;ul>
&lt;li>有参考书，虽然书中的知识体系全面，但是内容多却晦涩难懂&lt;/li>
&lt;li>内容老旧，一时找不到合适的试验环境来验证，仅仅停留于书本，缺乏强烈直观的感受&lt;/li>
&lt;li>各种规范各种环境乱七八糟不统一，讨论汇编时太依赖于特定环境，进一步加大了动手验证的难度&lt;/li>
&lt;/ul>
&lt;p>这无疑是给学习者泼来一盆冷水，本来只有3分的热情被灭掉了余下0.3分。&lt;/p>
&lt;p>其实学习汇编语言，和学习C语言就有所不同了。你不用想着以后用汇编进行编程，学习汇编语言的首要目标是理解CPU运行程序的时候到底在干什么，你编写的C程序或者其他什么代码在CPU的眼里到底是个什么玩意儿，你能够通过汇编去分析程序的行为，解释一些高级语言下无法解释的现象，等等，才是学习的目标。&lt;/p>
&lt;p>也就是说，学习汇编语言，应该抱着理解的目标去学习，理解透彻便足矣，无需做到能够流利地用汇编进行编程。&lt;/p>
&lt;p>即便如此，笔者前面所述的各方面入门内容也仅仅是入门。不同人有不同的学习历程、只是背景，笔者很难保证自己觉得足够的入门教程，能够让每个读者都刚好受用。鉴于此，笔者将列出在了解了入门内容后需要关心的内容。&lt;/p>
&lt;h1 id="后续">后续&lt;a class="td-heading-self-link" href="#%e5%90%8e%e7%bb%ad" aria-label="Heading self-link">&lt;/a>&lt;/h1>
&lt;p>前面也有提到，学习汇编所需要侧重的是理解，而非熟练地编代码。汇编语言更像是一套理论知识，用于分析和解释程序在足够底层时的行为和现象。&lt;/p>
&lt;p>与其说学习汇编，不如说是揭开高级语言的面纱，深入到更底层的地方去了解计算机原理，靠近计算机程序的本质。深入理解底层原理，有助于建立对计算机更系统的更深入的认识，面对一些看似诡异的问题时能心中有数，有方法有思路有理据去分析和解决。&lt;/p>
&lt;p>要做到这一点，是需要循序渐进慢慢学习的。读者可在后续关注这样一些内容：&lt;/p>
&lt;h2 id="继续学习汇编语言">继续学习汇编语言&lt;a class="td-heading-self-link" href="#%e7%bb%a7%e7%bb%ad%e5%ad%a6%e4%b9%a0%e6%b1%87%e7%bc%96%e8%af%ad%e8%a8%80" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>笔者前面所述的入门内容只能保证覆盖了核心内容，并未覆盖到汇编语言相关的方方面面。读者至少要让自己学习汇编知识覆盖面达到足以形成图灵完备的最小集合。&lt;/p>
&lt;p>可以从分析高级语言（比如C）去学习汇编语言，如前面所述的反汇编。关注这样一些内容：&lt;/p>
&lt;ul>
&lt;li>指令&lt;/li>
&lt;li>寄存器&lt;/li>
&lt;li>内存访问&lt;/li>
&lt;li>条件跳转&lt;/li>
&lt;li>堆栈&lt;/li>
&lt;li>程序状态&lt;/li>
&lt;/ul>
&lt;p>这些概念不仅在x86体系下，在ARM或是MIPS体系下也适用。仅仅是在不同环境下有不同的思路，有不同的表现形式，但是，核心的概念都是一致的。&lt;/p>
&lt;h2 id="计算机组成原理">计算机组成原理&lt;a class="td-heading-self-link" href="#%e8%ae%a1%e7%ae%97%e6%9c%ba%e7%bb%84%e6%88%90%e5%8e%9f%e7%90%86" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>到这里，便是让你对所有计算机系统的认识有一个大统一。上面说到不同CPU平台下都有相当的共性。组成原理便是对所有计算机系统的大统一，不同平台只是在根据自己的目标特点和偏好在对计算机原理进行应用而已。&lt;/p>
&lt;p>组成原理会告诉你，计算机在电路这个层面的本质是什么。&lt;/p>
&lt;p>有了上述的汇编、组成原理的认识，同时也还应该去了解计算机操作系统。这里所谓的操作系统是指站在专业的角度，讨论操作系统本质上是在做什么事情，解决什么问题。&lt;/p>
&lt;p>这些基础知识将会作为今后进一步学习计算机的坚实基础，基于对计算机、对操作系统的理解，计算机中的一切都将不再神秘，其本质都不过如此，基于自己所学去分析、去理解即可。&lt;/p>
&lt;h1 id="学习方法">学习方法&lt;a class="td-heading-self-link" href="#%e5%ad%a6%e4%b9%a0%e6%96%b9%e6%b3%95" aria-label="Heading self-link">&lt;/a>&lt;/h1>
&lt;p>笔者在学习的过程中，也尝试过总结适合自己的方法，在此也谈一谈自己学习的方式。&lt;/p>
&lt;p>虽然微积分揭示了自然现象和数学之间很多本质的东西，但是让一个小学生直接学习的话，难免会困难重重。这里的问题并不在于知识体系不够全面、不够严谨，而在于学习者对微积分没有基本的感性认识，根本不知道是个啥玩意儿（计算期末考试分数的新方法？），即使记住了这些公式和证明，但自身难以去自行演绎，这其实是无效的学习。&lt;/p>
&lt;p>所以，借助此例，结合自身经历，笔者认为学习的时候应该先关注下面一些方面：&lt;/p>
&lt;h2 id="寻求感性认识">寻求感性认识&lt;a class="td-heading-self-link" href="#%e5%af%bb%e6%b1%82%e6%84%9f%e6%80%a7%e8%ae%a4%e8%af%86" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>学习一个陌生的东西，我都会尝试先寻求一个感性的认识，感性认识达到什么程度呢？就是做到自己能够用一些简洁的白话，把自己接下来要学习的东西到底是什么玩意儿，给解释清楚，最好能够做到让不明白的人也能听明白。&lt;/p>
&lt;p>比如什么是计算机？你能想到的就是你面对的那一台电脑，里面有丰富的的软件，可以做很多有趣的事情。&lt;/p>
&lt;h2 id="了解边界">了解边界&lt;a class="td-heading-self-link" href="#%e4%ba%86%e8%a7%a3%e8%be%b9%e7%95%8c" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>笔者所谓的边界，即接下来要学习的这个东西，能做什么，不能做什么。学习之前明白这一点也非常重要，基于此，便能有一个清晰的目标，对学习后会面对的问题也有一定的底。&lt;/p>
&lt;p>比如计算机是无法直接驱动一个火箭上天的，但是能够对航天器的行为进行控制。&lt;/p>
&lt;h2 id="让学习可验证">让学习可验证&lt;a class="td-heading-self-link" href="#%e8%ae%a9%e5%ad%a6%e4%b9%a0%e5%8f%af%e9%aa%8c%e8%af%81" aria-label="Heading self-link">&lt;/a>&lt;/h2>
&lt;p>学习过程中自己会思考，会想出很多问题，但是这些问题并非都能从书中找到答案。而在学习过程中要刻意的去养成对知识、对自己的新想法进行验证的习惯。这里面大致是这样的过程：&lt;/p>
&lt;ul>
&lt;li>脑子里冒出来一个想法，可能是疑惑，可能是矛盾&lt;/li>
&lt;li>猜想一种最可能的情况来解释&lt;/li>
&lt;li>通过实践去验证自己的猜想&lt;/li>
&lt;li>总结、回顾（验证后的结果不能解释自己的猜想，会去重新猜）&lt;/li>
&lt;/ul>
&lt;p>这里的动手验证不是说真的得去拿个锤子砸钉子，而是说这里的分析、思考、试验、演绎等过程需要落到实处，而不是看到了某个模糊的说法，就这样糊弄过去了。&lt;/p></description></item></channel></rss>