<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>PrideLzh</title>
  
  <subtitle>一蓑烟雨任平生</subtitle>
  <link href="https://blog.pridelzh.top/atom.xml" rel="self"/>
  
  <link href="https://blog.pridelzh.top/"/>
  <updated>2025-11-10T07:56:16.000Z</updated>
  <id>https://blog.pridelzh.top/</id>
  
  <author>
    <name>pridelizihao</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>a good app</title>
    <link href="https://blog.pridelzh.top/posts/a-good-app.html"/>
    <id>https://blog.pridelzh.top/posts/a-good-app.html</id>
    <published>2025-11-10T07:56:16.000Z</published>
    <updated>2025-11-10T07:56:16.000Z</updated>
    
    <content type="html"><![CDATA[<p>很久没有更新我的博客了，今天心有所感，新发一篇吧</p><p>我最近偶尔发现了一下非常好用的网站和app，二者搭配起来一定会有很好的效果，帮助我实现了图书自由</p><p>首先要介绍的是网站，zlibrary，一个免费的电子书下载网站，我之前一直不知道，最近才发现，里面的电子书种类非常丰富，而且下载速度非常快，而且支持多种格式，比如pdf，epub，mobi等，而且还可以下载电子书中的图片，非常方便，而且网站界面也非常简洁，非常容易使用，我强烈推荐大家去尝试一下</p><h2 id="zlibrary"><a href="#zlibrary" class="headerlink" title="zlibrary"></a>zlibrary</h2><p>我先把网址放在这里了 <a href="https://z-lib.id/">https://z-lib.id/</a> ，大家可以去尝试一下</p><h2 id="微信读书"><a href="#微信读书" class="headerlink" title="微信读书"></a>微信读书</h2><p>然后是微信读书，微信读书是一款非常优秀的读书app，我之前一直不知道，最近才发现，里面的电子书种类也非常丰富，而且下载速度也非常快，而且支持多种格式，比如pdf，epub，mobi等，而且还可以下载电子书中的图片，非常方便，而且网站界面也非常简洁，非常容易使用，我强烈推荐大家去尝试一下</p><p>我们把而且结合，在手机上或者说平板或者说电脑上，就可以实现图书自由了，而且还可以随时随地阅读，非常方便，我强烈推荐大家去尝试一下</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;很久没有更新我的博客了，今天心有所感，新发一篇吧&lt;/p&gt;
&lt;p&gt;我最近偶尔发现了一下非常好用的网站和app，二者搭配起来一定会有很好的效果，帮助我实现了图书自由&lt;/p&gt;
&lt;p&gt;首先要介绍的是网站，zlibrary，一个免费的电子书下载网站，我之前一直不知道，最近才发现，里面</summary>
      
    
    
    
    <category term="apprecommend" scheme="https://blog.pridelzh.top/categories/apprecommend/"/>
    
    
    <category term="app" scheme="https://blog.pridelzh.top/tags/app/"/>
    
  </entry>
  
  <entry>
    <title>浅谈户晨风事件</title>
    <link href="https://blog.pridelzh.top/posts/qthcf.html"/>
    <id>https://blog.pridelzh.top/posts/qthcf.html</id>
    <published>2025-09-24T15:15:25.000Z</published>
    <updated>2025-09-24T15:15:25.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="浅谈户晨风事件"><a href="#浅谈户晨风事件" class="headerlink" title="浅谈户晨风事件"></a>浅谈户晨风事件</h1><p>最近对于户晨风事件，我有一些看法，在此与大家分享一下。</p><p>户晨风最近在各个平台已经被封杀了，原因可能是因为他总是强行将人打上标签，比如说，将手机操作系统，安卓和苹果加上人的身上，<br>新早了很多的新词语，比如说，安卓思维，苹果房子，苹果学校。</p><p>这样将人划分为不同的阶层固然是不对的，但是，我现在还没有想好怎么反驳这个观点，让我们先搁置吧</p><p>户晨风还说了三件套，即为用苹果手机，开特斯拉，逛山姆超市，这三件套，他称之为“三件套”，并以此作为标准，将人划分为不同的阶层。</p><p>但是我认为，这个貌似是有道理的。我一直认为，普通人应该在自己的能力范围内，其体验新的技术等等的，就比如说手机电脑等电子产品，从某种程度上来说，这是普通人最能够和富有的人同时享有的科技产品了，你可能住不起几百平米的大平层，开不起几百万的豪车，但是手机电脑这种东西，咬咬牙还是可以的，这绝对不是虚荣，而是对于自己的尊重。</p><p>而且，就我本人的使用体验来讲，苹果的产品往往可以长时间使用而不发生卡顿，其他的产品往往做不到，就比如我手上的一个MacBook air，这是一个十年的老物件，但是现在用来上网的话，还是十分流畅的，这个还是在4GB内存的前提下，如果说是Windows，大概率做不到，还有我同学的iPhone 13，四年前的物件了，先在还是可以正常使用，升级最新系统，但是我的华为P60，才用了三年，虽然正常使用还是比较流畅的，但是很久没有升级系统了，有时候就会卡顿，而且电池续航也不如以前了，甚至说在外面骚哥自行车五分钟都没有扫开来，这个确实有点离谱了。</p><p>如果以后有机会，我觉得自己可以尝试一下apple生态，毕竟，电子产品，还是苹果的比较稳定，也算是人生的一个体验，在这里立一个flag。</p><p>其他的，我们以后再聊。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;浅谈户晨风事件&quot;&gt;&lt;a href=&quot;#浅谈户晨风事件&quot; class=&quot;headerlink&quot; title=&quot;浅谈户晨风事件&quot;&gt;&lt;/a&gt;浅谈户晨风事件&lt;/h1&gt;&lt;p&gt;最近对于户晨风事件，我有一些看法，在此与大家分享一下。&lt;/p&gt;
&lt;p&gt;户晨风最近在各个平台已经被封杀</summary>
      
    
    
    
    <category term="心得" scheme="https://blog.pridelzh.top/categories/%E5%BF%83%E5%BE%97/"/>
    
    
    <category term="说真话" scheme="https://blog.pridelzh.top/tags/%E8%AF%B4%E7%9C%9F%E8%AF%9D/"/>
    
  </entry>
  
  <entry>
    <title>人的交友欲望和交友厌恶性</title>
    <link href="https://blog.pridelzh.top/posts/1e547de5.html"/>
    <id>https://blog.pridelzh.top/posts/1e547de5.html</id>
    <published>2025-09-12T02:00:00.000Z</published>
    <updated>2025-09-12T02:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="人的交友欲望和交友厌恶性"><a href="#人的交友欲望和交友厌恶性" class="headerlink" title="人的交友欲望和交友厌恶性"></a>人的交友欲望和交友厌恶性</h1><h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>人类是社会性动物，交友是人与生俱来的需求。然而，在实际的社交过程中，我们既会感受到强烈的交友欲望，也会产生对某些社交场合或人群的厌恶性。在当前社会背景下，这种复杂的心理现象更加值得我们深入探讨。</p><h2 id="交友欲望的根源"><a href="#交友欲望的根源" class="headerlink" title="交友欲望的根源"></a>交友欲望的根源</h2><h3 id="社会认同感"><a href="#社会认同感" class="headerlink" title="社会认同感"></a>社会认同感</h3><p>人需要通过与他人建立联系来获得归属感和认同感。这种需求在心理学上被称为“社会认同”。当个体感到被群体接纳时，会产生积极的情感体验，从而增强自我价值感。</p><h3 id="情感支持"><a href="#情感支持" class="headerlink" title="情感支持"></a>情感支持</h3><p>朋友可以提供情感上的支持和安慰，帮助我们应对生活中的压力和挑战。这种支持不仅有助于心理健康，还能提升生活的幸福感。</p><h3 id="信息交流"><a href="#信息交流" class="headerlink" title="信息交流"></a>信息交流</h3><p>通过与他人交流，我们可以获取新的知识和信息，拓宽视野。这种信息交换的过程也是个人成长的重要途径。</p><h2 id="交友厌恶性的成因"><a href="#交友厌恶性的成因" class="headerlink" title="交友厌恶性的成因"></a>交友厌恶性的成因</h2><h3 id="个性差异"><a href="#个性差异" class="headerlink" title="个性差异"></a>个性差异</h3><p>每个人的性格、兴趣和价值观都有所不同。当遇到与自己格格不入的人时，可能会产生排斥感。这种差异可能导致沟通障碍，进而引发厌恶性情绪。</p><h3 id="社交焦虑"><a href="#社交焦虑" class="headerlink" title="社交焦虑"></a>社交焦虑</h3><p>一些人在社交场合中会感到紧张和不安，担心自己的表现不佳或被他人评判。这种社交焦虑可能导致他们避免参与社交活动，甚至对交友产生厌恶性反应。</p><h3 id="负面经历"><a href="#负面经历" class="headerlink" title="负面经历"></a>负面经历</h3><p>过去的负面社交经历，如被背叛、误解或伤害，可能让人对未来的社交关系产生不信任感，从而表现出厌恶性行为。</p><h2 id="当前社会背景下的交友新趋势"><a href="#当前社会背景下的交友新趋势" class="headerlink" title="当前社会背景下的交友新趋势"></a>当前社会背景下的交友新趋势</h2><h3 id="数字化社交的兴起"><a href="#数字化社交的兴起" class="headerlink" title="数字化社交的兴起"></a>数字化社交的兴起</h3><p>随着互联网和社交媒体的发展，人们的交友方式发生了显著变化。线上社交平台为人们提供了更多结识新朋友的机会，但也带来了信息过载和虚假社交的问题。</p><h3 id="“搭子”文化的流行"><a href="#“搭子”文化的流行" class="headerlink" title="“搭子”文化的流行"></a>“搭子”文化的流行</h3><p>当前社会中，“搭子”文化逐渐兴起。这种基于共同兴趣或需求的浅社交关系，满足了年轻人对陪伴和社交的需求，同时也体现了他们对边界感和独立性的重视。</p><h3 id="群体性孤独与社交恐惧"><a href="#群体性孤独与社交恐惧" class="headerlink" title="群体性孤独与社交恐惧"></a>群体性孤独与社交恐惧</h3><p>尽管社交工具不断升级，但部分年轻人却感到更加孤独。社交恐惧和对亲密关系的不信任，使得他们更倾向于选择临时性、短暂性的社交关系，以避免情感上的风险。</p><h2 id="平衡交友欲望与厌恶性"><a href="#平衡交友欲望与厌恶性" class="headerlink" title="平衡交友欲望与厌恶性"></a>平衡交友欲望与厌恶性</h2><h3 id="自我认知"><a href="#自我认知" class="headerlink" title="自我认知"></a>自我认知</h3><p>了解自己的性格特点和社交偏好，明确自己在交友中的需求和底线。这有助于我们在社交中保持真实，同时避免不必要的冲突。</p><h3 id="选择性社交"><a href="#选择性社交" class="headerlink" title="选择性社交"></a>选择性社交</h3><p>不必强迫自己与所有人建立深厚的友谊。可以选择那些与自己价值观相近、能够相互尊重和支持的人进行深入交往。</p><h3 id="积极应对"><a href="#积极应对" class="headerlink" title="积极应对"></a>积极应对</h3><p>对于社交焦虑或负面经历带来的影响，可以通过心理咨询、自我调节等方式积极应对，逐步克服这些障碍。</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>交友欲望和交友厌恶性是人类社交心理的两个重要方面。在当前社会背景下，理解它们的成因和影响，可以帮助我们更好地处理人际关系，建立健康、和谐的社交网络。在这个过程中，保持自我认知和选择性社交是非常关键的。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;人的交友欲望和交友厌恶性&quot;&gt;&lt;a href=&quot;#人的交友欲望和交友厌恶性&quot; class=&quot;headerlink&quot; title=&quot;人的交友欲望和交友厌恶性&quot;&gt;&lt;/a&gt;人的交友欲望和交友厌恶性&lt;/h1&gt;&lt;h2 id=&quot;引言&quot;&gt;&lt;a href=&quot;#引言&quot; class=&quot;</summary>
      
    
    
    
    
    <category term="交友" scheme="https://blog.pridelzh.top/tags/%E4%BA%A4%E5%8F%8B/"/>
    
  </entry>
  
  <entry>
    <title>LSTM简介</title>
    <link href="https://blog.pridelzh.top/posts/lstm1.html"/>
    <id>https://blog.pridelzh.top/posts/lstm1.html</id>
    <published>2025-09-06T10:47:00.000Z</published>
    <updated>2025-09-06T10:47:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>Long ShortTerm 网络——一般就叫做LSTM——是一种RNN特殊的类型，可以学习长期依赖信息。当然，LSTM和基线RNN并没有特别大的结构不同，但是它们用了不同的函数来计算隐状态。</p><p>LSTM的“记忆”我们叫做细胞&#x2F;cells，你可以直接把它们想做黑盒，这个黑盒的输入ht-1为前状态和当前输入xt。这些“细胞”会决定哪些之前的信息和状态需要保留&#x2F;记住，而哪些要被抹去。实际的应用中发现，这种方式可以有效地保存很长时间之前的关联信息。</p><p>简而言之，LSTM可以自动保留有效信息，舍弃无效信息，这样的一个机制保证了它可以由长期记忆。</p><hr><h2 id="1-1-什么是LSTM网络"><a href="#1-1-什么是LSTM网络" class="headerlink" title="1.1 什么是LSTM网络"></a>1.1 <strong>什么是LSTM网络</strong></h2><p>举个例子，当你想在网上购买生活用品时，一般都会查看一下此前已购买该商品用户的评价。</p><p>当你浏览评论时，你的大脑下意识地只会记住重要的关键词，比如“amazing”和“awsome”这样的词汇，而不太会关心“this”、“give”、“all”、“should”等字样。如果朋友第二天问你用户评价都说了什么，那你可能不会一字不漏地记住它，而是会说出但大脑里记得的主要观点，比如“下次肯定还会来买”，那其他一些无关紧要的内容自然会从记忆中逐渐消失。</p><p>而这基本上就像是 LSTM 或 GRU 所做的那样，它们可以学习只保留相关信息来进行预测，并忘记不相关的数据。简单说，因记忆能力有限，记住重要的，忘记无关紧要的。</p><p><strong>所以说，LSTM 就是会有选择地记忆</strong></p><p>LSTM由Hochreiter&amp;Schmidhuber(1997)提出，并在近期被AlexGraves进行了改良和推广。在很多问题，LSTM都取得相当巨大的成功，并得到了广泛的使用。<br>LSTM通过刻意的设计来避免长期依赖问题。记住长期的信息在实践中是LSTM的默认行为，而非需要付出很大代价才能获得的能力！</p><p>所有RNN都具有一种重复神经网络模块的链式的形式。在标准的RNN中，这个重复的模块只有一个非常简单的结构，例如一个tanh层。</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906184903742.webp" alt="image.png"></p><p>激活函数 Tanh 作用在于帮助调节流经网络的值，使得数值始终限制在 -1 和 1 之间。</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906184922955.webp" alt="image.png"></p><p>LSTM的结构基本类似，但是重复的模块拥有不同的结构，即RNN是重复单一的神经网络层，LSTM包含四个交互层，三个Sigmoid 和一个tanh层，并以一种非常特殊的方式进行交互。</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906184943880.webp" alt="image.png"></p><p>上图中，σ表示的Sigmoid 激活函数与 tanh 函数类似，不同之处在于 sigmoid 是把值压缩到0<del>1 之间而不是 -1</del>1 之间。这样的设置有助于更新或忘记信息：</p><ul><li>因为任何数乘以 0 都得 0，这部分信息就会剔除掉；</li><li>同样的，任何数乘以 1 都得到它本身，这部分信息就会完美地保存下来</li></ul><p>相当于要么是1则记住，要么是0则忘掉，所以还是这个原则：<strong>因记忆能力有限，记住重要的，忘记无关紧要的。</strong></p><h2 id="1-2-LSTM的核心思想"><a href="#1-2-LSTM的核心思想" class="headerlink" title="1.2 LSTM的核心思想"></a>1.2 LSTM的核心思想</h2><p>LSTM的关键就是细胞状态，水平线在图上方贯穿运行。</p><p>细胞状态类似于传送带。直接在整个链上运行，只有一些少量的线性交互。信息在上面流传保持不变会很容易。</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906185007674.webp" alt="image.png"></p><p>LSTM有通过精心设计的称作为“门”的结构来去除或者增加信息到细胞状态的能力。门是一种让信息选择式通过的方法。他们包含一个sigmoid神经网络层和一个pointwise乘法的非线性操作。</p><p>如此，0代表“不许任何量通过”，1就指“允许任意量通过”！从而使得网络就能了解哪些数据是需要遗忘，哪些数据是需要保存。</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906185054560.webp" alt="image.png"></p><p>LSTM拥有三种类型的门结构：遗忘门&#x2F;忘记门、输入门和输出门，来保护和控制细胞状态。</p><p>增加一篇博客参考：<a href="https://www.cnblogs.com/xuruilong100/p/8506949.html#%E7%90%86%E8%A7%A3-lstm-%E7%BD%91%E7%BB%9C%EF%BC%8C%E5%8F%AF%E4%BB%A5%E7%9C%8B%E7%9C%8B">https://www.cnblogs.com/xuruilong100/p/8506949.html#%E7%90%86%E8%A7%A3-lstm-%E7%BD%91%E7%BB%9C，可以看看</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Long ShortTerm 网络——一般就叫做LSTM——是一种RNN特殊的类型，可以学习长期依赖信息。当然，LSTM和基线RNN并没有特别大的结构不同，但是它们用了不同的函数来计算隐状态。&lt;/p&gt;
&lt;p&gt;LSTM的“记忆”我们叫做细胞&amp;#x2F;cells，你可以直接把</summary>
      
    
    
    
    <category term="机器学习,深度学习" scheme="https://blog.pridelzh.top/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="LSTM" scheme="https://blog.pridelzh.top/tags/LSTM/"/>
    
  </entry>
  
  <entry>
    <title>RNN神经网络</title>
    <link href="https://blog.pridelzh.top/posts/rnn.html"/>
    <id>https://blog.pridelzh.top/posts/rnn.html</id>
    <published>2025-09-06T08:59:00.000Z</published>
    <updated>2025-09-06T08:59:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="RNN"><a href="#RNN" class="headerlink" title="RNN"></a>RNN</h1><h2 id="1-1-单层网络到RNN结构"><a href="#1-1-单层网络到RNN结构" class="headerlink" title="1.1 单层网络到RNN结构"></a>1.1 单层网络到RNN结构</h2><p>在学习LSTM之前，得先学习RNN，而在学习RNN之前，首先要了解一下最基本的单层网络，它的结构如下图所示：</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906170029830.webp" alt="image.png"></p><p>输入是x，经过变换Wx+b和激活函数f，得到输出y。相信大家对这个已经非常熟悉了。</p><p>在实际应用中，我们还会遇到很多序列形的数据：</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906170339286.webp" alt="image.png"></p><p>如：</p><ol><li>自然语言处理问题。x1可以看做是第一个单词，x2可以看做是第二个单词，依次类推</li><li>语音处理。此时，x1、x2、x3……是每帧的声音信号。</li><li>时间序列问题。例如每天的股票价格等等</li></ol><p>而其中，序列形的数据就不太好用原始的神经网络处理了。</p><p>RNN引入了隐状态h（hidden state）的概念，<strong>隐状态h可以对序列形的数据提取特征，接着再转换为输出</strong>。</p><p>先从h1的计算开始看</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906170509249.webp" alt="image.png"></p><p>图示中记号的含义是：</p><ul><li>a）圆圈或方块表示的是向量。</li><li>b）一个箭头就表示对该向量做一次变换。如上图中h0和x1分别有一个箭头连接，就表示对和各做了一次变换</li></ul><p>h2的计算和h1的计算类似，但是有两点需要注意：</p><ol><li><strong>在计算时，每一步使用的参数U、W、b都是一样的，也就是说每个步骤的参数都是共享的，这是RNN的重要特点</strong>，一定要牢记；</li><li>而下文马上要看到的<strong>LSTM中的权值则不共享</strong>，因为它是在两个不同的向量中。而RNN的权值为何共享呢？很简单，因为RNN的权值是在同一个向量中，只是不同时刻而已</li></ol><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906175035284.webp" alt="image.png"></p><p>同样可以计算剩下来的</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906175101368.webp" alt="image.png"></p><p>而RNN的输出方式就是，直接通过h进行计算</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906175101368.webp" alt="image.png"></p><p>OK！大功告成！这就是最经典的RNN结构，是x1, x2, …..xn，输出为y1, y2, …yn，也就是说，输入和输出序列必须要是等长的</p><h2 id="1-2-RNN的应用"><a href="#1-2-RNN的应用" class="headerlink" title="1.2 RNN的应用"></a>1.2 RNN的应用</h2><p>类似于人脑的思考，我们的思考不会是完全的零基础，一定是建立在一定的基础上，每一次新的学习思考都是更进一步的</p><p>传统的神经网络并不能做到这点，看起来也像是一种巨大的弊端。例如，假设你希望对电影中的每个时间点的时间类型进行分类。传统的神经网络应该很难来处理这个问题：使用电影中先前的事件推断后续的事件。循环神经网络RNN解决了这个问题。</p><p>RNN就是这样包含循环的神经网络</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906175146046.webp" alt="image.png"></p><p>本质上RNN就是同一神经网络的多次复制，每个神经网络模块会把消息传递给下一个</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906175206102.webp" alt="image.png"></p><h2 id="1-3-RNN的局限性：长期依赖问题"><a href="#1-3-RNN的局限性：长期依赖问题" class="headerlink" title="1.3 RNN的局限性：长期依赖问题"></a>1.3 RNN的局限性：长期依赖问题</h2><p>RNN的关键点之一就是他们可以用来连接先前的信息到当前的任务上，但是真的可以么？答案是，还有很多依赖因素。</p><p>在较短的循环进程中，RNN是很有效的</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906175226683.webp" alt="image.png"></p><p>但是相关信息和当前预测位置之间的间隔肯定也有大的时候。</p><p>不幸的是，在这个间隔不断增大时，RNN会丧失学习到连接如此远的信息的能力。</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250906175240959.webp" alt="image.png"></p><p>RNN 会受到短时记忆的影响。如果一条序列足够长，那它们将很难将信息从较早的时间步传送到后面的时间步。</p><p>因此，如果你正在尝试处理一段文本进行预测，RNN 可能从一开始就会遗漏重要信息。在反向传播期间（反向传播是一个很重要的核心议题，本质是通过不断缩小误差去更新权值，从而不断去修正拟合的函数），RNN 会面临梯度消失的问题。</p><p>因为梯度是用于更新神经网络的权重值（新的权值 &#x3D; 旧权值 - 学习率*梯度），梯度会随着时间的推移不断下降减少，而当梯度值变得非常小时，就不会继续学习。</p><p>换言之，在递归神经网络中，获得小梯度更新的层会停止学习—— 那些通常是较早的层。 由于这些层不学习，RNN会忘记它在较长序列中以前看到的内容，因此RNN只具有短时记忆。</p><p>而梯度爆炸则是因为计算的难度越来越复杂导致。</p><p>然而，幸运的是，有个RNN的变体——LSTM，可以在一定程度上解决梯度消失和梯度爆炸这两个问题，这个我们之后再讨论。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;RNN&quot;&gt;&lt;a href=&quot;#RNN&quot; class=&quot;headerlink&quot; title=&quot;RNN&quot;&gt;&lt;/a&gt;RNN&lt;/h1&gt;&lt;h2 id=&quot;1-1-单层网络到RNN结构&quot;&gt;&lt;a href=&quot;#1-1-单层网络到RNN结构&quot; class=&quot;headerlink&quot; </summary>
      
    
    
    
    <category term="机器学习" scheme="https://blog.pridelzh.top/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
    <category term="深度学习" scheme="https://blog.pridelzh.top/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="RNN" scheme="https://blog.pridelzh.top/tags/RNN/"/>
    
  </entry>
  
  <entry>
    <title>Windows10/11下的WSL2安装教程</title>
    <link href="https://blog.pridelzh.top/posts/dsbjda454.html"/>
    <id>https://blog.pridelzh.top/posts/dsbjda454.html</id>
    <published>2025-09-01T13:03:00.000Z</published>
    <updated>2025-09-01T16:19:32.595Z</updated>
    
    <content type="html"><![CDATA[<h2 id="第一部分：用-wsl-–install-安装-WSL-和-Linux"><a href="#第一部分：用-wsl-–install-安装-WSL-和-Linux" class="headerlink" title="第一部分：用 wsl –install 安装 WSL 和 Linux"></a>第一部分：用 wsl –install 安装 WSL 和 Linux</h2><p>系统要求<br>Windows 10 版本 2004 及更高版本 (Build 19041 及以上) 或 Windows 11</p><p>确保 Windows 已更新到最新版本</p><p>安装步骤<br>以管理员身份打开 PowerShell 或 CMD</p><p>按 Win + X，选择 “Windows PowerShell (管理员)” 或 “命令提示符 (管理员)”</p><p>执行一键安装命令</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 这会安装 WSL 2、Linux 内核，并默认安装 Ubuntu</span></span><br><span class="line">wsl <span class="literal">--install</span></span><br><span class="line">如果你想安装特定的发行版（如 Kali Linux）：</span><br></pre></td></tr></table></figure><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 首先查看可用的发行版列表</span></span><br><span class="line">wsl <span class="literal">--list</span> <span class="literal">--online</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 然后安装指定发行版，比如 Kali Linux</span></span><br><span class="line">wsl <span class="literal">--install</span> <span class="literal">-d</span> Kali<span class="literal">-linux</span></span><br></pre></td></tr></table></figure><p>重启计算机</p><p>命令执行完成后，系统会提示你重启计算机</p><p>完成初始设置</p><p>重启后，会自动打开一个窗口</p><p>等待文件解压和安装完成</p><p>设置你的 用户名 和 密码（输入密码时不会显示字符）</p><h2 id="第二部分：安装后的基本配置"><a href="#第二部分：安装后的基本配置" class="headerlink" title="第二部分：安装后的基本配置"></a>第二部分：安装后的基本配置</h2><p>更新系统软件包<br>在 Ubuntu&#x2F;Kali 终端中运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 更新软件包列表</span></span><br><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"></span><br><span class="line"><span class="comment"># 升级已安装的包</span></span><br><span class="line"><span class="built_in">sudo</span> apt upgrade -y</span><br><span class="line"></span><br><span class="line"><span class="comment"># 清理不必要的包</span></span><br><span class="line"><span class="built_in">sudo</span> apt autoremove -y</span><br></pre></td></tr></table></figure><h2 id="第三部分：安装-Neofetch-和-Fastfetch"><a href="#第三部分：安装-Neofetch-和-Fastfetch" class="headerlink" title="第三部分：安装 Neofetch 和 Fastfetch"></a>第三部分：安装 Neofetch 和 Fastfetch</h2><p>在 Ubuntu WSL 中安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 neofetch</span></span><br><span class="line"><span class="built_in">sudo</span> apt insl neofetch -y</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 fastfetch (更快的替代品)</span></span><br><span class="line"><span class="built_in">sudo</span> apt install fastfetch -y</span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试运行</span></span><br><span class="line">neofetch</span><br><span class="line"><span class="comment"># 或</span></span><br><span class="line">fastfetch</span><br></pre></td></tr></table></figure><p>在 Kali Linux WSL 中安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Kali 推荐使用 fastfetch</span></span><br><span class="line"><span class="built_in">sudo</span> apt install fastfetch -y</span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试运行</span></span><br><span class="line">fastfetch</span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果想安装原版 neofetch（需要从源码安装）</span></span><br><span class="line"><span class="built_in">sudo</span> apt install git make -y</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/dylanaraps/neofetch.git</span><br><span class="line"><span class="built_in">cd</span> neofetch</span><br><span class="line"><span class="built_in">sudo</span> make install</span><br><span class="line">neofetch</span><br></pre></td></tr></table></figure><h2 id="结果预览"><a href="#结果预览" class="headerlink" title="结果预览"></a>结果预览</h2><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250901205110861.webp"></p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250901205026078.webp"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;第一部分：用-wsl-–install-安装-WSL-和-Linux&quot;&gt;&lt;a href=&quot;#第一部分：用-wsl-–install-安装-WSL-和-Linux&quot; class=&quot;headerlink&quot; title=&quot;第一部分：用 wsl –install 安装 W</summary>
      
    
    
    
    
    <category term="WSL2" scheme="https://blog.pridelzh.top/tags/WSL2/"/>
    
    <category term="教程" scheme="https://blog.pridelzh.top/tags/%E6%95%99%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Transformer简介</title>
    <link href="https://blog.pridelzh.top/posts/shx49469.html"/>
    <id>https://blog.pridelzh.top/posts/shx49469.html</id>
    <published>2025-08-31T16:19:33.039Z</published>
    <updated>2025-08-31T16:19:32.595Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-Transformer-是什么？"><a href="#1-Transformer-是什么？" class="headerlink" title="1. Transformer 是什么？"></a><strong>1. Transformer 是什么？</strong></h1><p>简单来说，Transformer 是一种深度学习模型架构，最初由 Google 在 2017 年提出，主要用于处理序列数据（如文本）。它彻底改变了自然语言处理(NLP)领域，现在几乎所有先进的 AI 语言模型（如 GPT、BERT 等）都基于 Transformer 架构。</p><h1 id="2-为什么需要-Transformer？"><a href="#2-为什么需要-Transformer？" class="headerlink" title="2. 为什么需要 Transformer？"></a><strong>2. 为什么需要 Transformer？</strong></h1><p>在 Transformer 出现之前，处理序列数据主要使用 RNN（循环神经网络）和 LSTM（长短期记忆网络）。但这些模型存在两个主要问题：</p><ul><li><strong>处理长序列困难</strong>：随着序列变长，早期信息容易被遗忘</li><li><strong>计算效率低</strong>：必须按顺序处理数据，无法并行计算</li><li>Transformer 通过全新的设计解决了这些问题。</li></ul><h1 id="3-Transformer-的核心思想"><a href="#3-Transformer-的核心思想" class="headerlink" title="3. Transformer 的核心思想"></a><strong>3. Transformer 的核心思想</strong></h1><h2 id="3-1-自注意力机制"><a href="#3-1-自注意力机制" class="headerlink" title="3.1 自注意力机制"></a><strong>3.1 自注意力机制</strong></h2><p>这是 Transformer 最核心的创新。想象你在读一篇文章：</p><ul><li>传统方法：从左到右一个字一个字读</li><li>Transformer 方法：一眼看完整句话，自动找出哪些词之间关系更密切</li><li>比如句子”这只猫坐在垫子上，因为它很柔软”：</li><li>“它”更可能与”垫子”相关，而不是”猫”</li><li>Transformer 能自动捕捉这种关系</li></ul><h2 id="3-2-位置编码"><a href="#3-2-位置编码" class="headerlink" title="3.2 位置编码"></a><strong>3.2 位置编码</strong></h2><p>由于 Transformer 不是顺序处理数据，需要额外信息告诉模型词语的位置关系。这就像给每个词加上”座位号”。</p><h1 id="4-Transformer-的基本结构"><a href="#4-Transformer-的基本结构" class="headerlink" title="4. Transformer 的基本结构"></a><strong>4. Transformer 的基本结构</strong></h1><p>一个标准 Transformer 由两部分组成：编码器和解码器</p><h2 id="4-1-编码器"><a href="#4-1-编码器" class="headerlink" title="4.1 编码器"></a><strong>4.1 编码器</strong></h2><ul><li>负责理解输入数据</li><li>由多个相同的层堆叠而成</li><li>每层包含自注意力机制和前馈神经网络</li></ul><p><img src="https://raw.gitcode.com/pridelzh/blogbed/raw/main/20250831223226702.webp"></p><h2 id="4-2-解码器"><a href="#4-2-解码器" class="headerlink" title="4.2 解码器"></a><strong>4.2 解码器</strong></h2><ul><li>负责生成输出</li><li>也有多层结构</li><li>比编码器多一个”编码器-解码器注意力”层</li></ul><p><img src="https://raw.gitcode.com/pridelzh/blogbed/raw/main/20250831223311181.webp"></p><h1 id="5-Transformer-为什么这么强大？"><a href="#5-Transformer-为什么这么强大？" class="headerlink" title="5. Transformer 为什么这么强大？"></a><strong>5. Transformer 为什么这么强大？</strong></h1><ul><li>并行处理：可以同时处理所有输入，训练速度大幅提升</li><li>长距离依赖：能捕捉序列中任意两个元素间的关系</li><li>可扩展性：通过堆叠更多层，模型能力可以不断增强</li></ul><h1 id="6-Transformer-在测试开发中的应用"><a href="#6-Transformer-在测试开发中的应用" class="headerlink" title="6. Transformer 在测试开发中的应用"></a><strong>6. Transformer 在测试开发中的应用</strong></h1><p>作为测试开发人员，了解 Transformer 有助于：</p><ul><li>测试 AI 系统：理解模型工作原理，设计更有效的测试用例</li><li>自动化测试：利用基于 Transformer 的模型生成测试数据或脚本</li><li>异常检测：分析日志或监控数据中的异常模式</li></ul><h1 id="7-通俗理解-Transformer"><a href="#7-通俗理解-Transformer" class="headerlink" title="7. 通俗理解 Transformer"></a><strong>7. 通俗理解 Transformer</strong></h1><p>想象你在组织一场会议：</p><ul><li>传统 RNN：像一个人依次听取每位发言者的话，容易忘记前面内容</li><li>Transformer：像所有人同时发言，但有一种神奇能力能自动聚焦到相关的发言上，综合理解整个讨论</li></ul><h1 id="8-常见-Transformer-模型"><a href="#8-常见-Transformer-模型" class="headerlink" title="8. 常见 Transformer 模型"></a><strong>8. 常见 Transformer 模型</strong></h1><p>BERT：Google 开发的，擅长理解语言</p><ul><li>谷歌搜索（BERT 应用）场景：理解长尾搜索 query 测试要点：长 query 意图识别准确率多义词消歧能力测试搜索延迟性能监控</li></ul><p>GPT 系列：OpenAI 开发的，擅长生成语言</p><ul><li>GitHub Copilot（GPT-3 微调）</li><li>测试相关：代码补全的边界测试安全测试：是否生成含漏洞的代码上下文记忆测试</li></ul><p>T5：Google 开发的，统一了各种 NLP 任务</p><h1 id="conclusion"><a href="#conclusion" class="headerlink" title="conclusion"></a>conclusion</h1><p>对于测试开发人员，建议：</p><ul><li>先理解基本概念和工作原理</li><li>学习如何使用现成的 Transformer 模型 API</li><li>了解模型评估指标和测试方法</li><li>逐步深入模型内部机制</li></ul><p>Transformer 是当今 AI 领域最重要的突破之一，它通过自注意力机制实现了对序列数据的高效处理。作为测试开发人员，理解这一技术将帮助我们更好地测试和利用 AI 系统。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-Transformer-是什么？&quot;&gt;&lt;a href=&quot;#1-Transformer-是什么？&quot; class=&quot;headerlink&quot; title=&quot;1. Transformer 是什么？&quot;&gt;&lt;/a&gt;&lt;strong&gt;1. Transformer 是什么？&lt;/st</summary>
      
    
    
    
    
    <category term="transformer" scheme="https://blog.pridelzh.top/tags/transformer/"/>
    
  </entry>
  
  <entry>
    <title>10-子程序设计</title>
    <link href="https://blog.pridelzh.top/posts/45788787.html"/>
    <id>https://blog.pridelzh.top/posts/45788787.html</id>
    <published>2025-08-31T08:08:00.000Z</published>
    <updated>2025-08-31T08:08:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、子程序设计要点"><a href="#一、子程序设计要点" class="headerlink" title="一、子程序设计要点"></a>一、子程序设计要点</h1><ol><li><p>两种传参方法</p><ol><li>寄存器</li><li>堆栈</li></ol></li><li><p><code>调用约定</code><br> 决定了到底怎么传参，在C语言写函数定义时，写以下关键词来显示指定调用约定，<br> 如<code>void _fastcall cf330(unsigned m, char *buffer)</code>指定了约定方式为_fastcall</p></li><li><p>安排局部变量</p><ol><li>子程序需要一些局部变量，限于子程序部分</li><li>寄存器可以作为局部变量提高效率，但是寄存器的数量过少，一般不把局部变量安排在寄存器中</li><li>使用堆栈来安排局部变量，较为复杂，但是可以安排足够多的局部变量<ol><li>用堆栈要控制esp指针位置</li><li>如果局部变量数量少，可以push一个寄存器进去，如果数量多，可以直接修改esp的值，然后用堆栈操作赋值</li></ol></li></ol></li><li><p>保护<a href="https://so.csdn.net/so/search?q=%E5%AF%84%E5%AD%98%E5%99%A8&spm=1001.2101.3001.7020">寄存器</a>的约定</p><ol><li>子程序可能会破坏某些寄存器内容。为此必须对有关寄存器的内容进行保护与恢复。</li><li>事前压入堆栈，事后从堆栈弹出。在利用堆栈进行寄存器的保护和恢复时，<strong>一定要注意堆栈的先进后出特性，一定要注意堆栈平衡</strong></li><li>可能会降低效率。</li><li>需要主程序和子程序之间的“默契”和“约定”。子程序<strong>只保护主程序关心的那些寄存器</strong>，通常保护ebx、esi、edi和ebp。</li></ol></li><li><p>描述子程序的说明</p><ol><li>在给出子程序代码时，应该给出子程序的说明信息。</li><li>子程序说明信息一般包括：<ol><li>子程序名（或者入口标号）；</li><li>子程序功能描述；</li><li>子程序的入口参数和出口参数；</li><li>所影响的寄存器等情况；</li><li>使用的算法和重要的性能指标；</li><li>其他调用注意事项和说明信息；</li><li>调用实例。</li></ol></li></ol></li></ol><h1 id="二、子程序举例说明"><a href="#二、子程序举例说明" class="headerlink" title="二、子程序举例说明"></a>二、子程序举例说明</h1><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//子程序名（入口标号）：BTOHS</span></span><br><span class="line"><span class="comment">//功    能： 把32位二进制数转换为8位十六进制数的ASCII码串</span></span><br><span class="line"><span class="comment">//入口参数：（1）存放ASCII码串缓冲区的首地址（先压入堆栈）</span></span><br><span class="line"><span class="comment">//          （2）二进制数据（后压入堆栈）</span></span><br><span class="line"><span class="comment">//出口参数： 无</span></span><br><span class="line"><span class="comment">//其他说明：（1）缓冲区应该足够大（至少9个字节）</span></span><br><span class="line"><span class="comment">//          （2）ASCII串以字节0为结束标记</span></span><br><span class="line"><span class="comment">//          （3）影响寄存器EAX、ECX、EDX的值</span></span><br><span class="line">_asm  </span><br><span class="line">&#123;</span><br><span class="line">BTOHS:  ;子程序入口标号</span><br><span class="line">    PUSH  EBP</span><br><span class="line">    MOV   EBP, ESP</span><br><span class="line">    PUSH  EDI<span class="comment">//保护EDI</span></span><br><span class="line">    MOV   EDI, [EBP+<span class="number">12</span>]</span><br><span class="line">    MOV   EDX, [EBP+<span class="number">8</span>]</span><br><span class="line">    MOV   ECX, <span class="number">8</span></span><br><span class="line">NEXT:</span><br><span class="line">    ROL   EDX, <span class="number">4</span></span><br><span class="line">    MOV   AL, DL</span><br><span class="line">    AND   AL, <span class="number">0F</span>H</span><br><span class="line">    ADD   AL, <span class="string">&#x27;0&#x27;</span></span><br><span class="line">    CMP   AL, <span class="string">&#x27;9&#x27;</span></span><br><span class="line">    JBE   LAB580</span><br><span class="line">    ADD   AL, <span class="number">7</span></span><br><span class="line">LAB580:</span><br><span class="line">    MOV   [EDI], AL</span><br><span class="line">    INC   EDI</span><br><span class="line">    LOOP  NEXT</span><br><span class="line">    MOV   BYTE PTR [EDI], <span class="number">0</span></span><br><span class="line">    POP   EDI</span><br><span class="line">    POP   EBP</span><br><span class="line">    RET</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//子程序名（入口标号）：ISDIGIT</span></span><br><span class="line"><span class="comment">//功    能：判断字符是否为十进制数字符</span></span><br><span class="line"><span class="comment">//入口参数：AL=字符</span></span><br><span class="line"><span class="comment">//出口参数：如果为非数字符，AL=0；否则AL保持不变</span></span><br><span class="line">_asm  </span><br><span class="line">&#123;</span><br><span class="line">    ISDIGIT:</span><br><span class="line">        CMP   AL, <span class="string">&#x27;0&#x27;</span>           ;与字符<span class="string">&#x27;0&#x27;</span>比较</span><br><span class="line">        JL    ISDIG1            ;有效字符是<span class="string">&#x27;0&#x27;</span>-<span class="string">&#x27;9&#x27;</span></span><br><span class="line">        CMP   AL,<span class="string">&#x27;9&#x27;</span></span><br><span class="line">        JA    ISDIG1</span><br><span class="line">        RET</span><br><span class="line">    ISDIG1:                     ;非数字符</span><br><span class="line">        XOR   AL,AL             ; AL= <span class="number">0</span></span><br><span class="line">        RET</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//演示调用上述子程序as334和子程序as335</span></span><br><span class="line"><span class="meta">#inclue  <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">int</span>  main(  )</span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span>  buff1[<span class="number">16</span>] = <span class="string">&quot;328&quot;</span>;</span><br><span class="line">    <span class="type">char</span>  buff2[<span class="number">16</span>] = <span class="string">&quot;1234024&quot;</span>;</span><br><span class="line">    <span class="type">unsigned</span>  x1, x2;</span><br><span class="line">    <span class="type">unsigned</span>  sum;</span><br><span class="line"></span><br><span class="line">    _asm  </span><br><span class="line">    &#123;</span><br><span class="line">        LEA   ESI, buff1        ;转换一个字符串</span><br><span class="line">        CALL  DSTOB</span><br><span class="line">        MOV   x1, EAX</span><br><span class="line">        LEA   ESI, buff2        ;转换另一个字符串</span><br><span class="line">        CALL  DSTOB</span><br><span class="line">        MOV   x2, EAX</span><br><span class="line">        ;</span><br><span class="line">        MOV   EDX, x1           ;求和</span><br><span class="line">        ADD   EDX, x2</span><br><span class="line">        MOV   sum, EDX</span><br><span class="line">        ;           ;如这些代码位于前面，</span><br><span class="line">        JMP   OK    ;需要通过该指令来跳过随后的子程序部分！</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">//在这里安排子程序DSTOB和ISDIGIT的代码</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">OK:</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, sum);</span><br><span class="line">    <span class="keyword">return</span>  <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="三、子程序调用方法"><a href="#三、子程序调用方法" class="headerlink" title="三、子程序调用方法"></a>三、子程序调用方法</h1><h2 id="（1）调用指令"><a href="#（1）调用指令" class="headerlink" title="（1）调用指令"></a>（1）调用指令</h2><ol><li><p>分类</p><blockquote><p>段内直接调用<br>段内间接调用<br>段间直接调用<br>段间间接调用</p></blockquote></li><li><p>段内直接</p></li></ol><table><thead><tr><th align="left">名称</th><th align="left">call（段内直接调用指令）</th></tr></thead><tbody><tr><td align="left">格式</td><td align="left"><code>CALL LABEL</code></td></tr><tr><td align="left">动作</td><td align="left">把调用指令下一行指令地址压栈，然后转到LABEL处执行</td></tr><tr><td align="left">注意</td><td align="left">除了保存返回地址，其他同无条件转<code>JMP</code></td></tr></tbody></table><ol start="3"><li>段内间接</li></ol><table><thead><tr><th align="left">名称</th><th align="left">call（段内间接调用指令）</th></tr></thead><tbody><tr><td align="left">格式</td><td align="left"><code>CALL OPDR</code></td></tr><tr><td align="left">动作</td><td align="left">把调用指令下一行指令地址压栈，然后OPDR内容送到EIP，转到OPDR给出偏移地址处执行</td></tr><tr><td align="left">合法值</td><td align="left">OPDR：保护方式下，<strong>32位通用寄存器</strong>、<strong>双字存储单元</strong></td></tr><tr><td align="left">注意</td><td align="left">除了保存返回地址，其他同无条件转<code>JMP</code></td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span>  <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">int</span>  subr_addr;                  <span class="comment">//存放子程序入口地址</span></span><br><span class="line"><span class="type">int</span>  valu;                       <span class="comment">//保存结果</span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">main</span><span class="params">( )</span></span><br><span class="line">&#123; </span><br><span class="line">_asm  </span><br><span class="line">&#123;</span><br><span class="line">        LEA    EDX, SUBR2        <span class="comment">//取得子程序二的入口地址</span></span><br><span class="line">        MOV    subr_addr, EDX    <span class="comment">//保存到存储单元</span></span><br><span class="line">        LEA    EDX, SUBR1        <span class="comment">//取得子程序一的入口地址</span></span><br><span class="line">        XOR    EAX, EAX          <span class="comment">//入口参数EAX=0</span></span><br><span class="line">        CALL    EDX              <span class="comment">//调用子程序一（段内间接，32位Reg）</span></span><br><span class="line">        CALL    subr_addr        <span class="comment">//调用子程序二（段内间接,双字存储单元）</span></span><br><span class="line">        MOV   valu, EAX</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;valu=%d\n&quot;</span>,valu);    <span class="comment">//显示为valu=28</span></span><br><span class="line">    <span class="keyword">return</span>  <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="4"><li>函数指针</li></ol><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//源C程序</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span>  <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">max</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span>;            <span class="comment">//声明函数原型</span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">min</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span>;            <span class="comment">//</span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;   </span><br><span class="line"><span class="type">int</span>  (*pf)(<span class="type">int</span>,<span class="type">int</span>);           <span class="comment">//定义指向函数的指针变量</span></span><br><span class="line">    <span class="type">int</span>  val1, val2;               <span class="comment">//存放结果的变量</span></span><br><span class="line"></span><br><span class="line">    pf = max;                      <span class="comment">//使得pf指向函数max</span></span><br><span class="line">    val1 = (*pf)(<span class="number">13</span>,<span class="number">15</span>);           <span class="comment">//调用由pf指向的函数</span></span><br><span class="line"></span><br><span class="line">    pf = min;                      <span class="comment">//使得pf指向函数min</span></span><br><span class="line">    val2 = (*pf)(<span class="number">23</span>,<span class="number">25</span>);           <span class="comment">//调用由pf指向的函数</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%d,%d\n&quot;</span>,val1,val2);   <span class="comment">//显示为15,23</span></span><br><span class="line">    <span class="keyword">return</span>  <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//反编译(不优化)</span></span><br><span class="line"><span class="comment">//标号max_YAHHH、min_YAHHH分别是两个函数入口地址</span></span><br><span class="line">push  ebp</span><br><span class="line">    mov   ebp, esp           ;建立堆栈框架</span><br><span class="line">    sub   esp, <span class="number">12</span>            ;安排<span class="number">3</span>个局部变量pf、val1和val2</span><br><span class="line"></span><br><span class="line">    ; pf = max;</span><br><span class="line">    mov   DWORD PTR [ebp<span class="number">-4</span>], OFFSET  max_YAHHH</span><br><span class="line">                             </span><br><span class="line">    ; val1 = (*pf)(<span class="number">13</span>,<span class="number">15</span>);</span><br><span class="line">    push  <span class="number">15</span></span><br><span class="line">    push  <span class="number">13</span></span><br><span class="line">    call  DWORD PTR [ebp<span class="number">-4</span>]  ;间接调用指针所指的函数max</span><br><span class="line">    add   esp, <span class="number">8</span> ;平衡堆栈</span><br><span class="line">   </span><br><span class="line">    ; val1= 返回结果</span><br><span class="line">    mov   DWORD PTR [ebp<span class="number">-12</span>], eax </span><br><span class="line">  </span><br><span class="line">    ; pf = min;</span><br><span class="line">    mov   DWORD PTR [ebp<span class="number">-4</span>], OFFSET  min_YAHHH</span><br><span class="line">   </span><br><span class="line">    ; val2 = (*pf)(<span class="number">23</span>,<span class="number">25</span>);</span><br><span class="line">    push  <span class="number">25</span></span><br><span class="line">    push  <span class="number">23</span></span><br><span class="line">    call   DWORD PTR  [ebp<span class="number">-4</span>] ;间接调用指针所指的函数min</span><br><span class="line">    add   esp, <span class="number">8</span></span><br><span class="line">    </span><br><span class="line">    ; val2= 返回结果</span><br><span class="line">    mov   DWORD PTR [ebp<span class="number">-8</span>], eax</span><br><span class="line">    mov   eax, DWORD PTR [ebp<span class="number">-8</span>]     ; eax= val2</span><br><span class="line">    push  eax</span><br><span class="line">    mov   ecx, DWORD PTR [ebp<span class="number">-12</span>]    ; ecx= val1</span><br><span class="line">    push  ecx</span><br><span class="line">    push  OFFSET  FORMTS             ;格式字符串</span><br><span class="line">    call  _printf                    ;段内直接调用</span><br><span class="line">    add   esp, <span class="number">12</span>                    ;平衡堆栈</span><br><span class="line">                                     ;</span><br><span class="line">    xor   eax, eax                   ;准备返回值</span><br><span class="line">    mov   esp, ebp                   ;撤销局部变量</span><br><span class="line">    pop   ebp                        ;撤销堆栈框架</span><br><span class="line">    ret</span><br></pre></td></tr></table></figure><p>可以看到</p><ol><li>指针的本质就是地址</li><li>这里把函数入口和函数参数都放在堆栈，用堆栈传参。</li><li>注意传参时<strong>从ESP开始向高地址找参数，push参数的时候按从右到左的顺序</strong></li><li>采用的是段内间接调用的方法</li></ol><h2 id="（2）返回指令"><a href="#（2）返回指令" class="headerlink" title="（2）返回指令"></a>（2）返回指令</h2><ol><li><p>分类</p><ol><li>按段内段间分<ol><li>段内返回指令（对应段内调用）</li><li>段间返回指令（对应段间调用）（不介绍）</li></ol></li><li>按返回时是否平衡堆栈    <ol><li>不带立即数的返回指令</li><li>带立即数的返回指令</li></ol></li></ol></li><li><p>段内返回不带立即数</p></li></ol><table><thead><tr><th align="left">名称</th><th align="left">RET（段内返回不带立即数指令）</th></tr></thead><tbody><tr><td align="left">格式</td><td align="left"><code>RET</code></td></tr><tr><td align="left">动作</td><td align="left">指令从堆栈弹出地址偏移，送到指令指针寄存器EIP，返回到call时压栈的返回地址处执行</td></tr></tbody></table><ol start="3"><li>段内返回带立即数</li></ol><table><thead><tr><th align="left">名称</th><th align="left">RET（段内返回带立即数指令）</th></tr></thead><tbody><tr><td align="left">格式</td><td align="left"><code>RET count</code></td></tr><tr><td align="left">动作</td><td align="left">指令从堆栈弹出地址偏移（当然这也会影响esp），送到指令指针寄存器EIP，还<strong>额外把count 加到ESP</strong></td></tr><tr><td align="left">注意</td><td align="left"><strong>用于平衡堆栈</strong></td></tr></tbody></table><h1 id="四、示例"><a href="#四、示例" class="headerlink" title="四、示例"></a>四、示例</h1><ul><li>以下是一个全汇编程序示例，它将十六进制数字符串转为数值（二进制），再转十进制输出查看，可以看一下函数调用的各种方法。</li><li>此程序是按8086机资源写的，在64位机器上运行此程序，需要:<ol><li>保存以下代码为<code>.asm</code>文件</li><li>用nasm编译成<code>.com</code>文件</li><li>用DOSbox模拟8086环境运行</li></ol></li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br></pre></td><td class="code"><pre><span class="line">;说明：将十六进制数字符串转为数值（二进制），再转十进制输出查看</span><br><span class="line">segment code;不分段，所有段共用一片内存空间</span><br><span class="line">org <span class="number">100</span>H;从<span class="number">100</span>H开始</span><br><span class="line"></span><br><span class="line">MOV    AX, CS       ;使得数据段与代码段相同</span><br><span class="line">   MOV    DS, AX       ;DS = CS</span><br><span class="line"></span><br><span class="line">MOV AX, <span class="built_in">string</span>;取到要转换的字符串首地址 </span><br><span class="line">PUSH AX </span><br><span class="line">CALL Hex2Bin;转换</span><br><span class="line">CALL PutWordDec;显示转换结果</span><br><span class="line"></span><br><span class="line">MOV   AH, <span class="number">4</span>CH</span><br><span class="line">    INT   <span class="number">21</span>H   </span><br><span class="line"></span><br><span class="line">;子程序名：Hex2Bin</span><br><span class="line">;功    能：把十六进制字符串转数值</span><br><span class="line">;入口参数：堆栈存字符串起始</span><br><span class="line">;出口参数：ax</span><br><span class="line">Hex2Bin:</span><br><span class="line">PUSH BP</span><br><span class="line">MOV BP, SP;建立堆栈框架</span><br><span class="line">MOV SI, [BP+<span class="number">4</span>];字长<span class="number">16</span>位</span><br><span class="line"></span><br><span class="line">MOV CX, <span class="number">-1</span>;避免提前结束</span><br><span class="line">XOR AX,AX;存结果</span><br><span class="line"></span><br><span class="line">DEC SI;方便循环</span><br><span class="line">TOBIN:</span><br><span class="line">INC SI</span><br><span class="line">MOV DL, <span class="string">&#x27;$&#x27;</span>;字符串结尾用$标记，DX在MUL的时候会被刷掉，这里要重新赋值</span><br><span class="line">CMP [SI],DL</span><br><span class="line">JE DONE</span><br><span class="line"></span><br><span class="line">MOV BX,<span class="number">16</span>;乘数<span class="number">16</span>，BX在下面Hex2Bin_WORD的时候会被刷掉，要重新赋值</span><br><span class="line">MUL BX;AX是被乘数，积的低<span class="number">16</span>位仍在AX</span><br><span class="line"></span><br><span class="line">PUSH WORD [SI];取一个<span class="number">16</span>进制字符，转值存到BX（这里入栈后面要手动平衡）</span><br><span class="line">CALL Hex2Bin_WORD</span><br><span class="line">ADD SP,<span class="number">2</span>;平衡堆栈</span><br><span class="line"></span><br><span class="line">ADD AX,BX</span><br><span class="line"></span><br><span class="line">;CALL PutWordDec</span><br><span class="line">;CALL PutSpace</span><br><span class="line">LOOP TOBIN</span><br><span class="line">DONE:</span><br><span class="line">POP BP;撤销堆栈框架</span><br><span class="line">RET</span><br><span class="line"></span><br><span class="line">;子程序名：Hex2Bin_WORD</span><br><span class="line">;功    能：把一个十六进制字符转成二进制值</span><br><span class="line">;入口参数：堆栈</span><br><span class="line">;出口参数：BX</span><br><span class="line">Hex2Bin_WORD:</span><br><span class="line">PUSH BP</span><br><span class="line">MOV BP, SP;建立堆栈框架</span><br><span class="line"></span><br><span class="line">MOV BX,[BP+<span class="number">4</span>]                </span><br><span class="line">MOV BH,<span class="number">0</span></span><br><span class="line">CMP BL,<span class="string">&#x27;A&#x27;</span></span><br><span class="line">JB NUM</span><br><span class="line">SUB BL,<span class="string">&#x27;A&#x27;</span><span class="number">-10</span></span><br><span class="line">JMP OK</span><br><span class="line">NUM:</span><br><span class="line">SUB BL,<span class="string">&#x27;0&#x27;</span></span><br><span class="line">OK:</span><br><span class="line">POP BP;撤销堆栈框架</span><br><span class="line">RET</span><br><span class="line"></span><br><span class="line">;子程序名：PutWordDec</span><br><span class="line">;功    能：把一个字的值转十进制输出</span><br><span class="line">;入口参数：AX</span><br><span class="line">;出口参数：无</span><br><span class="line">PutWordDec:</span><br><span class="line">PUSH BP</span><br><span class="line">MOV BP, SP;建立堆栈框架</span><br><span class="line">PUSHA;保护所有reg(关键是AX/BX/CX/DX)</span><br><span class="line"></span><br><span class="line">MOV CX, <span class="number">-1</span></span><br><span class="line">MOV BX,<span class="number">10</span></span><br><span class="line">LoopPWD1:</span><br><span class="line">XOR DX, DX</span><br><span class="line">DIV BX</span><br><span class="line">PUSH DX</span><br><span class="line">CMP AX, <span class="number">0</span></span><br><span class="line">LOOPNE LoopPWD1</span><br><span class="line"></span><br><span class="line">NOT CX</span><br><span class="line">LoopPWD2:</span><br><span class="line">POP DX</span><br><span class="line">ADD DL, <span class="string">&#x27;0&#x27;</span></span><br><span class="line">CALL PutChar</span><br><span class="line">LOOP LoopPWD2</span><br><span class="line"></span><br><span class="line">POPA;恢复所有reg</span><br><span class="line">POP BP;撤销堆栈框架</span><br><span class="line">RET</span><br><span class="line"></span><br><span class="line">;子程序名：PutChar</span><br><span class="line">;功    能：显示输出一个字符</span><br><span class="line">;入口参数：DL = 显示输出字符ASCII码</span><br><span class="line">;出口参数：无</span><br><span class="line">PutChar:</span><br><span class="line">    PUSH AX;简单的函数，可以不建立堆栈框架</span><br><span class="line">    MOV   AH,<span class="number">2</span></span><br><span class="line">    INT   <span class="number">21</span>H       ;调用<span class="number">2</span>号系统功能显示输出</span><br><span class="line">    POP AX</span><br><span class="line">    RET</span><br><span class="line"></span><br><span class="line">;子程序名：PutSpace</span><br><span class="line">;功    能：显示输出一个空格</span><br><span class="line">;入口参数：无</span><br><span class="line">;出口参数：无</span><br><span class="line">PutSpace:</span><br><span class="line">    PUSH AX;简单的函数，可以不建立堆栈框架</span><br><span class="line">    PUSH DX</span><br><span class="line">    MOV   DL,<span class="number">20</span>H</span><br><span class="line">    MOV   AH,<span class="number">2</span></span><br><span class="line">    INT   <span class="number">21</span>H       ;调用<span class="number">2</span>号系统功能显示输出</span><br><span class="line">    POP DX</span><br><span class="line">    POP AX</span><br><span class="line">    RET</span><br><span class="line">;---------------------------------------------</span><br><span class="line"><span class="built_in">string</span> db <span class="string">&quot;1234&quot;</span>, <span class="string">&#x27;$&#x27;</span>;在这里写要转换的十六进制数</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、子程序设计要点&quot;&gt;&lt;a href=&quot;#一、子程序设计要点&quot; class=&quot;headerlink&quot; title=&quot;一、子程序设计要点&quot;&gt;&lt;/a&gt;一、子程序设计要点&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;两种传参方法&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;寄存器&lt;/li&gt;
&lt;li&gt;</summary>
      
    
    
    
    <category term="汇编语言" scheme="https://blog.pridelzh.top/categories/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
    
    <category term="子程序, 汇编语言" scheme="https://blog.pridelzh.top/tags/%E5%AD%90%E7%A8%8B%E5%BA%8F-%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>9-循环程序设计</title>
    <link href="https://blog.pridelzh.top/posts/456456.html"/>
    <id>https://blog.pridelzh.top/posts/456456.html</id>
    <published>2025-08-31T08:07:00.000Z</published>
    <updated>2025-08-31T08:07:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、循环程序设计"><a href="#一、循环程序设计" class="headerlink" title="一、循环程序设计"></a>一、循环程序设计</h1><h2 id="（1）循环程序设计示例"><a href="#（1）循环程序设计示例" class="headerlink" title="（1）循环程序设计示例"></a>（1）循环程序设计示例</h2><ol><li>两种循环结构</li></ol><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250831164457388.webp" alt="1747123181507.webp"></p><ol start="2"><li>简单循环示例</li></ol><ul><li>简单循环程序</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//统计无符号整数n作为十进制数时的位数</span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">cf320</span><span class="params">(<span class="type">unsigned</span>  <span class="type">int</span>  n)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span>  len = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">do</span>  &#123;</span><br><span class="line">        len++;</span><br><span class="line">        n = n/<span class="number">10</span>;</span><br><span class="line">    &#125; <span class="keyword">while</span> (n != <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">return</span>  len ; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>反汇编之后</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//堆栈传参数，eax传返回值</span></span><br><span class="line">push  ebp</span><br><span class="line">    mov   ebp, esp</span><br><span class="line">    push  ecx                    ;在堆栈，安排局部变量len</span><br><span class="line">    mov   DWORD PTR  [ebp<span class="number">-4</span>], <span class="number">0</span>  ; len=<span class="number">0</span>;</span><br><span class="line">LN3cf320:                         ; <span class="keyword">do</span>  &#123;</span><br><span class="line">                                  ;    len++;</span><br><span class="line">    mov   eax, DWORD PTR [ebp<span class="number">-4</span>]</span><br><span class="line">    add   eax, <span class="number">1</span></span><br><span class="line">    mov   DWORD PTR [ebp<span class="number">-4</span>], eax</span><br><span class="line">                                  ; n = n/<span class="number">10</span>;</span><br><span class="line">    mov   eax, DWORD PTR [ebp+<span class="number">8</span>]</span><br><span class="line">    xor   edx, edx               ;因n是无符号数，用XOR指令清<span class="number">0</span></span><br><span class="line">    mov   ecx, <span class="number">10</span></span><br><span class="line">    div   ecx</span><br><span class="line">    mov   DWORD PTR [ebp+<span class="number">8</span>], eax</span><br><span class="line">    cmp   DWORD PTR [ebp+<span class="number">8</span>], <span class="number">0</span></span><br><span class="line">    jne   SHORT LN3cf320</span><br><span class="line">                                  ; <span class="keyword">return</span>  len ;</span><br><span class="line">    mov   eax, DWORD PTR [ebp<span class="number">-4</span>]  ;准备返回值</span><br><span class="line">                                  ;&#125;</span><br><span class="line">    mov   esp, ebp                ;撤销局部变量len</span><br><span class="line">    pop   ebp                     ;撤销堆栈框架</span><br><span class="line">    ret</span><br></pre></td></tr></table></figure><ul><li><p>简单分析</p><ol><li>堆栈示例<br>  <img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747123486750.webp" alt="1747123486750.webp"></li><li>32位数除法，先把被除数扩展到64位，这里是无符号数，所以直接0扩展就行。使用64位无符号数除法<code>div OPDR</code>，被除数放在<code>edx:eax</code>中，除数OPDR这里是ecx，商存在<code>eax</code>，余数在<code>edx</code></li><li>没优化，改一个数的值要三步：从堆栈取到寄存器，改寄存器值，存回堆栈。几乎所有数据计算之后都要先在堆栈更新，要用时再从堆栈取</li></ol></li><li><p>反汇编之后</p></li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">push  ebp</span><br><span class="line">    mov   ebp, esp</span><br><span class="line">                                  ;ECX作为len</span><br><span class="line">    xor   ecx, ecx                ;len=<span class="number">0</span>;</span><br><span class="line">    push  esi                     ;在使用ESI之前，保护之</span><br><span class="line">LL3cf320:                         ;<span class="keyword">do</span>  &#123;</span><br><span class="line">                                  ;     len++;</span><br><span class="line">                                  ;     n = n/<span class="number">10</span>;</span><br><span class="line">    mov   eax, DWORD PTR [ebp+<span class="number">8</span>]</span><br><span class="line">    push  <span class="number">10</span>                      ;准备借助堆栈送到ESI</span><br><span class="line">    xor   edx, edx                ;使得EDX=<span class="number">0</span></span><br><span class="line">    pop   esi                     ;使得ESI=<span class="number">10</span></span><br><span class="line">    div   esi</span><br><span class="line">    inc   ecx</span><br><span class="line">    mov   DWORD PTR [ebp+<span class="number">8</span>], eax</span><br><span class="line">    test  eax, eax                ;测试n是否为<span class="number">0</span>？</span><br><span class="line">    jne   SHORT LL3cf320</span><br><span class="line">                                  ; <span class="keyword">return</span> len ;</span><br><span class="line">    mov   eax, ecx                ;准备返回值</span><br><span class="line">    pop   esi                     ;恢复ESI</span><br><span class="line">                                  ;&#125;</span><br><span class="line">    pop   ebp</span><br><span class="line">    ret</span><br></pre></td></tr></table></figure><ul><li>简单分析<ol><li>堆栈分析<br>  <img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747123804595.webp" alt="1747123804595.webp"></li><li>仍使用64位无符号数除法<code>div OPDR</code>，但除数OPDR用了源变址寄存器<code>esi</code>，可能因为本质是指针寄存器，所以这里利用堆栈给它赋值，esi&#x3D;0x0A</li><li>优化<ol><li>每轮循环一开始就把n取到寄存器，循环结束时才存回，减少堆栈操作。</li><li>使用test指令判断等于0</li></ol></li></ol></li></ul><h2 id="（2）循环指令"><a href="#（2）循环指令" class="headerlink" title="（2）循环指令"></a>（2）循环指令</h2><ol><li><p><strong>循环指令的说明</strong></p><ol><li>类似于条件转移指令，<strong>段内转移，相对转移</strong>方式。</li><li>通过在<strong>指令指针寄存器EIP上加一个地址差的方式实现转移</strong>。</li><li>只<strong>用一个字节（8位）表示地址差，转移范围仅在-128至+127之间</strong>。</li><li>在保护方式（32位代码段）下，以<strong>ECX作为循环计数器</strong>。在实方式下，以<strong>CX作为循环计数器</strong>。</li><li><strong>不影响</strong>各标志。</li></ol></li><li><p><strong>计数循环指令LOOP</strong></p></li></ol><table><thead><tr><th>名称</th><th align="left">LOOP（计数循环指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>LOOP LABEL</code></td></tr><tr><td>动作</td><td align="left">令使寄存器ECX的值减1，如果结果不等于0，则转移到标号LABEL处，否则顺序执行LOOP指令后的指令</td></tr><tr><td>注意</td><td align="left"><strong>用于循环次数已知的</strong>循环，如for循环</td></tr><tr><td></td><td align="left">计数器必须用ecx先设置计数器ECX初值，即循环次数。</td></tr><tr><td></td><td align="left">由于首先进行ECX减1操作，再判结果是否为0，所以最多可循环 2 32 2^{32} 232遍。</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">;统计寄存器EAX中位是<span class="number">1</span>的个数</span><br><span class="line"></span><br><span class="line">  XOR   EDX, EDX        ;清EDX</span><br><span class="line">      MOV   ECX, <span class="number">32</span>         ;设置循环计数</span><br><span class="line">LAB1: SHR   EAX, <span class="number">1</span>          ;右移<span class="number">1</span>位（最低位进入进位标志CF）</span><br><span class="line">      ADC   DL, <span class="number">0</span>           ;统计（实际是加CF）</span><br><span class="line">      LOOP    LAB1         ;循环</span><br></pre></td></tr></table></figure><ol start="3"><li>**等于&#x2F;全零循环指令LOOPE&#x2F;LOOPZ</li></ol><table><thead><tr><th>名称</th><th align="left">LOOPE&#x2F;LOOPZ（等于&#x2F;全零循环指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>LOOPE（LOOPZ） LABEL</code></td></tr><tr><td>动作</td><td align="left">指令使寄存器ECX的值减1，如果<strong>结果不等于0</strong>，并且零标志<strong>ZF等于1（表示相等</strong>），则<strong>转移到标号LABEL处</strong>，否则顺序执行。</td></tr><tr><td>注意</td><td align="left">适用于循环比较直到找到<strong>相等字符</strong>的情况</td></tr><tr><td></td><td align="left">同一条指令，有两个助记符</td></tr><tr><td></td><td align="left">指令本身实施的ECX减1操作不影响标志</td></tr><tr><td></td><td align="left">可以在循环开始前把ecx设为-1，相当于最大循环FFFFFFFFH-1次，退出循环后用<code>not ecx</code>把ecx按位取反，即可得LOOPE执行次数</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在一个字符数组中查找第一个非空格字符，假设字符数组buff的长度为100：</span></span><br><span class="line"></span><br><span class="line">    LEA   EDX, buff           ;指向字符数组首</span><br><span class="line">    MOV   ECX, <span class="number">100</span>            ;</span><br><span class="line">    MOV   AL, <span class="number">20</span>H             ;空格字符</span><br><span class="line">    DEC   EDX                 ;为了简化循环，先减<span class="number">1</span></span><br><span class="line">LAB2:</span><br><span class="line">    INC   EDX                 ;调整到指向当前字符</span><br><span class="line">    CMP   AL, [EDX]           ;比较</span><br><span class="line">    LOOPE    LAB2</span><br></pre></td></tr></table></figure><ol start="4"><li><strong>不等于&#x2F;非零循环指令LOOPNE&#x2F;LOOPNZ</strong></li></ol><table><thead><tr><th>名称</th><th align="left">LOOPNE&#x2F;LOOPNZ（等于&#x2F;全零循环指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>LOOPNE（LOOPNZ） LABEL</code></td></tr><tr><td>动作</td><td align="left">指令使寄存器ECX的值减1，如果结果<strong>不等于</strong>0，并且零标志<strong>ZF等于0（表示不相等）</strong>，则<strong>转移到标号LABEL</strong>处，否则顺序执行。</td></tr><tr><td>注意</td><td align="left">适用于循环比较直到找到<strong>不相等字符</strong>的情况</td></tr><tr><td></td><td align="left">同一条指令，有两个助记符</td></tr><tr><td></td><td align="left">指令本身实施的ECX减1操作不影响标志</td></tr><tr><td></td><td align="left">可以在循环开始前把ecx设为-1，相当于最大循环FFFFFFFFH-1次，退出循环后用<code>not ecx</code>把ecx按位取反，即可得LOOPNE执行次数</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//演示LOOPNE指令的使用：嵌入汇编代码形式，测量由用户输入的字符串之长度</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span>  <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">main</span><span class="params">( )</span></span><br><span class="line">&#123;   <span class="type">char</span>  <span class="built_in">string</span>[<span class="number">100</span>];           <span class="comment">//用于存放字符串</span></span><br><span class="line">    <span class="type">int</span>  len;                    <span class="comment">//用于存放字符串长度</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Input string:&quot;</span>);     <span class="comment">//由用户输入一个字符串</span></span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%s&quot;</span>,<span class="built_in">string</span>);</span><br><span class="line">    </span><br><span class="line">    _asm  </span><br><span class="line">    &#123;</span><br><span class="line">        LEA   EDI, str        <span class="comment">//使得EDI指向字符串</span></span><br><span class="line">        XOR   ECX, ECX        <span class="comment">//假设字符串“无限长”</span></span><br><span class="line">        XOR   AL, AL          <span class="comment">//使AL=0（字符串结束标记）</span></span><br><span class="line">        DEC   EDI             <span class="comment">//为了简化循环，先减1</span></span><br><span class="line"> LAB3:  INC   EDI             <span class="comment">//指向待判断字符</span></span><br><span class="line">        CMP   AL, [EDI]       <span class="comment">//是否为结束标记</span></span><br><span class="line">        LOOPNE   LAB3       <span class="comment">//如果不是结束标记，继续循环</span></span><br><span class="line">        NOT   ECX             <span class="comment">//据ECX，推得字符串长度</span></span><br><span class="line">        MOV   len, ECX</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;len=%d\n&quot;</span>,len);      <span class="comment">//显示为len=12</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="（3）计数器转移指令"><a href="#（3）计数器转移指令" class="headerlink" title="（3）计数器转移指令"></a>（3）计数器转移指令</h2><ul><li>上面的第一条<code>LOOP</code>指令，提供了一种指定循环次数的方法，但它有一个问题：由于是先将ecx减一再判断，当设定循环次数为0时，实际上会循环FFFFFFFFH次。为了解决这个问题，IA32专门提供了一条用<strong>ECX是否为0</strong>作为判断条件的条件转移指令JECXZ&#x2F;JCXZ</li></ul><table><thead><tr><th>名称</th><th align="left">JECXZ&#x2F;JCXZ（计数器转移指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>JECXZ（JCXZ） LABEL</code></td></tr><tr><td>动作</td><td align="left">指令实现当寄存器<strong>ECX（CX）的值等于0时转移到标号LABEL</strong>处，否则顺序执行。</td></tr><tr><td>注意</td><td align="left">通常在上面几条循环指令之前使用，这样当循环次数为0时，就可以跳过循环体</td></tr><tr><td></td><td align="left">JECXZ对应判断ECX值；JCXZ对应判断CX值</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//计算由用户输入的若干成绩的平均值</span></span><br><span class="line"><span class="comment">//演示堆栈传递参数调用子程序和JECXZ指令的使用：</span></span><br><span class="line"><span class="comment">//注意JECXZ和LOOP配合</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span>  <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span>  COUNT  5                  <span class="comment">//假设成绩项数</span></span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span>  score[COUNT];             <span class="comment">//用于存放由用户输入的成绩</span></span><br><span class="line">    <span class="type">int</span>  i, average;</span><br><span class="line">    <span class="keyword">for</span>  (i=<span class="number">0</span>; i &lt; COUNT; i++)</span><br><span class="line">    &#123;                             <span class="comment">//由用户从键盘输入成绩</span></span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;score[%d]=&quot;</span>, i);</span><br><span class="line">        <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>, &amp;score[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//调用子程序计算成绩平均值</span></span><br><span class="line">    _asm  &#123;</span><br><span class="line">        LEA    EAX, score</span><br><span class="line">        PUSH   COUNT         <span class="comment">//把数组长度压入堆栈</span></span><br><span class="line">        PUSH   EAX            <span class="comment">//把数组起始地址压入堆栈</span></span><br><span class="line">        CALL    AVER           <span class="comment">//调用子程序</span></span><br><span class="line">        ADD    ESP, <span class="number">8</span>           <span class="comment">//平衡堆栈</span></span><br><span class="line">        MOV    average, EAX</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;average=%d\n&quot;</span>,average);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> _asm  &#123;</span><br><span class="line">    AVER:     <span class="comment">//子程序入口</span></span><br><span class="line">        PUSH  EBP</span><br><span class="line">        MOV   EBP, ESP</span><br><span class="line">        MOV   ECX, [EBP+<span class="number">12</span>]      <span class="comment">//取得数组长度</span></span><br><span class="line">        MOV   EDX, [EBP+<span class="number">8</span>]       <span class="comment">//取得数组起始地址</span></span><br><span class="line">        XOR   EAX, EAX           <span class="comment">//将EAX作为和sum</span></span><br><span class="line">        XOR   EBX, EBX           <span class="comment">//将EBX作为下标i</span></span><br><span class="line">        JECXZ   OVER            <span class="comment">//如数组长度为0，不循环累加</span></span><br><span class="line">    NEXT:</span><br><span class="line">        ADD   EAX, [EDX+EBX*<span class="number">4</span>]   <span class="comment">//累加</span></span><br><span class="line">        INC   EBX                <span class="comment">//调整下标i</span></span><br><span class="line">        LOOP   NEXT </span><br><span class="line">       </span><br><span class="line">        CDQ                        <span class="comment">//被除数符号扩展到64位，准备做除法</span></span><br><span class="line"></span><br><span class="line">        IDIV  DWORD PTR [EBP+<span class="number">12</span>]</span><br><span class="line">    OVER:</span><br><span class="line">        POP   EBP                  <span class="comment">//撤销堆栈框架</span></span><br><span class="line">        RET                        <span class="comment">//返回</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>说明：</p><ol><li><p>堆栈示意<br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747124190761.webp" alt="1747124190761.webp"></p></li><li><p>32位有符号数除法，先用<code>CDQ</code>把<code>EAX</code>符号扩展到<code>EDX:EAX</code></p></li></ol><h1 id="二、综合示例"><a href="#二、综合示例" class="headerlink" title="二、综合示例"></a>二、综合示例</h1><ol><li><p>把二进制数转换为十进制数的ASCII码串</p><ol><li><p>方法：</p><ol><li>把一个整数除以10，所得的余数就是个位数。</li><li>把所得的商再除以10，所得的余数就是十位数。</li><li>继续把所得的商除以10，所得的余数就是百位数。</li><li>依次类推，就可以得到一个整数的各位十进制数字了。</li></ol></li></ol><p> 32位二<a href="https://so.csdn.net/so/search?q=%E8%BF%9B%E5%88%B6&spm=1001.2101.3001.7020">进制</a>数能表示的最大十进制数只有10位，<strong>循环地除上10次，就可以得到各位十进制数</strong>，注意这样得到的结果<strong>最前面有若干个0</strong></p><ol start="2"><li><p>把一位十进制数转换为对应的ASCII码，只要加上数字符‘0’的ASCII码。</p></li><li><p>存放顺序：<br> 由于先得到个位数，然后得到十位数，再得到百位数，<strong>所以在把所得的各位十进制数的ASCII码存放到字符串中去时，要从字符串的尾部开始。</strong><br> <img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747124475280.webp" alt="1747124475280.webp"></p></li></ol></li></ol><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>  <span class="title function_">main</span><span class="params">( )</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">unsigned</span>  uintx = <span class="number">56789123</span>;  <span class="comment">//无符号整型变量</span></span><br><span class="line">    <span class="type">char</span>  buffer[<span class="number">11</span>];            <span class="comment">//用于存放ASCII码串的缓冲区</span></span><br><span class="line">    _asm  </span><br><span class="line">    &#123;</span><br><span class="line">    LEA   ESI, buffer          ;获存放字符串的缓冲区首地址</span><br><span class="line">    MOV   EAX, uintx           ;取得待转换的数据</span><br><span class="line">    MOV   ECX, <span class="number">10</span>              ;循环次数（十进制数的位数）</span><br><span class="line">    MOV   EBX, <span class="number">10</span>              ;十进制的基数是<span class="number">10</span></span><br><span class="line">NEXT:</span><br><span class="line">    XOR   EDX, EDX             ;形成<span class="number">64</span>位的被除数（无符号数除）</span><br><span class="line">    DIV   EBX                  ;除以<span class="number">10</span>，EAX含商，EDX含余数</span><br><span class="line">    ADD   DL, <span class="string">&#x27;0&#x27;</span>              ;把析出十进制位转成对应的ASCII码</span><br><span class="line">    MOV   [ESI+ECX<span class="number">-1</span>], DL     ;保存到缓冲区</span><br><span class="line">    LOOP   NEXT              ;计数循环</span><br><span class="line">    ;</span><br><span class="line">    MOV   BYTE PTR [ESI+<span class="number">10</span>],<span class="number">0</span>  ;设置字符串结束标志</span><br><span class="line">&#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%s\n&quot;</span>, buffer);      <span class="comment">//输出字符串</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>改进上面的程序<br>（1）设二进制数是有符号的。如果负数，则所得字符串的第一个字符应该是负号。<br>（2）不需要前端可能出现的字符‘0’</li></ol><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>  <span class="title function_">main</span><span class="params">( )</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span>   intx = <span class="number">-57312</span>;</span><br><span class="line"><span class="type">char</span>  buffer[<span class="number">16</span>];               <span class="comment">//足够长</span></span><br><span class="line"><span class="comment">//printf(“%d\n”,intx);</span></span><br><span class="line"></span><br><span class="line">    _asm  </span><br><span class="line">    &#123;</span><br><span class="line">    LEA   ESI, buffer           ;置指针初值</span><br><span class="line">    MOV   EAX, intx             ;取得待转换的数据</span><br><span class="line">    CMP   EAX, <span class="number">0</span>                ;判断待转换数据是否为负数</span><br><span class="line">    JGE   LAB1                  ;非负数，转</span><br><span class="line">    MOV   BYTE  PTR  [ESI], <span class="string">&#x27;-&#x27;</span> ;先保存一个负号</span><br><span class="line">    INC   ESI                   ;调整指针</span><br><span class="line">    NEG   EAX                   ;取相反数，得正数</span><br><span class="line">LAB1:</span><br><span class="line">    MOV   ECX, <span class="number">10</span>               ;最多循环<span class="number">10</span>次</span><br><span class="line">    MOV   EBX, <span class="number">10</span>               ;每次除以<span class="number">10</span></span><br><span class="line">    MOV   EDI, <span class="number">0</span>                ;置有效位数的计数器初值</span><br><span class="line">NEXT1:</span><br><span class="line">    XOR   EDX, EDX</span><br><span class="line">    DIV   EBX                   ;获得<span class="number">1</span>位十进制数</span><br><span class="line">    ;</span><br><span class="line">    PUSH   EDX                ;把所得<span class="number">1</span>位十进制数压入堆栈</span><br><span class="line">    INC   EDI                   ;有效位数增加<span class="number">1</span></span><br><span class="line">    ;</span><br><span class="line">    OR    EAX, EAX              ;测试结果（商）</span><br><span class="line">    LOOPNE    NEXT1          ;如结果不为<span class="number">0</span>，考虑继续循环</span><br><span class="line">    MOV   ECX, EDI              ;置下一个循环的计数</span><br><span class="line">NEXT2:</span><br><span class="line">    POP   EDX                  ;从堆栈弹出余数</span><br><span class="line">    ADD   DL, <span class="string">&#x27;0&#x27;</span>               ;转成对应的ASCII码</span><br><span class="line">    MOV   [ESI], DL             ;依次存放到缓冲区</span><br><span class="line">    INC   ESI</span><br><span class="line">    LOOP    NEXT2             ;循环处理下一位</span><br><span class="line">    ;</span><br><span class="line">    MOV   BYTE  PTR  [ESI], <span class="number">0</span>   ;设置字符串结束标志</span><br><span class="line">&#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%s\n&quot;</span>, buffer);      <span class="comment">//输出字符串</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、循环程序设计&quot;&gt;&lt;a href=&quot;#一、循环程序设计&quot; class=&quot;headerlink&quot; title=&quot;一、循环程序设计&quot;&gt;&lt;/a&gt;一、循环程序设计&lt;/h1&gt;&lt;h2 id=&quot;（1）循环程序设计示例&quot;&gt;&lt;a href=&quot;#（1）循环程序设计示例&quot; class</summary>
      
    
    
    
    <category term="汇编语言" scheme="https://blog.pridelzh.top/categories/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
    
    <category term="循环结构, 汇编语言" scheme="https://blog.pridelzh.top/tags/%E5%BE%AA%E7%8E%AF%E7%BB%93%E6%9E%84-%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>8-分支程序设计</title>
    <link href="https://blog.pridelzh.top/posts/784783.html"/>
    <id>https://blog.pridelzh.top/posts/784783.html</id>
    <published>2025-08-31T08:06:00.000Z</published>
    <updated>2025-08-31T08:06:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、分支程序设计"><a href="#一、分支程序设计" class="headerlink" title="一、分支程序设计"></a>一、分支程序设计</h1><h3 id="（1）分支程序设计示例"><a href="#（1）分支程序设计示例" class="headerlink" title="（1）分支程序设计示例"></a>（1）分支程序设计示例</h3><h4 id="1-两种分支结构"><a href="#1-两种分支结构" class="headerlink" title="1. 两种分支结构"></a>1. 两种分支结构</h4><ul><li>if结构（图a）；if-else结构（图b）</li><li>需要注意一下用汇编写if-else结构的时候，<strong>if分支结束后要用无条件转跳过else分支</strong>，后面详细说明</li><li><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250513123820909.webp"></li></ul><h4 id="2-简单分支示例"><a href="#2-简单分支示例" class="headerlink" title="2. 简单分支示例"></a>2. 简单分支示例</h4><ul><li>有简单分支程序</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//大写字母转小写</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">cf315</span><span class="params">(<span class="type">int</span> ch)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">if</span>(ch&gt;=<span class="string">&#x27;A&#x27;</span> &amp;&amp; ch&lt;=<span class="string">&#x27;Z&#x27;</span>)</span><br><span class="line">ch+=<span class="number">0x20</span>;</span><br><span class="line"><span class="keyword">return</span> ch;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>现在把它反汇编（关闭优化）</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//子过程cf315</span></span><br><span class="line"><span class="comment">//入口参数：堆栈传递ch</span></span><br><span class="line"><span class="comment">//出口参数：eax</span></span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">CF315:</span><br><span class="line">push ebp</span><br><span class="line">mov ebp,esp</span><br><span class="line"></span><br><span class="line">cmp DWORD PTR[ebp+<span class="number">8</span>],<span class="number">65</span></span><br><span class="line">jl SHORT lab1<span class="comment">//&lt;A跳转</span></span><br><span class="line">cmp DWORD PTR[ebp+<span class="number">8</span>],<span class="number">90</span></span><br><span class="line">jg SHORT lab1<span class="comment">//&gt;Z跳转</span></span><br><span class="line"></span><br><span class="line">mov eax,DWORD PTR[ebp+<span class="number">8</span>]<span class="comment">//是大写字母，转小写</span></span><br><span class="line">add eax,<span class="number">32</span></span><br><span class="line">mov DWORD PTR[ebp+<span class="number">8</span>],eax</span><br><span class="line"></span><br><span class="line">lab1:<span class="comment">//用eax返回结果</span></span><br><span class="line">mov eax,DWORD PTR[ebp+<span class="number">8</span>]</span><br><span class="line">pop ebp</span><br><span class="line">ret</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>现在再打开优化反汇编一次</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">CF315:</span><br><span class="line">push ebp</span><br><span class="line">mov ebp,esp</span><br><span class="line"></span><br><span class="line">mov eax,DWORD PTR[ebp+<span class="number">8</span>]<span class="comment">//ch取到eax中</span></span><br><span class="line">lea ecx,DWORD PTR[eax<span class="number">-65</span>]<span class="comment">//ecx=eax-65</span></span><br><span class="line">cmp ecx,<span class="number">25</span></span><br><span class="line">ja lab1<span class="comment">//看作无符号数，如果ecx&gt;25，意味着ch不是大写字母</span></span><br><span class="line">add ecx,<span class="number">32</span></span><br><span class="line"></span><br><span class="line">lab1:<span class="comment">//用eax返回结果</span></span><br><span class="line">pop ebp</span><br><span class="line">ret</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>观察到的优化手段：<ol><li>巧妙地把两个分支减少到一个</li><li>充分利用寄存器，减少从内存取值</li></ol></li></ul><h4 id="3-双分支示例"><a href="#3-双分支示例" class="headerlink" title="3. 双分支示例"></a>3. 双分支示例</h4><ul><li>有双分支程序</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//把十进制数m转十六进制字符的ASCII码</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">cf316</span><span class="params">(<span class="type">int</span> m)</span></span><br><span class="line">&#123;</span><br><span class="line">m = m &amp; <span class="number">0x0f</span>;<span class="comment">//确保m的值在0~15</span></span><br><span class="line"><span class="keyword">if</span>(m&lt;<span class="number">10</span>)</span><br><span class="line">m+=<span class="number">0x30</span>;<span class="comment">//如：1-&gt;&#x27;1&#x27;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">m+=<span class="number">0x37</span>;<span class="comment">//如：10-&gt;&#x27;A&#x27;</span></span><br><span class="line"><span class="keyword">return</span> m;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>现在把它反汇编（关闭优化）</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">CF316:</span><br><span class="line">push ebp</span><br><span class="line">mov ebp,esp</span><br><span class="line"></span><br><span class="line">mov eax,DWORD PTR[ebp+<span class="number">8</span>]<span class="comment">//m = m &amp; 0x15;</span></span><br><span class="line">and eax,<span class="number">15</span></span><br><span class="line">mov DWORD PTR[ebp+<span class="number">8</span>],eax</span><br><span class="line"></span><br><span class="line">cmp DWORD PTR[ebp+<span class="number">8</span>],<span class="number">10</span></span><br><span class="line">jge SHORT lab1<span class="comment">//m&gt;=10转lab1</span></span><br><span class="line"></span><br><span class="line">mov ecx,DWORD PTR[ebp+<span class="number">8</span>]<span class="comment">//m&lt;10,m+=0x30</span></span><br><span class="line">add ecx,<span class="number">48</span></span><br><span class="line">mov DWORD PTR[ebp+<span class="number">8</span>],ecx</span><br><span class="line">jmp lab2<span class="comment">//绕过else分支，注意！！！</span></span><br><span class="line">lab1:</span><br><span class="line">mov ecx,DWORD PTR[ebp+<span class="number">8</span>]<span class="comment">//m&gt;=10,m+=0x37</span></span><br><span class="line">add ecx,<span class="number">55</span></span><br><span class="line">mov DWORD PTR[ebp+<span class="number">8</span>],ecx</span><br><span class="line">lab2:<span class="comment">//统一的返回位置</span></span><br><span class="line">mov eax,DWORD PTR[ebp+<span class="number">8</span>]</span><br><span class="line">pop ebp</span><br><span class="line">ret</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>现在打开优化再反汇编一次</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">push ebp</span><br><span class="line">mov ebp,esp</span><br><span class="line"></span><br><span class="line">mov eax,DWORD PTR[ebp+<span class="number">8</span>]</span><br><span class="line">and eax,<span class="number">15</span></span><br><span class="line"></span><br><span class="line">cmp eax,<span class="number">10</span></span><br><span class="line">jge SHORT lab1</span><br><span class="line"></span><br><span class="line">add eax,<span class="number">48</span></span><br><span class="line">pop ebp</span><br><span class="line">ret</span><br><span class="line"></span><br><span class="line">lab1:</span><br><span class="line">add eax,<span class="number">55</span></span><br><span class="line">pop ebp</span><br><span class="line">ret</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>观察：</p><ol><li>如果不开优化，参数或局部变量都存在堆栈，要修改必须要进行：取到Reg，修改，写回堆栈三步</li><li>汇编程序是自上往下顺序执行的，（不像C语言中if-else可以选择分支执行；while、for可以循环执行一段代码），它只能用jcc指令修改下一条程序代码的位置，就好像不能用if、else、while、for，只能用goto的C程序。因此像if-else结构，必须在if分支最后jmp跳过else，否则会顺序把else也执行一次。</li><li>优化手段：<ol><li>用寄存器减少从内存取值</li><li>避免了jmp指令，两个分支不再合并而是各自返回。减少跳转次数</li></ol></li></ol><ul><li>优化源程序</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">cf317</span><span class="params">(<span class="type">int</span> m)</span></span><br><span class="line">&#123;</span><br><span class="line">m=m&amp;<span class="number">0x0f</span>;</span><br><span class="line">m+=<span class="number">0x30</span>;</span><br><span class="line"><span class="keyword">if</span>(m&gt;<span class="string">&#x27;9&#x27;</span>)</span><br><span class="line">m+=<span class="number">7</span>;</span><br><span class="line"><span class="keyword">return</span> m;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//开优化反汇编</span></span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">push ebp</span><br><span class="line">mov ebp,esp</span><br><span class="line"></span><br><span class="line">mov eax,DWORD PTR[ebp+<span class="number">8</span>]</span><br><span class="line">and eax,<span class="number">15</span></span><br><span class="line"></span><br><span class="line">add eax,<span class="number">48</span></span><br><span class="line">cmp eax,<span class="number">57</span></span><br><span class="line">jle lab1</span><br><span class="line">add eax,<span class="number">7</span></span><br><span class="line">lab1:</span><br><span class="line">pop ebp</span><br><span class="line">ret</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>小结：<ol><li>靠编译器自动优化，再好也是依赖于源C程序的，要想真正提高程序效率，还得从源程序上改进</li><li>优化策略：<ol><li>减少内存的存取数据，多用寄存器</li><li>减少跳转数量</li><li>避免时钟数多的指令（右移代替除法）</li><li>减少循环次数</li><li>用内联函数减少call和ret</li></ol></li></ol></li></ul><h3 id="（2）无条件和条件转移指令"><a href="#（2）无条件和条件转移指令" class="headerlink" title="（2）无条件和条件转移指令"></a>（2）无条件和条件转移指令</h3><h4 id="1-基本概念"><a href="#1-基本概念" class="headerlink" title="1. 基本概念"></a>1. 基本概念</h4><ul><li><code>段内转移（近转移）</code>：转移时只重置<code>指令指针寄存器EIP</code>，不重置<code>代码段寄存器CS</code>，</li><li><code>段间转移（远转移）</code>：转移时重置<code>指令指针寄存器EIP</code>和<code>代码段寄存器CS</code></li><li>转移类型判断</li></ul><table><thead><tr><th>转移</th><th>属于段内还是段间</th></tr></thead><tbody><tr><td>条件转移</td><td>段内</td></tr><tr><td>循环指令</td><td>段内</td></tr><tr><td>无条件转移</td><td>段内或段间</td></tr><tr><td>过程调用和返回</td><td>段内或段间</td></tr><tr><td>软中断指令</td><td>段间</td></tr><tr><td>中断返回指令</td><td>段间</td></tr></tbody></table><ul><li><code>直接转移</code>：转移指令中直接给出转移目的地址</li><li><code>间接转移</code>：转移指令中给出包含转移目的地址的寄存器或存储单元</li></ul><h4 id="2-无条件转移指令"><a href="#2-无条件转移指令" class="headerlink" title="2. 无条件转移指令"></a>2. 无条件转移指令</h4><ol><li><strong>无条件段内直接转移</strong><ol><li>无条件段内直接转移指令的机器码格式<br> <img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250513124631853.webp" alt="image.png"></li><li>操作码OP：转移指令的机器码</li><li>地址差rel：转移目标地址偏移（标号LABEL所指定指令的地址偏移）与紧随JMP指令的下一条指令的地址偏移之间的差值。<ol><li>rel会被汇编器自动计算，并自动选取为8&#x2F;16&#x2F;32位来表示。如果只用了8位，就称为短（short）转移。</li><li>如果程序不能自动计算地址偏差了多少，用32位来表示rel</li><li>如果编程时可以判断地址偏差不超过8位范围，可以用SHORT指令强制汇编器用8位表示rel</li><li>由于rel是有符号数，转移方向可以向前也可以向后</li></ol></li><li>执行无条件段内转移指令时，把指令中的地址差rel加到指令指针寄存器EIP上，使EIP之内容为转移目标地址偏移，从而实现转移。</li></ol></li></ol><table><thead><tr><th>名称</th><th align="left">jmp（无条件段内直接转移指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>jmp label</code></td></tr><tr><td>动作</td><td align="left">下一条指令转移到 label 处执行</td></tr></tbody></table><ol start="2"><li><strong>无条件段内间接转移</strong></li></ol><table><thead><tr><th>名称</th><th align="left">jmp（无条件段内间接转移指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>jmp OPDR</code></td></tr><tr><td>动作</td><td align="left">指令使控制无条件地转移到由操作数OPRD的内容给定的目标地址处</td></tr><tr><td>合法值</td><td align="left">OPDR：<code>32位寄存器、32位存储单元</code></td></tr><tr><td>注意</td><td align="left">OPRD内容直接被装入指令指针寄存器EIP，从而实现转移</td></tr></tbody></table><h4 id="3-条件转移指令"><a href="#3-条件转移指令" class="headerlink" title="3. 条件转移指令"></a>3. 条件转移指令</h4><ol><li>之前的文章已经写过，见<code>jcc</code>相关部分：</li><li>条件转移指令通过判断状态标志确定转移是否发生，但是本身不影响标志状态</li><li>也是通过<code>label</code>标记确定转移位置，在机器码层面的实现和无条件段内直接转移一样</li></ol><h3 id="（3）多分支的实现"><a href="#（3）多分支的实现" class="headerlink" title="（3）多分支的实现"></a>（3）多分支的实现</h3><ul><li>多分枝类似C中的<code>switch-case</code></li><li>源程序</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//示例函数cf319</span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">cf319</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> operation)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">int</span>  y;</span><br><span class="line"><span class="comment">//多路分支</span></span><br><span class="line"><span class="keyword">switch</span> (operation) &#123;</span><br><span class="line"><span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">y = <span class="number">3</span> * x;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line">y = <span class="number">5</span> * x + <span class="number">6</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> <span class="number">4</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="number">5</span>:</span><br><span class="line">y = x * x;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> <span class="number">8</span>:</span><br><span class="line">y = x * x + <span class="number">4</span> * x;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">default</span>:<span class="comment">//0 3 6 7 9 10 ...</span></span><br><span class="line">y = x;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (y &gt; <span class="number">1000</span>)</span><br><span class="line">y = <span class="number">1000</span>;</span><br><span class="line"><span class="keyword">return</span>  y;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>进行反汇编</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//返汇编（速度最大化）</span></span><br><span class="line">push  ebp</span><br><span class="line">    mov   ebp, esp</span><br><span class="line">                                   ; <span class="keyword">switch</span> ( operation ) &#123;</span><br><span class="line">    mov   eax, DWORD PTR [ebp+<span class="number">12</span>]  ;取得参数operation（<span class="keyword">case</span>值）</span><br><span class="line">    dec   eax                      ;从<span class="number">0</span>开始计算，所以先减去<span class="number">1</span></span><br><span class="line">    cmp   eax, <span class="number">7</span>                   ;从<span class="number">0</span>开始计算，最多就是<span class="number">7</span></span><br><span class="line">    ja    SHORT LN2cf319           ;超过，则转<span class="keyword">default</span></span><br><span class="line">    ;</span><br><span class="line">    jmp   DWORD  PTR  LN12cf319[ eax*<span class="number">4</span> ]     ;实施多路分支</span><br><span class="line">    ;</span><br><span class="line">LN6cf319:                            ; <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line">                                     ; y = <span class="number">3</span>*x;</span><br><span class="line">    mov   eax, DWORD PTR [ebp+<span class="number">8</span>]</span><br><span class="line">    lea   eax, DWORD PTR [eax+eax*<span class="number">2</span>]</span><br><span class="line">    jmp   SHORT  LN7cf319            ; <span class="keyword">break</span>;</span><br><span class="line">    ;</span><br><span class="line">LN5cf319:                            ; <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line">                                     ; y = <span class="number">5</span>*x+<span class="number">6</span>;</span><br><span class="line">    mov   eax, DWORD PTR [ebp+<span class="number">8</span>]</span><br><span class="line">    lea   eax, DWORD PTR [eax+eax*<span class="number">4</span>+<span class="number">6</span>]</span><br><span class="line">    jmp   SHORT  LN7cf319            ; <span class="keyword">break</span>;</span><br><span class="line">    </span><br><span class="line">LN4cf319:                            ; <span class="keyword">case</span> <span class="number">4</span>:</span><br><span class="line">                                     ; y = x*x ;</span><br><span class="line">    mov   eax, DWORD PTR [ebp+<span class="number">8</span>]</span><br><span class="line">    imul  eax, eax</span><br><span class="line">    jmp   SHORT LN7cf319             ;  <span class="keyword">break</span>;</span><br><span class="line">    ;</span><br><span class="line">LN3cf319:                            ; <span class="keyword">case</span> <span class="number">8</span>:</span><br><span class="line">                                     ; y = x*x+<span class="number">4</span>*x;</span><br><span class="line">    mov   ecx, DWORD PTR [ebp+<span class="number">8</span>]</span><br><span class="line">    lea   eax, DWORD PTR [ecx+<span class="number">4</span>]</span><br><span class="line">    imul  eax, ecx</span><br><span class="line">    jmp   SHORT LN7cf319             ; <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">LN2cf319:                            ; <span class="keyword">default</span>:</span><br><span class="line">                                     ; y = x ;</span><br><span class="line">    mov   eax, DWORD PTR [ebp+<span class="number">8</span>]</span><br><span class="line">                                     ; &#125;</span><br><span class="line">LN7cf319:                            ; <span class="keyword">if</span> ( y &gt; <span class="number">1000</span> )</span><br><span class="line">    cmp   eax, <span class="number">1000</span></span><br><span class="line">    jle   SHORT  LN1cf319</span><br><span class="line">                                     ; y = <span class="number">1000</span>;</span><br><span class="line">    mov   eax, <span class="number">1000</span></span><br><span class="line">LN1cf319:                            ; <span class="keyword">return</span>  y;</span><br><span class="line">    pop   ebp                        ;撤销堆栈框架</span><br><span class="line">    ret</span><br><span class="line">    ;</span><br><span class="line"></span><br><span class="line">LN12cf319:                           ;多向分支目标地址表</span><br><span class="line">    DD    LN6cf319                   ; <span class="keyword">case</span> <span class="number">1</span>(DD代表双字，每<span class="number">4</span>个字节存放一个入口地址)</span><br><span class="line">    DD    LN5cf319                   ; <span class="keyword">case</span> <span class="number">2</span></span><br><span class="line">    DD    LN2cf319                   ; <span class="keyword">default</span></span><br><span class="line">    DD    LN4cf319                   ; <span class="keyword">case</span> <span class="number">4</span></span><br><span class="line">    DD    LN4cf319                   ; <span class="keyword">case</span> <span class="number">5</span></span><br><span class="line">    DD    LN2cf319                   ; <span class="keyword">default</span></span><br><span class="line">    DD    LN2cf319                   ; <span class="keyword">default</span></span><br><span class="line">    DD    LN3cf319                   ; <span class="keyword">case</span> <span class="number">8</span></span><br></pre></td></tr></table></figure><p>分析</p><ol><li>最后<code>LN12cf319</code>部分，开启了多向分支目标地址表，<code>DD</code>代表地址表中每一项占4个字节，因此<code>LN12cf319+4*0</code>就是<code>LN6cf319</code>；<code>LN12cf319+4*1</code>就是<code>LN5cf319</code>，依次类推</li><li><code>jmp DWORD PTR LN12cf319[ eax*4 ]</code>这句，跳转到地址<code>LN12cf319+eax*4</code>执行，这是实现switch-case的关键。注意eax要改到0起始</li><li><strong>空位置<code>default</code>必须留出，否则转换过来的时候会出错</strong></li><li>可以看到地址表是按地址排列的，<strong>适用于多分枝且没有大空洞的情况</strong>。如果各个case间有大间距，会导致地址表中<code>default</code>项过多，最好预处理一下，或者改用if-else逻辑</li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、分支程序设计&quot;&gt;&lt;a href=&quot;#一、分支程序设计&quot; class=&quot;headerlink&quot; title=&quot;一、分支程序设计&quot;&gt;&lt;/a&gt;一、分支程序设计&lt;/h1&gt;&lt;h3 id=&quot;（1）分支程序设计示例&quot;&gt;&lt;a href=&quot;#（1）分支程序设计示例&quot; class</summary>
      
    
    
    
    <category term="汇编语言" scheme="https://blog.pridelzh.top/categories/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
    
    <category term="分支结构, 汇编语言" scheme="https://blog.pridelzh.top/tags/%E5%88%86%E6%94%AF%E7%BB%93%E6%9E%84-%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>7-算术逻辑运算指令</title>
    <link href="https://blog.pridelzh.top/posts/78465153.html"/>
    <id>https://blog.pridelzh.top/posts/78465153.html</id>
    <published>2025-08-31T08:05:00.000Z</published>
    <updated>2025-08-31T08:05:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、乘除运算指令"><a href="#一、乘除运算指令" class="headerlink" title="一、乘除运算指令"></a>一、乘除运算指令</h1><ul><li>乘除运算指令区分有符号数与无符号数</li><li>对状态标志的影响，和加减指令相比不是很自然。</li></ul><h3 id="（1）无符号数乘法指令"><a href="#（1）无符号数乘法指令" class="headerlink" title="（1）无符号数乘法指令"></a>（1）无符号数乘法指令</h3><table><thead><tr><th>名称</th><th align="left">MUL（无符号乘法指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>MUL OPRD</code></td></tr><tr><td>动作</td><td align="left">乘数是OPRD，被乘数位于AL、AX或EAX中（由OPRD的尺寸决定，<strong>乘数和被乘数的尺寸一致</strong>），相乘后乘积尺寸翻倍：16位乘积送到AX；32位乘积送DX:AX；64位乘积送EDX:EAX</td></tr><tr><td>合法值</td><td align="left">OPRD：<strong>通用寄存器、存储单元</strong>；</td></tr><tr><td>注意</td><td align="left">OPRD不能是立即数</td></tr><tr><td></td><td align="left">乘数和被乘数的尺寸一致</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">MUL   BL                 <span class="comment">//8位乘    乘积在AX</span></span><br><span class="line">MUL   ECX                <span class="comment">//32位乘   乘积在DX:AX</span></span><br><span class="line">MUL   DX                 <span class="comment">//16位乘   乘积在EDX:EAX</span></span><br></pre></td></tr></table></figure><h3 id="（1）有符号数乘法指令"><a href="#（1）有符号数乘法指令" class="headerlink" title="（1）有符号数乘法指令"></a>（1）有符号数乘法指令</h3><ol><li>单操作数形式：</li></ol><table><thead><tr><th>名称</th><th align="left">IMUL（单操作数乘法指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>IMUL OPRD</code></td></tr><tr><td>动作</td><td align="left">乘数是OPRD，被乘数位于AL、AX或EAX中（由OPRD的尺寸决定，乘数和被乘数的尺寸一致），<strong>乘法运算时把二者看作有符号数</strong>，相乘后乘积尺寸翻倍：16位乘积送到AX；32位乘积送DX:AX；64位乘积送EDX:EAX</td></tr><tr><td>合法值</td><td align="left">OPRD：<strong>通用寄存器、存储单元</strong>；</td></tr><tr><td>注意</td><td align="left">OPRD不能是立即数</td></tr><tr><td></td><td align="left">乘数和被乘数的尺寸一致</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">IMUL   CL</span><br><span class="line">IMUL   DWORD PTR [EBP+<span class="number">12</span>]     <span class="comment">//双字存储单元</span></span><br></pre></td></tr></table></figure><ol start="2"><li>双操作数形式：</li></ol><table><thead><tr><th>名称</th><th align="left">IMUL（双操作数乘法指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>IMUL DEST，SRC</code></td></tr><tr><td>动作</td><td align="left">DEST和SRC相乘后送到DEST，<strong>乘法运算时把二者看作有符号数</strong></td></tr><tr><td>合法值</td><td align="left">DEST：<strong>16位或者32位通用寄存器</strong></td></tr><tr><td></td><td align="left">SRC：<strong>通用寄存器、存储单元、立即数</strong></td></tr><tr><td>注意</td><td align="left">DEST不能是8位寄存器</td></tr><tr><td></td><td align="left">SRC是通用寄存器、存储单元时，尺寸需要和目的操作数一致</td></tr><tr><td></td><td align="left">SRC是立即数时，尺寸不能超过目的操作数</td></tr></tbody></table><ol start="3"><li>三操作数形式：和<code>MUL</code>相比</li></ol><table><thead><tr><th>名称</th><th align="left">IMUL（三操作数乘法指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>IMUL DEST，SRC1，SRC2</code></td></tr><tr><td>动作</td><td align="left">SRC1 和SRC2相乘后送到DEST，<strong>乘法运算时把二者看作有符号数</strong></td></tr><tr><td>合法值</td><td align="left">SRC1：<strong>寄存器、存储单元</strong></td></tr><tr><td></td><td align="left">SRC2：<strong>立即数</strong></td></tr><tr><td>注意</td><td align="left">SRC1和SRC2都看作有符号数</td></tr><tr><td></td><td align="left">SRC1尺寸需要和目的操作数一致</td></tr><tr><td></td><td align="left">SRC2尺寸不能超过目的操作数</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">IMUL   BX</span><br><span class="line">IMUL   EBX, ECX</span><br><span class="line">IMUL   AX，CX，<span class="number">3</span></span><br><span class="line">IMUL   EDX，DWORD PTR [ESI]，<span class="number">5</span></span><br><span class="line">IMUL   AX，<span class="number">7</span></span><br><span class="line">IMUL   AX，AX，<span class="number">7</span></span><br></pre></td></tr></table></figure><h3 id="（3）无符号数除法指令"><a href="#（3）无符号数除法指令" class="headerlink" title="（3）无符号数除法指令"></a>（3）无符号数除法指令</h3><table><thead><tr><th>名称</th><th align="left">DIV（无符号数除法指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>DIV OPRD</code></td></tr><tr><td>动作</td><td align="left">除数是OPRD</td></tr><tr><td></td><td align="left">被除数位于AX、DX:AX或EDX:EAX中（<strong>被除数的尺寸是OPRD两倍</strong>）</td></tr><tr><td></td><td align="left">商在AL、AX或者EAX中（<strong>尺寸与oprd相同</strong>）</td></tr><tr><td></td><td align="left">余数在AH、DX或者EDX中（<strong>尺寸与oprd相同</strong>）</td></tr><tr><td>合法值</td><td align="left">OPRD：<strong>寄存器、存储单元</strong></td></tr><tr><td>注意</td><td align="left">必须防止除溢出，<strong>除数不能为0</strong>，<strong>商不能太大超出存放位置尺寸</strong></td></tr><tr><td></td><td align="left">OPDR<strong>不能是立即数</strong></td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">DIV   BL                <span class="comment">//除数8位</span></span><br><span class="line">DIV   ESI               <span class="comment">//除数32位</span></span><br><span class="line">DIV   CX                <span class="comment">//除数16位</span></span><br></pre></td></tr></table></figure><h3 id="（4）有符号数除法指令"><a href="#（4）有符号数除法指令" class="headerlink" title="（4）有符号数除法指令"></a>（4）有符号数除法指令</h3><table><thead><tr><th>名称</th><th align="left">IDIV（有符号数除法指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>IDIV OPRD</code></td></tr><tr><td>动作</td><td align="left">除数是OPRD</td></tr><tr><td></td><td align="left">被除数位于AX、DX:AX或EDX:EAX中（<strong>尺寸为OPRD两倍</strong>）</td></tr><tr><td></td><td align="left">商在AL、AX或者EAX中（<strong>尺寸同OPRD</strong>）</td></tr><tr><td></td><td align="left">余数在AH、DX或者EDX中（<strong>尺寸同OPRD</strong>）</td></tr><tr><td>合法值</td><td align="left">OPRD：<strong>寄存器、存储单元</strong></td></tr><tr><td>注意</td><td align="left">必须防止除溢出，<strong>除数不能为0</strong>，<strong>商绝对值不能太大超出存放位置尺寸</strong></td></tr><tr><td></td><td align="left">如果不能整除，余数的符号与被除数一致，而且余数的绝对值小于除数的绝对值。</td></tr><tr><td></td><td align="left">OPDR<strong>不能是立即数</strong></td></tr></tbody></table><h3 id="（5）符号扩展指令"><a href="#（5）符号扩展指令" class="headerlink" title="（5）符号扩展指令"></a>（5）符号扩展指令</h3><ol><li>字节转换为字</li></ol><table><thead><tr><th>名称</th><th align="left">CBW（字节转换为字指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>CBW</code></td></tr><tr><td>动作</td><td align="left">指令把AL中的<strong>符号</strong>扩展到AH</td></tr><tr><td>注意</td><td align="left">若AL的最高有效位为0，则AH&#x3D;0；若AL的最高有效位为1，则AH&#x3D;0FFH，也即AH的8位全都为1</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MOV   AX, <span class="number">3487</span>H        <span class="comment">//AX=3487H</span></span><br><span class="line">CBW                    <span class="comment">//AX=FF87H</span></span><br><span class="line">MOV   AX, <span class="number">8734</span>H</span><br><span class="line">CBW                    <span class="comment">//AX=0034H</span></span><br></pre></td></tr></table></figure><ol start="2"><li>字转换为双字</li></ol><table><thead><tr><th>名称</th><th align="left">CWD（字转换为双字指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>CWD</code></td></tr><tr><td>动作</td><td align="left">指令把AX中的<strong>符号</strong>扩展到DX</td></tr><tr><td>注意</td><td align="left">若AX的最高有效位为0，则DX&#x3D;0；若AX最高有效位为1，则DX&#x3D;0FFFFH，也即DX的16位全都为1</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MOV   AX, <span class="number">3487</span>H        <span class="comment">//AX=3487H</span></span><br><span class="line">CWD                    <span class="comment">//DX=0000H, AX=3487H</span></span><br><span class="line">MOV   AX, <span class="number">8734</span>H</span><br><span class="line">CWD                    <span class="comment">//DX=FFFFH, AX=8734H</span></span><br></pre></td></tr></table></figure><table><thead><tr><th>名称</th><th align="left">CWDE（字转换为双字指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>CWDE</code></td></tr><tr><td>动作</td><td align="left">指令把AX中的<strong>符号</strong>扩展到EAX高16位</td></tr><tr><td>注意</td><td align="left">AX的最高有效位为0，则EAX的高16位都为0；若AX的最高有效位为1，则EAX的高16位都为1。</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MOV   AX, <span class="number">3487</span>H        <span class="comment">//AX=3487H</span></span><br><span class="line">CWDE                   <span class="comment">//EAX=00003487H</span></span><br><span class="line">MOV   AX, <span class="number">8734</span>H</span><br><span class="line">CWDE                   <span class="comment">//EAX=FFFF8734H</span></span><br></pre></td></tr></table></figure><ol start="3"><li>双字转换为四字</li></ol><table><thead><tr><th>名称</th><th align="left">CDQ（双字转换为四字指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>CDQ</code></td></tr><tr><td>动作</td><td align="left">指令把EAX中的<strong>符号</strong>扩展到EDX</td></tr><tr><td>注意</td><td align="left">若EAX的最高有效位为0，则DX&#x3D;0；若AX最高有效位为1，则DX&#x3D;0FFFFFFFFH，也即EDX的16位全都为1</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">MOV   EAX, <span class="number">12563487</span>H   ;EAX=<span class="number">12563487</span>H</span><br><span class="line">CDQ                    ;EDX=<span class="number">00000000</span>H, EAX=<span class="number">12563487</span>H</span><br></pre></td></tr></table></figure><p>综合示例：演示除法指令和符号扩展指令的使用</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span>  <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;   </span><br><span class="line"><span class="type">int</span>  quotient, remainder;    <span class="comment">//为了输出结果，安排两个变量</span></span><br><span class="line">    _asm </span><br><span class="line">    &#123;</span><br><span class="line">        MOV   AX, <span class="number">-601</span></span><br><span class="line">        MOV   BL, <span class="number">10</span></span><br><span class="line">        IDIV   BL                   <span class="comment">//除数是BL，被除数是AX,余数在AH,商在AL</span></span><br><span class="line">        MOV   BL, AH              <span class="comment">//先临时保存余数</span></span><br><span class="line">        ;</span><br><span class="line">        CBW                        <span class="comment">//商在AL，符号扩展到AX</span></span><br><span class="line">        CWDE                       <span class="comment">//AX符号扩展到EAX</span></span><br><span class="line">        MOV   quotient, EAX</span><br><span class="line">        ;</span><br><span class="line">        MOV   AL, BL                <span class="comment">//余数送到AL</span></span><br><span class="line">        CBW                        <span class="comment">//AL符号扩展到AX</span></span><br><span class="line">        CWDE                       <span class="comment">//AX符号扩展到EAX</span></span><br><span class="line">        MOV   remainder, EAX</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;quotient= %d\n&quot;</span>, quotient);     <span class="comment">//显示为-200</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;remainder= %d\n&quot;</span>, remainder);   <span class="comment">//显示为-1</span></span><br><span class="line">    <span class="keyword">return</span>  <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="4"><li>符号扩展传送指令</li></ol><table><thead><tr><th>名称</th><th align="left">MOVSX（符号扩展传送指令指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>MOVSX DEST，SRC</code></td></tr><tr><td>动作</td><td align="left">把源操作数<strong>符号扩展</strong>后送至目的操作数DEST</td></tr><tr><td>合法值</td><td align="left">SRC：<strong>通用寄存器、存储单元</strong></td></tr><tr><td></td><td align="left">DEST：<strong>通用寄存器</strong></td></tr><tr><td>注意</td><td align="left"><strong>目的操作数的尺寸必须大于源操作数的尺寸</strong>。源操作数的尺寸可以是8位或者16位；目的操作数的尺寸可以是16位或者32位</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MOV    AL, <span class="number">85</span>H               <span class="comment">//AL=85H</span></span><br><span class="line">MOVSX  EDX, AL               <span class="comment">//EDX=FFFFFF85H</span></span><br><span class="line">MOVSX  CX, AL                <span class="comment">//CX=FF85H</span></span><br><span class="line">MOV    AL, <span class="number">75</span>H               <span class="comment">//AL=75H</span></span><br><span class="line">MOVSX  EAX, AL               <span class="comment">//EAX=00000075H</span></span><br></pre></td></tr></table></figure><ol start="5"><li>零扩展传送指令</li></ol><table><thead><tr><th>名称</th><th align="left">MOVZX（零扩展传送指令指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>MOVZX DEST，SRC</code></td></tr><tr><td>动作</td><td align="left">指令把源操作数SRC<strong>零扩展</strong>后送至目的操作数DEST</td></tr><tr><td>合法值</td><td align="left">SRC：<strong>通用寄存器、存储单元</strong></td></tr><tr><td></td><td align="left">DEST：<strong>通用寄存器</strong></td></tr><tr><td>注意</td><td align="left"><strong>目的操作数的尺寸必须大于源操作数的尺寸</strong>。源操作数的尺寸可以是8位或者16位；目的操作数的尺寸可以是16位或者32位</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">MOV    DX, <span class="number">8885</span>H             ;DX=<span class="number">8885</span>H</span><br><span class="line">MOVZX  ECX, DL               ;ECX=<span class="number">00000085</span>H</span><br><span class="line">MOVZX  EAX, DX               ;EAX=<span class="number">00008885</span>H</span><br></pre></td></tr></table></figure><p>综合示例</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//参数是有符号字符型，返回值是int</span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">cf310</span><span class="params">(<span class="type">char</span> x, <span class="type">char</span> y)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span>  ( x + <span class="number">22</span> ) / y ;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//转汇编如下</span></span><br><span class="line">push   ebp</span><br><span class="line">mov    ebp, esp</span><br><span class="line">movsx  eax, BYTE PTR [ebp+<span class="number">8</span>]   <span class="comment">//把参数x符号扩展后送到eax</span></span><br><span class="line">add    eax, <span class="number">22</span></span><br><span class="line">movsx  ecx, BYTE PTR [ebp+<span class="number">12</span>]  <span class="comment">//把参数y符号扩展后送到ecx</span></span><br><span class="line">cdq                            <span class="comment">//符号扩展，形成64位的被除数</span></span><br><span class="line">idiv   ecx</span><br><span class="line">pop    ebp</span><br><span class="line">ret</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//参数是无符号字符型，返回值是unsigned int</span></span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span>  <span class="title function_">cf311</span><span class="params">(<span class="type">unsigned</span> <span class="type">char</span> x, <span class="type">unsigned</span> <span class="type">char</span> y)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span>  (<span class="type">unsigned</span>)( x + <span class="number">22</span> ) / y ;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//转汇编如下</span></span><br><span class="line">push   ebp</span><br><span class="line">mov    ebp, esp</span><br><span class="line">movzx  eax, BYTE PTR [ebp+<span class="number">8</span>]   <span class="comment">//把参数x零扩展后送到eax</span></span><br><span class="line">add    eax, <span class="number">22</span></span><br><span class="line">movzx  ecx, BYTE PTR [ebp+<span class="number">12</span>]  <span class="comment">//把参数x零扩展后送到ecx</span></span><br><span class="line">xor    edx, edx                <span class="comment">//零扩展，形成64位的被除数</span></span><br><span class="line">div    ecx</span><br><span class="line">pop    ebp</span><br><span class="line">ret</span><br></pre></td></tr></table></figure><h1 id="二、逻辑运算指令"><a href="#二、逻辑运算指令" class="headerlink" title="二、逻辑运算指令"></a>二、逻辑运算指令</h1><ul><li>C语言中有一组按位逻辑运算符<ol><li>按位取反运算符 ~</li><li>按位与运算符 &amp;</li><li>按位或运算符 |</li><li>按位异或运算符 ^</li></ol></li><li>处理器提供一组逻辑运算指令<ol><li>否指令 NOT</li><li>与指令 AND</li><li>或指令 OR</li><li>异或指令 XOR</li></ol></li><li>关于逻辑运算指令的通用说明<ol><li><strong>只有通用寄存器或存储单元可作为目的操作数</strong>，用于存放运算结果。</li><li>如只有一个操作数，则该操作数既是源又是目的。</li><li>如有两个操作数，那么<strong>最多只能有一个是存储单元</strong>，<strong>源操作数可以是立即数</strong>。</li><li>存储单元可采用各种存储器操作数寻址方式。</li><li>操作数可以是字节、字或者双字。<strong>如果有两个操作数，尺寸必须一致</strong>。</li></ol></li></ul><h3 id="（1）否运算指令"><a href="#（1）否运算指令" class="headerlink" title="（1）否运算指令"></a>（1）否运算指令</h3><table><thead><tr><th>名称</th><th align="left">NOT（否运算指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>NOT OPRD</code></td></tr><tr><td>动作</td><td align="left">把操作数OPRD<strong>按位取反</strong>，然后送回OPRD</td></tr><tr><td>合法值</td><td align="left">OPRD：<strong>通用寄存器、存储单元</strong></td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">NOT   CL</span><br><span class="line">NOT   EAX</span><br><span class="line">NOT   BX</span><br></pre></td></tr></table></figure><h3 id="（2）与运算指令"><a href="#（2）与运算指令" class="headerlink" title="（2）与运算指令"></a>（2）与运算指令</h3><table><thead><tr><th>名称</th><th align="left">AND（与运算指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>AND DEST，SRC</code></td></tr><tr><td>动作</td><td align="left">指令对两个操作数进行<strong>按位的逻辑与</strong>运算，结果送到目的操作数DEST</td></tr><tr><td>合法值</td><td align="left">SRC：<strong>通用寄存器、存储单元、立即数</strong></td></tr><tr><td></td><td align="left">OPRD：<strong>通用寄存器、存储单元</strong></td></tr><tr><td>注意</td><td align="left">两个操作数最多只能有一个是存储单元</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">AND   ECX，ESI</span><br><span class="line">MOV   AX，<span class="number">3437</span>H          <span class="comment">//AX=3437H</span></span><br><span class="line">AND   AX，<span class="number">0F</span>0FH          <span class="comment">//AX=0407H</span></span><br></pre></td></tr></table></figure><h3 id="（3）或运算指令"><a href="#（3）或运算指令" class="headerlink" title="（3）或运算指令"></a>（3）或运算指令</h3><table><thead><tr><th>名称</th><th align="left">OR（或运算指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>OR DEST，SRC</code></td></tr><tr><td>动作</td><td align="left">指令对两个操作数进行<strong>按位的逻辑或</strong>运算，结果送到目的操作数DEST</td></tr><tr><td>合法值</td><td align="left">SRC：<strong>通用寄存器、存储单元、立即数</strong></td></tr><tr><td></td><td align="left">OPRD：<strong>通用寄存器、存储单元</strong></td></tr><tr><td>注意</td><td align="left">两个操作数最多只能有一个是存储单元</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">OR    CL，CH</span><br><span class="line">OR    EBX，EAX</span><br><span class="line">MOV   AL，<span class="number">41</span>H         ;AL=<span class="number">01000001B</span>，后缀B表示二进制</span><br><span class="line">OR    AL，<span class="number">20</span>H         ;AL=<span class="number">01100001B</span></span><br></pre></td></tr></table></figure><h3 id="（4）异或运算指令"><a href="#（4）异或运算指令" class="headerlink" title="（4）异或运算指令"></a>（4）异或运算指令</h3><table><thead><tr><th>名称</th><th align="left">XOR（异或运算指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>XOR DEST，SRC</code></td></tr><tr><td>动作</td><td align="left">指令对两个操作数进行<strong>按位异或</strong>的逻辑“异或”运算，结果送到目的操作数DEST</td></tr><tr><td>合法值</td><td align="left">SRC：<strong>通用寄存器、存储单元、立即数</strong></td></tr><tr><td></td><td align="left">OPRD：<strong>通用寄存器、存储单元</strong></td></tr><tr><td>注意</td><td align="left">两个操作数最多只能有一个是存储单元</td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MOV   AL，<span class="number">34</span>H         ;AL=<span class="number">00110100B</span>，符号B表示二进制</span><br><span class="line">MOV   BL，<span class="number">0F</span>H         ;BL=<span class="number">00001111B</span></span><br><span class="line">XOR   AL，BL          ;AL=<span class="number">00111011B</span></span><br><span class="line">XOR   ECX，ECX        ;ECX=<span class="number">0</span>，CF=<span class="number">0</span></span><br></pre></td></tr></table></figure><h3 id="（5）测试指令"><a href="#（5）测试指令" class="headerlink" title="（5）测试指令"></a>（5）测试指令</h3><table><thead><tr><th>名称</th><th align="left">TEST（测试指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>TEST DEST，SRC</code></td></tr><tr><td>动作</td><td align="left"><strong>类似指令AND</strong>，把两个操作数进行按位“与”，<strong>但结果不送到目的操作数DEST，仅仅影响状态标志</strong></td></tr><tr><td>合法值</td><td align="left">SRC：<strong>通用寄存器、存储单元、立即数</strong></td></tr><tr><td></td><td align="left">OPRD：<strong>通用寄存器、存储单元</strong></td></tr><tr><td>注意</td><td align="left">两个操作数最多只能有一个是存储单元</td></tr><tr><td></td><td align="left"><strong>标志ZF、PF和SF反映运算结果，标志CF和OF被清0</strong></td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">TEST  AL, BL</span><br><span class="line">TEST  EDX, ECX</span><br><span class="line"></span><br><span class="line"><span class="comment">//检查AL中的位6和位2是否有一位为1</span></span><br><span class="line">TEST   AL，<span class="number">01000100B</span>       <span class="comment">//符号B表示二进制</span></span><br><span class="line"><span class="comment">//随后，判断标志位ZF，若ZF=0，则这两位上都是0</span></span><br></pre></td></tr></table></figure><h1 id="三、移位指令"><a href="#三、移位指令" class="headerlink" title="三、移位指令"></a>三、移位指令</h1><p>关于移位的几个要素：</p><ol><li>移动方式<ul><li>一般移位指令</li><li>循环移位指令</li><li>双精度移位指令</li></ul></li><li>移动方向<ul><li>左移</li><li>右移</li></ul></li><li>移动位数<ul><li>1位</li><li>m位</li></ul></li></ol><h3 id="（1）一般移位指令"><a href="#（1）一般移位指令" class="headerlink" title="（1）一般移位指令"></a>（1）一般移位指令</h3><table><thead><tr><th>名称</th><th align="left">算术左移SAL <code>SAL OPRD，count</code></th></tr></thead><tbody><tr><td></td><td align="left">逻辑左移SHL <code>SHL OPRD，count</code></td></tr><tr><td></td><td align="left">算术右移SAR <code>SAR OPRD，count</code></td></tr><tr><td></td><td align="left">逻辑右移SHR <code>SHR OPRD，count</code></td></tr><tr><td>动作</td><td align="left">算术左移SAL：动作一样，左移count位。每左移一位，<strong>移出的最高位进入CF，最低位补0</strong></td></tr><tr><td></td><td align="left">逻辑左移SHL：同算术左移SAL</td></tr><tr><td></td><td align="left">算术右移SAR：右移count位。每右移一位，<strong>移出的最低位进入CF，符号（最高）位不变</strong></td></tr><tr><td></td><td align="left">逻辑右移SAR：右移count位。每右移一位，<strong>移出的最低位进入CF，符号（最高）位补0</strong></td></tr><tr><td>合法值</td><td align="left">count：<strong>8位立即数、存储单元、寄存器CL</strong></td></tr><tr><td></td><td align="left">OPRD：<strong>通用寄存器、存储单元</strong>（字节、字、双字）</td></tr><tr><td>注意</td><td align="left"><strong>通过截取count的低5位，实际的移位数被限于0到31之间</strong></td></tr></tbody></table><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250831164042244.webp" alt="1747106285619.webp"></p><p>SAL、SHL示例</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">MOV   EBX, <span class="number">7400</span>EF9CH    <span class="comment">//EBX=7400EF9CH</span></span><br><span class="line">ADD   EBX, <span class="number">0</span>            <span class="comment">//EBX=7400EF9CH，CF=0,SF=0,ZF=0,PF=1</span></span><br><span class="line">SHL   EBX, <span class="number">1</span>            <span class="comment">//EBX=E801DF38H，CF=0,SF=1,ZF=0,PF=0</span></span><br><span class="line">MOV   CL, <span class="number">3</span>             <span class="comment">//CL=3</span></span><br><span class="line">SHL   EBX, CL           <span class="comment">//EBX=400EF9C0H，CF=1,SF=0,ZF=0,PF=1</span></span><br><span class="line">SHL   EBX, <span class="number">16</span>           <span class="comment">//EBX=F9C00000H，CF=0,SF=1,ZF=0,PF=1</span></span><br><span class="line">SHL   EBX, <span class="number">12</span>           <span class="comment">//EBX=00000000H，CF=0,SF=0,ZF=1,PF=1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//实现把寄存器AL中的内容（设为无符号数）乘以10</span></span><br><span class="line"><span class="comment">//算术（逻辑）左移1位，相当于乘以2</span></span><br><span class="line">XOR   AH, AH            <span class="comment">//AH=0</span></span><br><span class="line">SHL   AX, <span class="number">1</span>             <span class="comment">//2*X</span></span><br><span class="line">MOV   BX, AX            <span class="comment">//暂存2*X</span></span><br><span class="line">SHL   AX, <span class="number">2</span>             <span class="comment">//8*X</span></span><br><span class="line">ADD   AX, BX            <span class="comment">//8*X+2*X</span></span><br></pre></td></tr></table></figure><p>SAR示例</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//算术右移1位，相当于有符号数除以2</span></span><br><span class="line">MOV   DX, <span class="number">82</span>C3H         <span class="comment">//DX=82C3H</span></span><br><span class="line">SAR   DX, <span class="number">1</span>             <span class="comment">//DX=C161H,CF=1,SF=1,ZF=0,PF=0</span></span><br><span class="line">MOV   CL, <span class="number">3</span>             <span class="comment">//CL=3</span></span><br><span class="line">SAR   DX, CL            <span class="comment">//DX=F82CH,CF=0,SF=1,ZF=0,PF=0</span></span><br><span class="line">SAR   DX, <span class="number">4</span>             <span class="comment">//DX=FF82H,CF=1,SF=1,ZF=0,PF=1</span></span><br></pre></td></tr></table></figure><p>SHR示例</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MOV   DX, <span class="number">82</span>C3H         <span class="comment">//DX=82C3H</span></span><br><span class="line">SHR   DX, <span class="number">1</span>     <span class="comment">//DX=4161H,CF=1,SF=0,ZF=0,PF=0</span></span><br><span class="line">MOV   CL, <span class="number">3</span>             <span class="comment">//CL=3</span></span><br><span class="line">SHR   DX, CL            <span class="comment">//DX=082CH,CF=0,SF=0,ZF=0,PF=0</span></span><br><span class="line">SHR   DX, <span class="number">12</span>            <span class="comment">//DX=0000H,CF=1,SF=0,ZF=1,PF=1</span></span><br></pre></td></tr></table></figure><h3 id="（2）循环移位指令"><a href="#（2）循环移位指令" class="headerlink" title="（2）循环移位指令"></a>（2）循环移位指令</h3><table><thead><tr><th>名称</th><th align="left">左循环移位指令 ROL <code>ROL OPRD，count</code></th></tr></thead><tbody><tr><td></td><td align="left">右循环移位指令 ROR <code>ROR OPRD，count</code></td></tr><tr><td></td><td align="left">带进位左循环移位指令 RCL <code>RCL OPRD，count</code></td></tr><tr><td></td><td align="left">带进位右循环移位指令 RCR <code>RCR OPRD，count</code></td></tr><tr><td>动作</td><td align="left">见下图</td></tr><tr><td>合法值</td><td align="left">count：<strong>8位立即数、存储单元、寄存器CL</strong></td></tr><tr><td></td><td align="left">OPRD：<strong>通用寄存器、存储单元</strong>（字节、字、双字）</td></tr><tr><td>注意</td><td align="left"><strong>通过截取count的低5位，实际的移位数被限于0到31之间</strong></td></tr></tbody></table><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747106441546.webp" alt="1747106441546.webp"></p><p>循环移位指令的示例</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">MOV   DX, <span class="number">82</span>C3H           ;DX=<span class="number">82</span>C3H</span><br><span class="line">ROL   DX, <span class="number">1</span>               ;DX=<span class="number">0587</span>H, CF=<span class="number">1</span></span><br><span class="line">MOV   CL, <span class="number">3</span>               ;CL=<span class="number">3</span></span><br><span class="line">ROL   DX, CL              ;DX=<span class="number">2</span>C38H, CF=<span class="number">0</span></span><br><span class="line">MOV   EBX, <span class="number">8</span>A2035F7H      ;EBX=<span class="number">8</span>A2035F7H</span><br><span class="line">ROR   EBX, <span class="number">4</span>              ;EBX=<span class="number">78</span>A2035FH, CF=<span class="number">0</span></span><br><span class="line">STC                       ;CF=<span class="number">1</span>（设置进位标志）</span><br><span class="line">RCL   EBX, <span class="number">1</span>              ;EBX=F14406BFH, CF=<span class="number">0</span></span><br><span class="line">RCR   EBX, CL             ;EBX=DE2880D7H, CF=<span class="number">1</span></span><br></pre></td></tr></table></figure><h3 id="（3）双精度移位指令"><a href="#（3）双精度移位指令" class="headerlink" title="（3）双精度移位指令"></a>（3）双精度移位指令</h3><ul><li><strong>把一个操作数的部分内容移位方式复制到另一个操作数</strong></li></ul><table><thead><tr><th>名称</th><th align="left">双精度左移SHLD <code>SHLD OPRD1，OPRD2，count</code></th></tr></thead><tbody><tr><td></td><td align="left">双精度右移SHRD <code>SHRD OPRD1，OPRD2，count</code></td></tr><tr><td>动作</td><td align="left">双精度左移SHLD ：OPDR1左移count位，低端空出的位用OPDR2高端的count位填补，但OPDR2不变。OPDR1中最后移出的放在CF</td></tr><tr><td></td><td align="left">双精度右移SHRD ：OPDR1右移count位，高端空出的位用OPDR2低端的count位填补，但OPDR2不变。OPDR1中最后移出的放在CF</td></tr><tr><td>合法值</td><td align="left">OPDR1：<strong>通用寄存器、存储单元</strong>（字、双字）</td></tr><tr><td></td><td align="left">OPRD：<strong>通用寄存器</strong>（字、双字）</td></tr><tr><td>注意</td><td align="left">两个操作数尺寸必须一致</td></tr><tr><td></td><td align="left">count表示移位的位数，可以是一个8位立即数，也可以是寄存器CL。寄存器CL表示移位数由CL的值决定。通过截取count的低5位，移位数被限于0到31之间</td></tr></tbody></table><p>示例</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">MOV   AX, <span class="number">8321</span>H</span><br><span class="line">MOV   DX, <span class="number">5678</span>H</span><br><span class="line">SHLD  AX, DX, <span class="number">1</span>            <span class="comment">//AX=0642H，DX=5678H，CF=1，OF=1</span></span><br><span class="line">SHLD  AX, DX, <span class="number">2</span>            <span class="comment">//AX=1909H，DX=5678H，CF=0，OF=0</span></span><br><span class="line"></span><br><span class="line">MOV   EAX, <span class="number">01234867</span>H</span><br><span class="line">MOV   EDX, <span class="number">5</span>ABCDEF9H</span><br><span class="line">SHRD  EAX, EDX, <span class="number">4</span>          <span class="comment">//EAX=90123486H，CF=0，OF=1</span></span><br><span class="line">MOV   CL, <span class="number">8</span></span><br><span class="line">SHRD  EAX, EDX, CL         <span class="comment">//EAX=F9901234H，CF=1，OF=0</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、乘除运算指令&quot;&gt;&lt;a href=&quot;#一、乘除运算指令&quot; class=&quot;headerlink&quot; title=&quot;一、乘除运算指令&quot;&gt;&lt;/a&gt;一、乘除运算指令&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;乘除运算指令区分有符号数与无符号数&lt;/li&gt;
&lt;li&gt;对状态标志的影响，和加减指</summary>
      
    
    
    
    <category term="汇编语言" scheme="https://blog.pridelzh.top/categories/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
    
    <category term="算术运算, 逻辑运算" scheme="https://blog.pridelzh.top/tags/%E7%AE%97%E6%9C%AF%E8%BF%90%E7%AE%97-%E9%80%BB%E8%BE%91%E8%BF%90%E7%AE%97/"/>
    
  </entry>
  
  <entry>
    <title>6-堆栈的作用</title>
    <link href="https://blog.pridelzh.top/posts/4564778.html"/>
    <id>https://blog.pridelzh.top/posts/4564778.html</id>
    <published>2025-08-31T08:04:00.000Z</published>
    <updated>2025-08-31T08:04:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、过程调用和返回指令"><a href="#一、过程调用和返回指令" class="headerlink" title="一、过程调用和返回指令"></a>一、过程调用和返回指令</h1><p>（1）过程</p><ol><li><code>过程</code>：<strong>汇编语言中的子程序称为过程（procedure），对应C语言中的函数</strong>。</li><li>调用子程序（过程、函数）在本质上是<strong>控制转移</strong>，它与无条件转移的区别是调用子程序<strong>要考虑返回</strong></li></ol><p>（2）过程调用指令</p><table><thead><tr><th>名称</th><th align="left">CALL（过程调用指令）</th><th></th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>CALL LABEL</code></td><td></td></tr><tr><td>动作</td><td align="left">先把返回地址偏移（EIP内容）压入堆栈，然后给EIP赋值为目标LABEL的地址偏移，实现转移</td><td></td></tr><tr><td>注意</td><td align="left"><code>返回地址</code>：紧随过程调用指令的下一条指令的地址（<strong>有效地址</strong>）</td><td></td></tr><tr><td></td><td align="left"><code>目标地址</code>：子程序开始处的地址（<strong>有效地址</strong>）</td><td></td></tr><tr><td></td><td align="left">与无条件转移指令相比，过程调用指令CALL只是多了第一步（保存返回地址）</td><td></td></tr></tbody></table><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747103763953.webp" alt="1747103763953.webp"></p><p>（3）过程返回指令</p><table><thead><tr><th>名称</th><th align="left">RET（过程返回指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>RET</code></td></tr><tr><td>动作</td><td align="left">从堆栈弹出地址偏移，并送到指令指针寄存器EIP</td></tr><tr><td>注意</td><td align="left">通常，这个返回地址就是在执行对应的<strong>调用指令时所压入堆栈的返回地址</strong></td></tr><tr><td></td><td align="left">过程返回指令的使用<strong>应该与过程调用指令相对应</strong></td></tr><tr><td>示例：</td><td align="left"></td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">dp32</span></span><br><span class="line"><span class="comment">·演示子程序的调用和返回，说明call和RET指令的作用</span></span><br><span class="line"><span class="comment">·调试时关注ESP的值，进出子程序时有压栈和出栈</span></span><br><span class="line"><span class="comment">·这里都是用寄存器传递参数的，影响堆栈的只有进/出子程序时保存/弹出原EIP位置</span></span><br><span class="line"><span class="comment">·特别注意，这里把子程序的汇编安排在return语句之后，这样能避免不经调用直接进入子程序。如果有编译器不支持这样写，需要用goto跳过</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">char</span> <span class="built_in">string</span>[] = <span class="string">&quot;abcde&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">LEA ESI,<span class="built_in">string</span><span class="comment">//指针寄存器ESI指向string首</span></span><br><span class="line">MOV AX,[ESI]<span class="comment">//从ESI取两个字节放入AX (&#x27;a&#x27;&#x27;b&#x27;)</span></span><br><span class="line">CALL TUPPER<span class="comment">//把这两个字节的字符小写转大写</span></span><br><span class="line">MOV [ESI],AX</span><br><span class="line"></span><br><span class="line">MOV AX,[ESI+<span class="number">2</span>]</span><br><span class="line">CALL TUPPER<span class="comment">//把这两个字节的字符小写转大写（这里进栈了返回地址偏移，ret时自动平衡)</span></span><br><span class="line">MOV [ESI+<span class="number">2</span>],AX</span><br><span class="line"></span><br><span class="line">MOV AL, [ESI+<span class="number">4</span>]</span><br><span class="line">CALL UPPER<span class="comment">//把最后一个字节的字符小写转大写</span></span><br><span class="line">MOV [ESI+<span class="number">4</span>],AL</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%s\n&quot;</span>, <span class="built_in">string</span>);</span><br><span class="line">system(<span class="string">&quot;pause&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">//UPPER函数用于把AL中的字符小写转大写</span></span><br><span class="line">UPPER:</span><br><span class="line">CMP AL, <span class="string">&#x27;a&#x27;</span></span><br><span class="line">JB UPPER2</span><br><span class="line">CMP AL, <span class="string">&#x27;z&#x27;</span></span><br><span class="line">JA UPPER2</span><br><span class="line">SUB AL, <span class="number">20</span>H<span class="comment">//确认是小写字符后，转为大写</span></span><br><span class="line">UPPER2 :</span><br><span class="line">RET</span><br><span class="line"></span><br><span class="line"><span class="comment">//TUPPER函数用于把AX中的两个字符小写转大写</span></span><br><span class="line">TUPPER :</span><br><span class="line">CALL UPPER</span><br><span class="line">XCHG AH, AL</span><br><span class="line">CALL UPPER</span><br><span class="line">XCHG AH, AL</span><br><span class="line">RET</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="二、参数传递"><a href="#二、参数传递" class="headerlink" title="二、参数传递"></a>二、参数传递</h1><p>（1）参数传递</p><ol><li><code>参数传递</code>：主程序在调用子程序时，往往要向子程序传递一些参数；同样，子程序运行后也经常要把一些结果返回给主程序。<strong>主程序与子程序之间的这种信息传递被称为参数传递</strong></li><li><code>入口参数</code>：<strong>由主程序传给子程序的参数</strong>称为子程序的入口参数<br> <code>出口参数</code>：<strong>由子程序传给主程序的参数</strong>称为子程序的出口参数</li><li>一般而言，子程序既有入口参数，又有出口参数。但有的子程序只有入口参数，而没有出口参数；少数子程序只有出口参数，而没有入口参数。</li></ol><p>（2）参数传递方式</p><ol><li>有多种传递参数的方法∶寄存器传递法、堆栈传递法、约定内存单元传递法和CALL后续区传递法等。有时可能同时采用多种方法，根据具体情况而事先约定好</li><li>常用的两种方法：</li></ol><table><thead><tr><th>方法</th><th align="left">说明</th><th align="left">特点</th></tr></thead><tbody><tr><td>寄存器传递参数</td><td align="left">把参数放在约定的寄存器中，在主程序中将参数放入寄存器，在子程序中取出</td><td align="left">实现简单和调用方便，但只适用于传递参数较少的情形</td></tr><tr><td>堆栈传递参数</td><td align="left">主程序在调用子程序之前，把需要传递的参数依次压入堆栈，然后子程序从堆栈中取入口参数</td><td align="left">不占用寄存器，也无需额外的存储单元。但较为复杂</td></tr><tr><td>示例：</td><td align="left"></td><td align="left"></td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">dp33</span></span><br><span class="line"><span class="comment">·演示利用堆栈传递子程序参数，子程序参数x,y,返回2x+5y+100</span></span><br><span class="line"><span class="comment">·注意平衡堆栈：esp和ebp的值在子程序调用前后是一致的</span></span><br><span class="line"><span class="comment">·如果反汇编C语言形式的这个函数，不要加_fastcall前缀，否则会用寄存器传参，不会生成这样的代码</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">int</span> sum = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">push <span class="number">456</span><span class="comment">//参数y进栈，esp+=4</span></span><br><span class="line">push <span class="number">23</span><span class="comment">//参数x进栈，esp+=4</span></span><br><span class="line">call cf34<span class="comment">//这里进栈了返回地址偏移（ret自动平衡）</span></span><br><span class="line">add esp,<span class="number">8</span><span class="comment">//手动平衡堆栈（x,y）</span></span><br><span class="line"></span><br><span class="line">mov sum,eax</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;sum=%d\n&quot;</span>, sum);</span><br><span class="line">system(<span class="string">&quot;pause&quot;</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">cf34:</span><br><span class="line">push ebp<span class="comment">//子程序返回时ebp要复原，这里先保存一下，esp+=4</span></span><br><span class="line">mov ebp,esp<span class="comment">//ebp指向栈顶（esp）——建立堆栈框架</span></span><br><span class="line">mov eax,DWORD PTR[ebp+<span class="number">12</span>]<span class="comment">//之前入栈了3个值，这里地址往回找4*3=12，取出第一个参数y</span></span><br><span class="line">mov ecx,DWORD PTR[ebp+<span class="number">8</span>]<span class="comment">//取第二个参数x</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//计算2x+5y+100,存入eax</span></span><br><span class="line">lea eax,DWORD PTR[eax+eax*<span class="number">4</span>+<span class="number">100</span>]</span><br><span class="line">lea eax, DWORD PTR[eax+ecx*<span class="number">2</span>]</span><br><span class="line">pop ebp<span class="comment">//回复ebp的值，平衡堆栈——撤销堆栈框架</span></span><br><span class="line">ret</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>堆栈传递参数的堆栈变化示意</p><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747104101750.webp" alt="1747104101750.webp"></p><p>三、局部变量</p><ol><li>局部变量是高级语言中的概念。所谓局部变量指对其的访问仅限于某个局部范围。在C语言中，局部的范围可能是函数，或者是复合语句。局部变量还有动态和静态之分。</li><li><strong>堆栈</strong>可以用于安排<strong>动态局部变量</strong></li></ol><p>示例：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">dp36</span></span><br><span class="line"><span class="comment">·演示利用堆栈安排动态局部变量</span></span><br><span class="line"><span class="comment">·等价的C程序：</span></span><br><span class="line"><span class="comment">int cf36(int x,int y)//返回xy中较大的</span></span><br><span class="line"><span class="comment">&#123;</span></span><br><span class="line"><span class="comment">int z;</span></span><br><span class="line"><span class="comment">z=x;</span></span><br><span class="line"><span class="comment">if(x&lt;y)</span></span><br><span class="line"><span class="comment">z=y;</span></span><br><span class="line"><span class="comment">return z;</span></span><br><span class="line"><span class="comment">&#125;</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">int</span> max = <span class="number">0</span>;</span><br><span class="line"><span class="type">int</span> x, y;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;输入两个整数：&quot;</span>);</span><br><span class="line"><span class="built_in">scanf</span>(<span class="string">&quot;%d %d&quot;</span>, &amp;x, &amp;y);</span><br><span class="line"></span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">mov eax,y</span><br><span class="line">push eax</span><br><span class="line">mov eax,x</span><br><span class="line">push eax</span><br><span class="line"></span><br><span class="line">call cf36<span class="comment">//这里进栈了返回地址偏移</span></span><br><span class="line">add esp,<span class="number">8</span><span class="comment">//平衡堆栈</span></span><br><span class="line"></span><br><span class="line">mov max,eax</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;max = %d\n&quot;</span>, max);</span><br><span class="line">system(<span class="string">&quot;pause&quot;</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">cf36:</span><br><span class="line">push ebp</span><br><span class="line">mov ebp,esp<span class="comment">//建立堆栈框架</span></span><br><span class="line">push ecx<span class="comment">//在堆栈安排局部变量z</span></span><br><span class="line">mov eax,DWORD PTR[ebp+<span class="number">8</span>]<span class="comment">//eax取得参数x</span></span><br><span class="line">mov DWORD PTR[ebp<span class="number">-4</span>],eax<span class="comment">//z=x</span></span><br><span class="line"></span><br><span class="line">mov ecx, DWORD PTR[ebp+<span class="number">8</span>]<span class="comment">//ecx取得参数x</span></span><br><span class="line">cmp ecx, DWORD PTR[ebp+<span class="number">12</span>]<span class="comment">//比较x和y</span></span><br><span class="line">jge SHORT LN1cf36<span class="comment">//x&gt;y则跳转，SHORT表示转移目的地就在附近</span></span><br><span class="line">mov edx, DWORD PTR[ebp+<span class="number">12</span>]<span class="comment">//x&lt;=y,则edx取得y,赋值给z</span></span><br><span class="line">mov DWORD PTR[ebp<span class="number">-4</span>],edx</span><br><span class="line"></span><br><span class="line">LN1cf36:</span><br><span class="line">mov eax,DWORD PTR[ebp<span class="number">-4</span>]<span class="comment">//eax取得z的值</span></span><br><span class="line">mov esp,ebp<span class="comment">//撤销局部变量z</span></span><br><span class="line">pop ebp<span class="comment">//撤销堆栈框架</span></span><br><span class="line">ret </span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>堆栈示意，安排局部变量并且由堆栈传递参数<br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747104248056.webp" alt="1747104248056.webp"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、过程调用和返回指令&quot;&gt;&lt;a href=&quot;#一、过程调用和返回指令&quot; class=&quot;headerlink&quot; title=&quot;一、过程调用和返回指令&quot;&gt;&lt;/a&gt;一、过程调用和返回指令&lt;/h1&gt;&lt;p&gt;（1）过程&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;过程&lt;/code&gt;</summary>
      
    
    
    
    <category term="汇编语言" scheme="https://blog.pridelzh.top/categories/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
    
    <category term="堆栈" scheme="https://blog.pridelzh.top/tags/%E5%A0%86%E6%A0%88/"/>
    
  </entry>
  
  <entry>
    <title>5-控制转移与堆栈</title>
    <link href="https://blog.pridelzh.top/posts/45646.html"/>
    <id>https://blog.pridelzh.top/posts/45646.html</id>
    <published>2025-08-31T08:03:00.000Z</published>
    <updated>2025-08-31T08:03:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="一、指令指针寄存器和简单控制转移"><a href="#一、指令指针寄存器和简单控制转移" class="headerlink" title="一、指令指针寄存器和简单控制转移"></a>一、指令指针寄存器和简单控制转移</h2><h3 id="（1）指令指针寄存器"><a href="#（1）指令指针寄存器" class="headerlink" title="（1）指令指针寄存器"></a>（1）指令指针寄存器</h3><ol><li><p><strong>指令指针寄存器EIP</strong>:</p><ul><li>IA-32系列CPU有一个32位的指令指针寄存器EIP，它始终指向当前处理的指令。</li><li>它是早先8086CPU指令指针寄存器IP的扩展</li><li>由CS和EIP确定所取指令的存储单元地址。段寄存器CS给出当前段代码段的段号，指令指 针寄存器EIP给出偏移。即<code>CS:EIP</code></li><li>如果代码段起始地址为0，则EIP给出的偏移直接决定所取指令的存储单元地址</li><li><strong>实方式</strong>下，段的最大范围是64k，EIP中高16位必须为0，只有低16位的IP起作用</li></ul></li><li><p><strong>顺序执行指令的过程</strong><br> CPU执行代码（程序）就是一条接一条地执行机器指令。可以把CPU执行指令的过程看做一条处理指令的流水线，通过以下两个步骤实现的指令的<strong>顺序执行</strong>。</p><ol><li>从存储器取指令</li><li>根据指令长度，自动调整指令指针寄存器EIP的值，使其指向下一条指令<br> 这些工作是CPU自动完成的，只需要把我们编写的汇编程序存入代码段，就可以自动顺序执行了。</li></ol></li><li><p>控制转移指令<br> 控制转移指令，它通过直接改变EIP寄存器的内容，实现指令执行过程中的跳转</p><ol><li><code>转移</code>：<strong>非自动顺序</strong>调整EIP内容</li><li><code>控制转移指令</code>：<strong>专门用于改变EIP内容的指令</strong></li><li>各种控制转移指令用于根据不同的情形改变EIP内容，从而实现转移，包括：<ol><li>条件转移指令</li><li>无条件转移指令</li><li>循环指令</li><li>函数调用及返回指令</li></ol></li></ol></li></ol><h3 id="（2）常用条件转移指令"><a href="#（2）常用条件转移指令" class="headerlink" title="（2）常用条件转移指令"></a>（2）常用条件转移指令</h3><ol><li>格式：<code>Jcc LABEL</code></li><li>操作：<code>jcc</code>代表各种条件转移指令的缩写（助记符），<strong>当条件满足时，转到标号<code>LABEL</code>处执行；否则顺序执行</strong></li><li>注意：就好像小于和不大于等于是一码事，<strong>同一条指令也可能有多个助记符</strong>，见下表<br> <img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250521222039086.webp" alt="image.png"></li></ol><h3 id="（3）比较指令和数值大小比较"><a href="#（3）比较指令和数值大小比较" class="headerlink" title="（3）比较指令和数值大小比较"></a>（3）比较指令和数值大小比较</h3><ol><li>比较指令</li></ol><table><thead><tr><th>名称</th><th align="left">CMP（比较指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>CMP DEST,SRC</code></td></tr><tr><td>动作</td><td align="left">根据<code>DEST-SRC的差</code><strong>影响标志寄存器中各状态标志，但不结果作为结果的差值送目的寄存器</strong></td></tr><tr><td>合法值</td><td align="left">SRC：<strong>通用寄存器、存储单元、立即数</strong></td></tr><tr><td></td><td align="left">DEST：<strong>通用寄存器、存储单元</strong></td></tr><tr><td>注意</td><td align="left">DEST 和 SRC 必须尺寸一致</td></tr><tr><td></td><td align="left"><strong>除了不把差值结果送DEST外，其他和SUB指令完全一致</strong></td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">CMP   EDX, <span class="number">-2</span>             ;把EDX与<span class="number">-2</span>比较</span><br><span class="line">CMP   ESI, EBX            ;把ESI与EBX比较</span><br><span class="line">CMP   AL, [ESI]           ;AL与由ESI所指的字节存储单元值作比较</span><br><span class="line">CMP   [EBX+EDI*<span class="number">4</span>+<span class="number">5</span>], DX   ;由EBX+EDI*<span class="number">4</span>+<span class="number">5</span>所指字存储单元值与DX作比较</span><br></pre></td></tr></table></figure><ol start="2"><li>比较数值的大小<ol><li>一般使用比较指令CMP。</li><li>根据零标志ZF判断是否相等（<code>JN/JE</code>）<ul><li>如果都是无符号数，可根据进位CF判断大小（<code>JNB/JAE/JC</code>）</li><li>如果都是有符号数，同时根据符号标志SF和溢出标志OF判断（<code>JL/JNGE</code>）</li></ul></li><li>IA-32同时提供两套以数值大小为条件的条件转移指令，分别使用无符号数之间比较和有符号数之间比较。二者判断标志不同<ul><li>有符号数间称：大于（G），等于（E）,小于（L）</li><li>无符号数间称：高于（A），等于（E）,低于（B）</li></ul></li></ol></li></ol><p>示例：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//假设ECX和EDX存储两个数，现在要把较大的存在ECX中，较小的存在EDX中</span></span><br><span class="line"><span class="comment">//如果这两个数是有符号数</span></span><br><span class="line">cmp ecx,edx</span><br><span class="line">jge OK<span class="comment">//有符号数比较转移：ecx&gt;=edx转到OK</span></span><br><span class="line">xchg ecx,edx<span class="comment">//ecx&lt;edx，交换</span></span><br><span class="line">OK:</span><br><span class="line"></span><br><span class="line"><span class="comment">//如果这两个数是无符号数</span></span><br><span class="line">cmp ecx,edx</span><br><span class="line">jae OK<span class="comment">//无符号数比较转移：ecx&gt;=edx转到OK</span></span><br><span class="line">xchg ecx,edx<span class="comment">//ecx&lt;edx，交换</span></span><br><span class="line">OK:</span><br></pre></td></tr></table></figure><p>示例：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//用汇编实现下面C函数转的功能：</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">myCmpare</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">int</span> z=<span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(x&gt;=<span class="number">13</span> &amp;&amp; y&lt;=<span class="number">28</span>)</span><br><span class="line">z=<span class="number">2</span>;</span><br><span class="line"><span class="keyword">return</span> z;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//转汇编,假设ecx传递x,edx传递y，eax作变量z：</span></span><br><span class="line">mov eax,<span class="number">1</span></span><br><span class="line">cmp ecx,<span class="number">13</span><span class="comment">//x和13比较</span></span><br><span class="line">jl SHORT lab1<span class="comment">//SHORT参数代表转移目的地就在附近</span></span><br><span class="line">cmp edx,<span class="number">28</span><span class="comment">//y和28比较</span></span><br><span class="line">jg SHORT lab1</span><br><span class="line">mov eax,<span class="number">2</span></span><br><span class="line">lab1:</span><br><span class="line">ret<span class="comment">//函数结束，返回到调用者</span></span><br></pre></td></tr></table></figure><h3 id="（4）简单无条件转移指令"><a href="#（4）简单无条件转移指令" class="headerlink" title="（4）简单无条件转移指令"></a>（4）简单无条件转移指令</h3><table><thead><tr><th>名称</th><th align="left">CMP（比较指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>JMP LABEL</code></td></tr><tr><td>动作</td><td align="left">指令控制无条件转移到LABEL处</td></tr><tr><td>注意</td><td align="left">是<strong>段内转移</strong>，没有任何前提<strong>一定发生转移</strong>，类似C中的goto</td></tr><tr><td></td><td align="left">通常用在if-else分支用，if分支结束后跳过else分支</td></tr></tbody></table><p>示例：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//原始C代码（_fastcall表示用寄存器传参数）</span></span><br><span class="line"><span class="type">int</span>  _fastcall  <span class="title function_">cf215</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span>  z;</span><br><span class="line">    <span class="keyword">if</span>  ( x &gt; <span class="number">10</span> )            <span class="comment">//语句A</span></span><br><span class="line">        z = <span class="number">3</span>*x+<span class="number">4</span>*y+<span class="number">7</span>;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        z = <span class="number">2</span>*x+<span class="number">7</span>*y<span class="number">-12</span>;</span><br><span class="line">    <span class="keyword">if</span> ( y &lt;= <span class="number">20</span> )           <span class="comment">//语句B</span></span><br><span class="line">        z = <span class="number">4</span>*z+<span class="number">3</span>;</span><br><span class="line">    <span class="keyword">return</span>  z;               <span class="comment">//语句C</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//反编译</span></span><br><span class="line"> cmp   ecx, <span class="number">10</span>                          <span class="comment">//x与10比较</span></span><br><span class="line">    jle   SHORT  LN3cf215                  <span class="comment">//当小于等于10时转</span></span><br><span class="line">    lea   eax, DWORD PTR [ecx+ecx*<span class="number">2</span>]       <span class="comment">//计算表达式3*x+4*y+7</span></span><br><span class="line">    lea   eax, DWORD PTR [eax+edx*<span class="number">4</span>+<span class="number">7</span>]</span><br><span class="line">    jmp   SHORT  LN2cf215                  <span class="comment">//无条件转（if-else语句结束）</span></span><br><span class="line">LN3cf215:</span><br><span class="line">    lea   eax, DWORD PTR [edx*<span class="number">8</span>]           <span class="comment">//计算表达式7*y+2*x-12</span></span><br><span class="line">    sub   eax, edx</span><br><span class="line">    lea   eax, DWORD PTR [eax+ecx*<span class="number">2</span><span class="number">-12</span>]</span><br><span class="line">LN2cf215:</span><br><span class="line">    cmp   edx, <span class="number">20</span>                          <span class="comment">//y与20比较</span></span><br><span class="line">    jg    SHORT  LN1cf215                  <span class="comment">//当大于20时转</span></span><br><span class="line">    lea   eax, DWORD PTR [eax*<span class="number">4</span>+<span class="number">3</span>]         <span class="comment">//计算4*z+3LN1cf215:</span></span><br><span class="line">    ret           </span><br></pre></td></tr></table></figure><h2 id="二、堆栈和堆栈操作"><a href="#二、堆栈和堆栈操作" class="headerlink" title="二、堆栈和堆栈操作"></a>二、堆栈和堆栈操作</h2><h3 id="（1）堆栈"><a href="#（1）堆栈" class="headerlink" title="（1）堆栈"></a>（1）堆栈</h3><ol><li><p>程序的运行和堆栈有密切关系</p><ul><li>cpu运行期间需要堆栈保存某些关键信息</li><li>程序自身用堆栈保存一些临时数据</li></ul></li><li><p>堆栈</p><ul><li><code>堆栈</code>：<strong>一段内存区域，对他的访问限于一端进行。存储于堆栈段，段寄存器为SS</strong></li><li><code>栈底</code>：堆栈中地址较大的一端</li><li><code>栈顶</code>：堆栈中地址较小的一端</li></ul></li><li><p>堆栈的操作</p><ul><li>后进先出原则，所有存取在栈顶进行（存入数据的地址越来越小，<strong>堆栈生长方向为从高地址到低地址</strong>）</li><li><code>进栈/压栈操作</code>：存入数据</li><li><code>出栈/弹出操作</code>：取出数据</li></ul></li><li><p>堆栈相关寄存器</p></li></ol><table><thead><tr><th>寄存器</th><th align="left">存储内容</th></tr></thead><tbody><tr><td>SS（堆栈段寄存器）</td><td align="left">当前堆栈段号，指示堆栈所在内存区域的位置</td></tr><tr><td>ESP（堆栈指针寄存器）</td><td align="left">栈顶的偏移，SS:ESP永远指向栈顶，<strong>CPU自动控制</strong></td></tr><tr><td>EBP（堆栈数据寄存器）</td><td align="left">栈内数据的偏移，SS:EBP指向栈中一个数据 （习惯指向函数帧栈底），<strong>手动控制</strong></td></tr></tbody></table><ol start="5"><li><p>堆栈平衡</p><ol><li><code>堆栈平衡</code>：<strong>在函数调用前后esp和ebp的值应当相同</strong></li><li>为何要做堆栈平衡：esp和ebp寄存器在子函数调用时是非常重要的（见下方说明），其值在调用过程中会发生改变。一个程序中可能有很多函数，有时还会有嵌套调用的情况，但CPU只有esp和ebp两个寄存器，怎么处理大量的函数呢？intel的策略是同一时刻只处理该时刻执行的函数，也就是说esp和ebp的值在不断刷新。如果一个函数执行后没有恢复esp和ebp指针，就会影响它前后及嵌套的函数，使它们操作堆栈时地址错乱。</li></ol></li><li><p>堆栈的用途</p><ol><li>保护寄存器、保护现场</li><li>保存返回地址</li><li>传递参数</li><li>安排局部变量或临时变量</li><li>反转一组数据</li></ol></li></ol><p>一张很清晰的图片<br><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250521222848546.webp" alt="image.png"></p><h3 id="（2）堆栈操作指令"><a href="#（2）堆栈操作指令" class="headerlink" title="（2）堆栈操作指令"></a>（2）堆栈操作指令</h3><ol><li><strong>进栈指令</strong></li></ol><table><thead><tr><th>名称</th><th align="left">PUSH（进栈指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>PUSH SRC</code></td></tr><tr><td>动作</td><td align="left">把源操作数SRC压入堆栈，并调整esp指向栈顶</td></tr><tr><td>合法值</td><td align="left">SRC：<strong>32&#x2F;16位通用Reg或段Reg；双字&#x2F;字存储单元；立即数</strong></td></tr><tr><td>注意</td><td align="left">双字入栈：ESP-4，然后把双字送到ESP所指的数据单元</td></tr><tr><td></td><td align="left">字入栈：ESP-2，然后把字送到ESP所指的数据单元</td></tr><tr><td></td><td align="left"><strong>至少进栈一个字</strong></td></tr><tr><td>示例：</td><td align="left"></td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">PUSH   EAX                <span class="comment">//把EAX的内容压入堆栈</span></span><br><span class="line">PUSH   DWORD PTR [ECX]    <span class="comment">//把ECX指示的双字存储单元的内容压入堆栈</span></span><br><span class="line">PUSH   BX                 <span class="comment">//把BX的内容压入堆栈</span></span><br><span class="line">PUSH   WORD PTR [EDX]     <span class="comment">//把EDX指示的字存储单元的内容压入堆栈</span></span><br></pre></td></tr></table></figure><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250521223016285.webp" alt="image.png"></p><ol start="2"><li><strong>出栈指令</strong></li></ol><table><thead><tr><th>名称</th><th align="left">POP（出栈指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>POP DEST</code></td></tr><tr><td>动作</td><td align="left">从栈顶弹出一个双字&#x2F;字到DEST，并调整esp指向栈顶</td></tr><tr><td>合法值</td><td align="left">DEST：<strong>32&#x2F;16位通用Reg或段Reg；双字&#x2F;字存储单元。但是不能是立即数或代码段寄存器CS</strong></td></tr><tr><td>注意</td><td align="left">双字出栈：先从ESP所指存储单元弹出一个双字数据送DEST，然后ESP+&#x3D;4</td></tr><tr><td></td><td align="left">字出栈：先从ESP所指存储单元弹出一个字数据送DEST，然后ESP+&#x3D;2</td></tr><tr><td></td><td align="left"><strong>至少出栈一个字</strong></td></tr><tr><td></td><td align="left">DEST不能是立即数或代码段寄存器CS</td></tr><tr><td>示例：</td><td align="left"></td></tr></tbody></table><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">POP   ESI                  <span class="comment">//从堆栈弹出一个双字到ESI</span></span><br><span class="line">POP   DWORD PTR [EBX+<span class="number">4</span>]    <span class="comment">//从堆栈弹出一个双字到EBX+4所指示存储单元</span></span><br><span class="line">POP   DI                   <span class="comment">//从堆栈弹出一个字到DI</span></span><br><span class="line">POP   WORD PTR [EDX+<span class="number">8</span>]     <span class="comment">//从堆栈弹出一个字到EDX+8所指示的存储单元</span></span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span>  <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">main</span><span class="params">( )</span>  </span><br><span class="line">&#123;</span><br><span class="line"><span class="type">int</span>  varsp1, varsp2, varsp3, varsp4, varsp5;  <span class="comment">//用于存放ESP值</span></span><br><span class="line"><span class="type">int</span>  varr1, varr2;                            <span class="comment">//用于存放EBX值</span></span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">    MOV   EAX, <span class="number">12345678</span>H     <span class="comment">//初值</span></span><br><span class="line">    MOV   varsp1, ESP        <span class="comment">//保存演示之初的ESP（假设为0013FA74H）</span></span><br><span class="line">    </span><br><span class="line">    PUSH  EAX                <span class="comment">//把EAX压入堆栈</span></span><br><span class="line">    MOV  varsp2, ESP         <span class="comment">//保存当前ESP（0013FA70H）</span></span><br><span class="line">    </span><br><span class="line">    PUSH  AX                 <span class="comment">//把AX压入堆栈</span></span><br><span class="line">    MOV   varsp3, ESP        <span class="comment">//保存当前ESP（0013FA6EH）</span></span><br><span class="line">    </span><br><span class="line">    POP   EBX                <span class="comment">//从堆栈弹出双字到EBX</span></span><br><span class="line">    MOV   varsp4, ESP        <span class="comment">//保存当前ESP（0013FA72H）</span></span><br><span class="line">    MOV   varr1, EBX</span><br><span class="line">    </span><br><span class="line">    POP   BX                 <span class="comment">//从堆栈弹出字到BX</span></span><br><span class="line">    MOV   varsp5, ESP        <span class="comment">//保存当前ESP（0013FA74H）</span></span><br><span class="line">    MOV   varr2, EBX</span><br><span class="line">&#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;ESP1=%08XH\n&quot;</span>,varsp1);    <span class="comment">//显示为ESP1=0013FA74H</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;ESP2=%08XH\n&quot;</span>,varsp2);    <span class="comment">//显示为ESP2=0013FA70H</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;ESP3=%08XH\n&quot;</span>,varsp3);    <span class="comment">//显示为ESP3=0013FA6EH</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;ESP4=%08XH\n&quot;</span>,varsp4);    <span class="comment">//显示为ESP4=0013FA72H</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;ESP5=%08XH\n&quot;</span>,varsp5);    <span class="comment">//显示为ESP5=0013FA74H</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;EBX1=%08XH\n&quot;</span>,varr1);     <span class="comment">//显示为EBX1=56785678H</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;EBX2=%08XH\n&quot;</span>,varr2);     <span class="comment">//显示为EBX2=56781234H</span></span><br><span class="line">    <span class="keyword">return</span>  <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li><strong>通用寄存器全进出栈指令</strong></li></ol><ul><li>有时需要把多个通用Reg压入栈，以保护值。为了提高效率，从80186开始提供了通用寄存器全进出栈指令</li></ul><p>（1）16位通用Reg</p><table><thead><tr><th>名称</th><th align="left">PUSHA（16位通用寄存器全进栈指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>PUSHA</code></td></tr><tr><td>动作</td><td align="left">将8个16位通用寄存器的内容压入堆栈，压入顺序：AX、CX、DX、BX、SP、BP、SI、DI</td></tr></tbody></table><table><thead><tr><th>名称</th><th align="left">POPA（16位通用寄存器全出栈指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>POPA</code></td></tr><tr><td>动作</td><td align="left">从堆栈弹出内容，以PUSHA相反的顺序送通用寄存器</td></tr></tbody></table><p>（2）32位通用Reg</p><table><thead><tr><th>名称</th><th align="left">PUSHAD（32位通用寄存器全进栈指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>PUSHAD</code></td></tr><tr><td>动作</td><td align="left">将8个16位通用寄存器的内容压入堆栈，压入顺序：EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI</td></tr></tbody></table><table><thead><tr><th>名称</th><th align="left">POPAD（32位通用寄存器全出栈指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>POPAD</code></td></tr><tr><td>动作</td><td align="left">从堆栈弹出内容，以PUSHA相反的顺序送通用寄存器</td></tr></tbody></table><p>示例：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//演示PUSHAD指令的执行效果，还演示另一种访问堆栈区域存储单元的方法</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span>  <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">int</span>   buff[<span class="number">8</span>];       <span class="comment">//全局数组，存放从堆栈中取出的各寄存器之值</span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">main</span><span class="params">( )</span></span><br><span class="line">&#123;</span><br><span class="line">    _asm </span><br><span class="line">    &#123;    </span><br><span class="line">     PUSH  EBP            <span class="comment">//先保存EBP！！</span></span><br><span class="line">    ;</span><br><span class="line">    MOV   EAX, <span class="number">0</span>         <span class="comment">//给各通用寄存器赋一个特定的值</span></span><br><span class="line">    MOV   EBX, <span class="number">1</span></span><br><span class="line">    MOV   ECX, <span class="number">2</span></span><br><span class="line">    MOV   EDX, <span class="number">3</span></span><br><span class="line">    ;                    <span class="comment">//决不能随意改变ESP！！</span></span><br><span class="line">    MOV   EBP, <span class="number">5</span></span><br><span class="line">    MOV   ESI, <span class="number">6</span></span><br><span class="line">    MOV   EDI, <span class="number">7</span></span><br><span class="line">    ;</span><br><span class="line">    PUSHAD               <span class="comment">//把8个通用寄存器之值全部推到堆栈</span></span><br><span class="line">    ;</span><br><span class="line">MOV   EBP, ESP       <span class="comment">//使得EBP也指向堆栈顶</span></span><br><span class="line">    LEA   EBX, buff      <span class="comment">//把数组buff首元素的有效地址送到EBX</span></span><br><span class="line">    MOV   ECX, <span class="number">0</span>         <span class="comment">//设置计数器（下标）初值</span></span><br><span class="line">NEXT:</span><br><span class="line">    MOV   EAX, [EBP+ECX*<span class="number">4</span>]    <span class="comment">//依次从堆栈中取</span></span><br><span class="line">    MOV   [EBX+ECX*<span class="number">4</span>], EAX    <span class="comment">//依次保存到数组buff</span></span><br><span class="line">    INC   ECX                 <span class="comment">//计数器加1</span></span><br><span class="line">    CMP   ECX, <span class="number">8</span>              <span class="comment">//是否满8</span></span><br><span class="line">    JNZ   NEXT                <span class="comment">//没有满8个，继续处理下一个</span></span><br><span class="line">    ;</span><br><span class="line">    POPAD                     <span class="comment">//恢复8个通用寄存器</span></span><br><span class="line">    POP   EBP      </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//依次显示数组buff各元素之值，从中观察PUAHAD指令压栈的效果</span></span><br><span class="line">    <span class="type">int</span>  i;</span><br><span class="line">    <span class="keyword">for</span>  (i=<span class="number">0</span>; i&lt;<span class="number">8</span>; i++)</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;buff[%d]=%u\n&quot;</span>, i, buff[i]);</span><br><span class="line">    <span class="keyword">return</span>  <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250521223310243.webp" alt="image.png"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;一、指令指针寄存器和简单控制转移&quot;&gt;&lt;a href=&quot;#一、指令指针寄存器和简单控制转移&quot; class=&quot;headerlink&quot; title=&quot;一、指令指针寄存器和简单控制转移&quot;&gt;&lt;/a&gt;一、指令指针寄存器和简单控制转移&lt;/h2&gt;&lt;h3 id=&quot;（1）指令指针寄存</summary>
      
    
    
    
    <category term="汇编语言" scheme="https://blog.pridelzh.top/categories/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
    
    <category term="控制转移, 堆栈" scheme="https://blog.pridelzh.top/tags/%E6%8E%A7%E5%88%B6%E8%BD%AC%E7%A7%BB-%E5%A0%86%E6%A0%88/"/>
    
  </entry>
  
  <entry>
    <title>4-段寄存器&amp;寻址方式</title>
    <link href="https://blog.pridelzh.top/posts/78454568.html"/>
    <id>https://blog.pridelzh.top/posts/78454568.html</id>
    <published>2025-08-31T08:01:00.000Z</published>
    <updated>2025-08-31T08:01:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、段寄存器及使用"><a href="#一、段寄存器及使用" class="headerlink" title="一、段寄存器及使用"></a>一、段寄存器及使用</h1><h2 id="1、存储器分段"><a href="#1、存储器分段" class="headerlink" title="1、存储器分段"></a>1、存储器分段</h2><p>（1）物理地址相关</p><ul><li><p>内存(逻辑存储器)：CPU能通过<strong>CPU总线直接寻址访问</strong>的存储器</p></li><li><p>物理地址：<code>内存(逻辑存储器)</code>每一个字节单元有一个唯一的地址，称为物理地址。</p><ol><li>这个物理地址<strong>就是各存储器在CPU总线上的地址</strong>，利用它CPU可以直接访问到对应的存储空间</li><li><strong>CPU的地址线数量</strong>决定可产生的<strong>最大物理地址</strong>，n根地址线—&gt;最大地址2^n-1，32位CPU通常有32根地址线，因此32位CPU的电脑最大只能装4G的内存条</li></ol></li><li><p>物理地址空间：所有可形成的<code>物理地址</code>的集合</p><ol><li>物理地址空间大小<strong>不等于</strong>实际安装物理内存大小</li><li>Intel8086有20根地址线，物理地址的范围是0到FFFFF<br>Intel80386有32根地址线，物理地址的范围是0到FFFFFFFF</li></ol></li></ul><p>（2）存储器分段</p><ul><li><p>为了有效地管理存储器，常常把</p><ol><li>线性的<strong>物理地址空间</strong>划分为若干<strong>逻辑段</strong></li><li><strong>存储空间</strong>被划分为若干<strong>存储段</strong></li></ol><p>  可以认为<strong>逻辑段和存储段是对应的</strong>。</p></li><li><p>通常，运行的程序把<strong>不同的数据存储于存储器中的不同存储段</strong>，包括</p><ol><li><code>代码</code>：要执行的指令序列（存储于<code>代码段</code>）</li><li><code>数据</code>：要处理加工的内容（存储<code>数据段</code>）</li><li><code>堆栈</code>：按先进后出规则存取的区域（存储于<code>堆栈段</code>）</li></ol></li></ul><h2 id="2、逻辑地址"><a href="#2、逻辑地址" class="headerlink" title="2、逻辑地址"></a>2、逻辑地址</h2><h4 id="（1）逻辑地址"><a href="#（1）逻辑地址" class="headerlink" title="（1）逻辑地址"></a>（1）逻辑地址</h4><ul><li>分段后，程序中使用的某个存储单元总是属于某个段，所以可以 <code>某某段</code> <code>某某单元</code>方式表示存储单元</li><li><code>逻辑地址</code>：<strong>程序中用于表示存储单元的地址</strong><ol><li>由于采用分段存储管理方式，程序中使用的逻辑地址是二维的，第一维给出某某段，第二维给出段内的某某单元。</li><li>二维的逻辑地址可以表示为：<code>段号∶段内地址</code></li><li><code>(段内)偏移</code>：<strong>存储单元的物理地址与所在段起始地址的差值</strong>。这个差值恰好是段内地址，因此二维的逻辑地址又可以表示为：<code>段号∶偏移</code></li><li>实方式下，段号是<code>段值</code><br> 保护方式下，段号是<code>段选择子</code></li><li><code>有效地址/偏移地址</code>：逻辑地址中的偏移称作有效地址或偏移地址，汇编程序中，不同的数据往往固定存放在不同的段，有唯一对应的段寄存器，因而这个地址用的最多。比如指示代码的EIP、指示堆栈位置的ESP、EBP等都是存储的有效地址</li></ol></li></ul><h4 id="（2）逻辑地址转为物理地址"><a href="#（2）逻辑地址转为物理地址" class="headerlink" title="（2）逻辑地址转为物理地址"></a>（2）逻辑地址转为物理地址</h4><ul><li><p><code>物理地址 = 段起始地址 + 偏移</code></p></li><li><p>获得物理地址的过程：</p><ol><li><strong>由段号得到段起始地址</strong></li><li>加上偏移</li></ol></li><li><p>对于IA32</p><ol><li>保护方式下：物理地址32位，段起始地址32位，偏移32位</li><li>实方式下：物理地址20位，段起始地址20位，偏移16位</li><li>可以参考：<a href="https://blog.csdn.net/hbrqlpf/article/details/2942292">我理解的逻辑地址、线性地址、物理地址和虚拟地址(补充完整了)</a></li></ol><p>  对于8086：</p><ol><li>只有实方式：物理地址20位，段起始地址20位，偏移16位</li><li>可以参考：<a href="https://blog.csdn.net/MyySophia/article/details/51503086">逻辑地址、线性地址和物理地址之间的转换</a></li></ol></li><li><p>如果整个程序只有一个段，则二维逻辑地址退化为一维。段起始地址完全相同，偏移决定一切。如果用VS2010来编写嵌入汇编程序，那么就是这种情况，只考虑偏移即可</p></li></ul><h4 id="（3）三种地址小结"><a href="#（3）三种地址小结" class="headerlink" title="（3）三种地址小结"></a>（3）三种地址小结</h4><table><thead><tr><th>地址</th><th align="left">说明</th></tr></thead><tbody><tr><td>物理地址</td><td align="left">各存储器在CPU总线上的地址，是实际上可以直接访问到存储器的地址</td></tr><tr><td>逻辑地址</td><td align="left">段号∶偏移 方式描述的地址，需要进行一些计算才能转换为物理地址</td></tr><tr><td>有效地址</td><td align="left">就是逻辑地址中的偏移量</td></tr></tbody></table><h2 id="3、段寄存器"><a href="#3、段寄存器" class="headerlink" title="3、段寄存器"></a>3、段寄存器</h2><ul><li>作用：段寄存器存放着当前使用的逻辑地址中的段号（段值&#x2F;段选择子），用于获得段起始地址。</li><li>段寄存器是16位的，在实方式下存储16位段值；在保护方式下存储16位段选择子</li><li><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747644039768.webp" alt="1747644039768.webp"></li><li>若代码段、堆栈段、数据段在同一个存储段，则CS、DS、SS给出相同的段起始地址</li><li>若段寄存器给出的段起始地址为0，则偏移相当于物理地址</li></ul><h1 id="二、寻址方式"><a href="#二、寻址方式" class="headerlink" title="二、寻址方式"></a>二、寻址方式</h1><ul><li><code>寻址方式</code>：<strong>表示指令中操作数所在的方法</strong></li><li>CPU常用的三种寻址方式：<code>立即寻址</code>、<code>寄存器寻址</code>、<code>存储器寻址</code>，此外还有固定寻址和I&#x2F;O端口寻址</li><li>以上三种方式都是对于<strong>偏移地址</strong>的不同描述，对于段基址：<ol><li><strong>基址寄存器是EBP或ESP时，默认的段寄存器是SS</strong></li><li><strong>否则，默认的段寄存器是DS</strong></li></ol></li></ul><h3 id="1、立即寻址"><a href="#1、立即寻址" class="headerlink" title="1、立即寻址"></a>1、立即寻址</h3><ul><li><p><code>立即寻址方式</code>：<strong>操作数本身就包含在指令中，直接作为指令的一部分给出</strong>，这样的操作数称为<code>立即数</code></p></li><li><p>注意：</p><ol><li>只有源操作数可以使用立即寻址方式</li><li>如果立即数由多个字节构成，么在作为指令的一部分存储时，也采用“高高低低”规则。</li></ol></li><li><p>由于立即寻址方式的操作数是立即数，包含在指令中，所以执行指令时，不需要再到存储器中去取该操作数了。</p><ol start="3"><li>立即寻址，会自动配合目的操作数尺寸，不需要dword ptr等参数</li></ol></li><li><p>示例：</p></li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//以下源操作数使用立即寻址方式</span></span><br><span class="line">MOV   EAX, <span class="number">12345678</span>H      <span class="comment">//给EAX寄存器赋初值</span></span><br><span class="line">ADD   BX, <span class="number">1234</span>H           <span class="comment">//给BX寄存器加上值1234H</span></span><br><span class="line">SUB   CL, <span class="number">2</span>               <span class="comment">//从CL寄存器减去值2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//以下源操作数值一致，但是尺寸不同</span></span><br><span class="line">MOV   EDX, <span class="number">1</span>              <span class="comment">//源操作数是32位</span></span><br><span class="line">MOV   DX, <span class="number">1</span>               <span class="comment">//源操作数是16位</span></span><br><span class="line">MOV   DL, <span class="number">1</span>               <span class="comment">//源操作数是8位</span></span><br></pre></td></tr></table></figure><h3 id="2、寄存器寻址"><a href="#2、寄存器寻址" class="headerlink" title="2、寄存器寻址"></a>2、寄存器寻址</h3><ul><li><p><code>寄存器寻址方式</code>：<strong>操作数存在CPU内部寄存器，指令指定寄存器</strong></p></li><li><p>适用范围：</p><ol><li>8个32位寄存器：<code>EAX</code>,<code>EBX</code>,<code>ECX</code>,<code>EDX</code>,<code>ESI</code>,<code>EDI</code>,<code>EBP</code>,<code>ESP</code></li><li>8个16位寄存器：<code>AX</code>,<code>BX</code>,<code>CX</code>,<code>DX</code>,<code>BP</code>,<code>SI</code>,<code>DI</code>,<code>SP</code></li><li>8个8位寄存器：<code>AH</code>,<code>AL</code>,<code>BH</code>,<code>BL</code>,<code>CH</code>,<code>CL</code>,<code>DH</code>,<code>DL</code></li></ol></li><li><p>操作数在寄存器中，<strong>不需要访问存储器取得操作数，这样指令执行速度较快</strong></p></li><li><p>示例：</p></li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MOV   EBP, ESP        <span class="comment">//把ESP之值送到EBP</span></span><br><span class="line">ADD   EAX, EDX        <span class="comment">//把EAX之值与EDX之值相加，结果送到EAX</span></span><br><span class="line">SUB   DI, BX          <span class="comment">//把DI之值减去BX之值，结果送到DI</span></span><br><span class="line">XCHG  AH, DH          <span class="comment">//交换AH与DH之值</span></span><br></pre></td></tr></table></figure><h3 id="3、32位的存储器寻址方式"><a href="#3、32位的存储器寻址方式" class="headerlink" title="3、32位的存储器寻址方式"></a>3、32位的存储器寻址方式</h3><ul><li><code>存储器寻址方式</code>：<strong>给出存储单元偏移的寻址方式</strong>（在某个段内，给出存储单元的偏移即可找到它）</li><li><code>存储器操作数</code>：在指令中<code>[xxx]</code>意味着从<code>xxx</code>地址取数据，称这个<code>[xxx]</code>为存储器操作数。</li><li><code>有效地址</code>：<strong>要访问的存储单元的段内偏移</strong>。在32位的存储器寻址方式下，存储单元有效地址可达32位</li><li>采用32位的存储器寻址方式，能够给出32位的偏移</li><li>有多种存储器寻址方式<ol><li>直接寻址</li><li>寄存器间接</li><li>寄存器相对</li><li>基址加变址</li><li>通用方法</li></ol></li><li>说明：<ol><li>存储器操作数尺寸是<strong>字节&#x2F;字&#x2F;双字</strong></li><li><strong>默认指令中的寄存器操作数的尺寸决定了存储器操作数的尺寸</strong>；但也可以显式指定存储器操作数尺寸</li></ol></li></ul><table><thead><tr><th>修饰符</th><th>功能</th></tr></thead><tbody><tr><td>WORD PTR</td><td>指定尺寸为“字”</td></tr><tr><td>BYTE PTR</td><td>指定尺寸为“字节”</td></tr><tr><td>DWORD PTR</td><td>指定尺寸为“双字”</td></tr></tbody></table><h4 id="（1）直接寻址方式"><a href="#（1）直接寻址方式" class="headerlink" title="（1）直接寻址方式"></a>（1）直接寻址方式</h4><ul><li><code>直接寻址方式</code>：操作数在存储器中，指令直接包含<strong>操作数所在的存储单元的有效地址</strong>。</li><li>示例：</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">MOV   ECX, [<span class="number">95480</span>H]       <span class="comment">//源操作数采用直接寻址</span></span><br><span class="line">MOV   [<span class="number">9547</span>CH], DX        <span class="comment">//目的操作数采用直接寻址</span></span><br><span class="line">ADD   BL, [<span class="number">95478</span>H]        <span class="comment">//源操作数采用直接寻址</span></span><br></pre></td></tr></table></figure><ul><li><p>即寻址和直接寻址的区别：</p><ol><li>直接寻址中十六进制数表示地址，要到此地址取出操作数；立即寻址中表示操作数</li><li>直接寻址的地址要写在方括号<code>[]</code>中</li></ol></li><li><p>注意：</p><ol><li>直接寻址时，<code>[]</code>给出的是被取出数据的最低地址</li><li>按 “高高低低” 规则取出数据<br>  <img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747644493685.webp" alt="1747644493685.webp"></li></ol></li></ul><h4 id="（2）寄存器间接寻址方式"><a href="#（2）寄存器间接寻址方式" class="headerlink" title="（2）寄存器间接寻址方式"></a>（2）寄存器间接寻址方式</h4><ul><li><p><code>寄存器间接寻址</code>：<strong>操作数在存储器中，由八个32位通用寄存器之一给出操作数所在存储单元有效地址。</strong></p></li><li><p>寄存器间接寻址和寄存器寻址的区别：</p><ol><li>寄存器间接的Reg名称出现在方括号<code>[]</code>中</li><li>寄存器间的Reg中存储的是操作数所在地址；寄存器寻址的Reg存储的是操作数</li></ol></li><li><p>注意：</p><ol><li>操作数地址必须来自<strong>八个32位通用寄存器之一</strong></li></ol></li><li><p>示例：</p></li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">MOV   EAX, [ESI]    <span class="comment">//源操作数寄存器间接寻址，ESI给出有效地址</span></span><br><span class="line">MOV   [EDI], CL     <span class="comment">//目的操作数寄存器间接寻址，EDI给出有效地址</span></span><br><span class="line">SUB   DX, [EBX]     <span class="comment">//源操作数寄存器间接寻址，EBX给出有效地址</span></span><br></pre></td></tr></table></figure><h4 id="（3）通用方式"><a href="#（3）通用方式" class="headerlink" title="（3）通用方式"></a>（3）通用方式</h4><ul><li><p><strong>存储单元的有效地址可以由三部分内容相加构成</strong></p><ol><li>一个32位<strong>基地址寄存器</strong></li><li>一个可乘上比例因子<code>1/2/4/8</code>的32位<strong>变址寄存器</strong></li><li>一个8&#x2F;16&#x2F;32位<strong>位移量</strong></li></ol><p>  PS：可省去任意两部分<br>  <img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747644625918.webp" alt="1747644625918.webp"></p></li><li><p>注意：</p><ol><li>变址寄存器乘上的比例因子取值只能是<code>1/2/4/8</code>之一</li><li>三部分中可以任意省略。省略后的寻址方式又有不同名称，但都属于通用方式，前面提到的直接寻址方式和寄存器间接寻址方式也是属于通用方式</li></ol></li><li><p>示意图：</p></li><li><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747644764536.webp" alt="1747644764536.webp"></p></li><li><p>示例：</p><ol><li><code>寄存器相对寻址方式</code>：<code>[寄存器名+偏移]</code></li></ol>  <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">MOV   EAX, [EBX+<span class="number">12</span>H]      ;源操作数有效地址是EBX值加上<span class="number">12</span>H</span><br><span class="line">MOV   [ESI<span class="number">-4</span>], AL         ;目的操作数有效地址是ESI值减去<span class="number">4</span></span><br><span class="line">ADD   DX, [ECX+<span class="number">5328</span>H]     ;源操作数有效地址是ECX值加上<span class="number">5328</span>H</span><br></pre></td></tr></table></figure><ol start="2"><li><code>基址+变址寻址</code>：<code>[寄存器名+寄存器名]</code></li></ol></li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">MOV   EAX, [EBX+ESI]      ;源操作数有效地址是EBX值加上ESI值</span><br><span class="line">SUB   [ECX+EDI], AL       ;目的操作数有效地址是ECX值加上EDI值</span><br><span class="line">XCHG  [EBX+ESI], DX       ;目的操作数有效地址是EBX值加上ESI值</span><br></pre></td></tr></table></figure><pre><code>3. `基址+带放大因子的变址寻址`：`[寄存器名+寄存器名*放大因子+偏移]`</code></pre><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MOV   EAX, [ECX+EBX*<span class="number">4</span>]      ;EBX作为变址寄存器，放大因子是<span class="number">4</span></span><br><span class="line">MOV   [EAX+ECX*<span class="number">2</span>], DL       ;ECX作为变址寄存器，放大因子是<span class="number">2</span></span><br><span class="line">ADD   EAX, [EBX+ESI*<span class="number">8</span>]      ;ESI作为变址寄存器，放大因子是<span class="number">8</span></span><br><span class="line">SUB   ECX, [EDX+EAX<span class="number">-4</span>]      ;EAX作为变址寄存器，放大因子是<span class="number">1</span></span><br><span class="line">MOV   EBX, [EDI+EAX*<span class="number">4</span>+<span class="number">300</span>H] ;EAX作为变址寄存器，放大因子是<span class="number">4</span></span><br></pre></td></tr></table></figure><h4 id="（4）补充"><a href="#（4）补充" class="headerlink" title="（4）补充"></a>（4）补充</h4><ul><li><p>用 [address] 这样的方法从内存取值</p><ol><li>address是地址尾，也就是取出值的<strong>最低字节地址</strong></li><li>存入寄存器时，低地址对应寄存器低位；高地址对应高位（<strong>“高高低低”规则</strong>）</li><li>初始化好的全局变量，占用内存空间是连续的</li></ol></li><li><p>使用尺寸修饰符的示例：</p></li><li><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747644937172.webp" alt="1747644937172.webp"></p></li><li><p>关于存储器寻址的说明</p><ol><li>缺省段寄存器<br> （1）<strong>如果基址寄存器不是EBP&#x2F;ESP，缺省引用的段寄存器为DS</strong><br> （2）<strong>如果基址寄存器是EBP&#x2F;ESP，缺省引用的段寄存器为SS</strong><br> （3）<strong>如果EBP作为变址寄存器（ESP不能做变址寄存器），缺省引用的段寄存器为DS</strong></li><li>有效地址<br> （1）无论存储器寻址方式具体是哪种，如果基址寄存器、变址 寄存器、比例因子、位移量这些算出来超过32位，只有低32位有效</li></ol></li></ul><h1 id="三、取有效地址指令LEA"><a href="#三、取有效地址指令LEA" class="headerlink" title="三、取有效地址指令LEA"></a>三、取有效地址指令LEA</h1><table><thead><tr><th>名称</th><th align="left">LEA（取有效地址指令）</th></tr></thead><tbody><tr><td>格式</td><td align="left"><code>LEA REG,OPRD</code></td></tr><tr><td>动作</td><td align="left">把操作数OPRD的有效地址传送到REG</td></tr><tr><td>合法值</td><td align="left">OPRD：<strong>存储器操作数</strong>；</td></tr><tr><td></td><td align="left">REG：<strong>16&#x2F;32位通用寄存器</strong></td></tr><tr><td>注意</td><td align="left">此指令不影响标志寄存器</td></tr><tr><td></td><td align="left">此指令是取地址，和mov有本质区别</td></tr></tbody></table><ul><li>示例1：基本操作</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MOV   EDI, <span class="number">51234</span>H                 <span class="comment">//EDI=00051234H</span></span><br><span class="line">MOV   EAX, <span class="number">6</span>                      <span class="comment">//EAX=00000006H</span></span><br><span class="line">LEA   ESI, [EDI+EAX]              <span class="comment">//ESI=0005123AH</span></span><br><span class="line">LEA   ECX, [EAX*<span class="number">4</span>]                <span class="comment">//ECX=00000018H</span></span><br><span class="line">LEA   EBX, [EDI+EAX*<span class="number">4</span>+<span class="number">300</span>H]       <span class="comment">//EBX=0005154CH</span></span><br></pre></td></tr></table></figure><ul><li>示例2：指针的实现</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span>  <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">char</span>  chx, chy;                <span class="comment">//全局字符变量</span></span><br><span class="line"><span class="type">int</span>  <span class="title function_">main</span><span class="params">( )</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span>  *p1, *p2;            <span class="comment">//两字符型指针变量</span></span><br><span class="line">    <span class="comment">//嵌入汇编代码之一</span></span><br><span class="line">    _asm &#123;</span><br><span class="line">        LEA   EAX, chx         <span class="comment">//取变量chx的存储单元有效地址</span></span><br><span class="line">        MOV   p1, EAX          <span class="comment">//送到指针变量p1, p1 = &amp;chx</span></span><br><span class="line">        LEA   EAX, chy         <span class="comment">//取变量chy的存储单元有效地址</span></span><br><span class="line">        MOV   p2, EAX          <span class="comment">//送到指针变量p2, p2 = &amp;chy</span></span><br><span class="line">   &#125; </span><br><span class="line">    </span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">&quot;Input:&quot;</span>);           <span class="comment">//提示</span></span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%c&quot;</span>, p1);            <span class="comment">//键盘输入一个字符</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">//嵌入汇编代码之二</span></span><br><span class="line">    _asm &#123;</span><br><span class="line">        MOV   ESI, p1           <span class="comment">//取回变量chx的有效地址</span></span><br><span class="line">        MOV   EDI, p2           <span class="comment">//取回变量chy的有效地址</span></span><br><span class="line">        MOV   AL, [ESI]         <span class="comment">//取变量chx之值</span></span><br><span class="line">        MOV   [EDI], AL         <span class="comment">//送到变量chy中</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;ASCII:%02XH\n&quot;</span>, *p2);  <span class="comment">//显示之</span></span><br><span class="line">    <span class="keyword">return</span>  <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>示例三：取一个double数据</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="type">int</span>     iarr[<span class="number">5</span>] = &#123;<span class="number">55</span>, <span class="number">87</span>, <span class="number">-23</span>, <span class="number">89</span>, <span class="number">126</span>&#125;;   </span><br><span class="line"><span class="type">double</span>  darr[<span class="number">5</span>] = &#123;<span class="number">9.8</span>, <span class="number">2.77</span>, <span class="number">3.1415926</span>, <span class="number">1.414</span>, <span class="number">1.73278</span>&#125;;</span><br><span class="line">                                            </span><br><span class="line"><span class="type">int</span>  <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;   <span class="type">int</span>     ival;           <span class="comment">//整型变量</span></span><br><span class="line">    <span class="type">double</span>  dval;           <span class="comment">//双精度浮点</span></span><br><span class="line">    <span class="comment">//嵌入汇编</span></span><br><span class="line">    _asm  &#123;</span><br><span class="line">        LEA   EBX, iarr         <span class="comment">//把整型数组首元素的有效地址送EBX</span></span><br><span class="line">        MOV   ECX, <span class="number">3</span></span><br><span class="line">        MOV   EDX, [EBX+ECX*<span class="number">4</span>]  <span class="comment">//取出iarr的第4个元素</span></span><br><span class="line">        MOV   ival, EDX</span><br><span class="line">        ;</span><br><span class="line">        LEA   ESI, darr         <span class="comment">//把浮点数组首元素的有效地址送ESI</span></span><br><span class="line">        LEA   EDI, dval         <span class="comment">//把变量dval的有效地址送EDI</span></span><br><span class="line">        MOV   ECX, <span class="number">2</span></span><br><span class="line">        MOV   EAX, [ESI+ECX*<span class="number">8</span>]    <span class="comment">//取darr的第3个元素的低双字</span></span><br><span class="line">        MOV   EDX, [ESI+ECX*<span class="number">8</span>+<span class="number">4</span>]  <span class="comment">//取darr的第3个元素的高双字</span></span><br><span class="line">        MOV   [EDI], EAX          <span class="comment">//保存低双字</span></span><br><span class="line">        MOV   [EDI+<span class="number">4</span>], EDX        <span class="comment">//保存高双字</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;iVAL=%d\n&quot;</span>,ival);     <span class="comment">//显示为iVAL=89</span></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;dVAL=%.8f\n&quot;</span>,dval);   <span class="comment">//显示为dVAL=3.14159260</span></span><br><span class="line">    <span class="keyword">return</span>  <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>示例四：<strong>妙用LEA和存储器取值，进行多项式计算</strong></li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//待翻译的c函数</span></span><br><span class="line"><span class="type">int</span>  _fastcall  <span class="title function_">cf212</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span>    <span class="comment">//由寄存器传参数</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> ( <span class="number">3</span> * x + <span class="number">7</span> * y + <span class="number">200</span> );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//翻译成汇编后的核心代码（ECX传递x，EDX传递y）</span></span><br><span class="line">lea   eax, DWORD PTR [ecx+ecx*<span class="number">2</span>]    <span class="comment">//eax=3*x</span></span><br><span class="line">lea   ecx, DWORD PTR [edx*<span class="number">8</span>]        <span class="comment">//ecx=8*y</span></span><br><span class="line">sub   ecx, edx                      <span class="comment">//ecx=7*y</span></span><br><span class="line">lea   eax, DWORD PTR [eax+ecx+<span class="number">200</span>]  <span class="comment">//eax=3*x+7*y+200</span></span><br><span class="line">ret </span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、段寄存器及使用&quot;&gt;&lt;a href=&quot;#一、段寄存器及使用&quot; class=&quot;headerlink&quot; title=&quot;一、段寄存器及使用&quot;&gt;&lt;/a&gt;一、段寄存器及使用&lt;/h1&gt;&lt;h2 id=&quot;1、存储器分段&quot;&gt;&lt;a href=&quot;#1、存储器分段&quot; class=&quot;he</summary>
      
    
    
    
    <category term="汇编语言" scheme="https://blog.pridelzh.top/categories/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
    
    <category term="寄存器, 寻址方式" scheme="https://blog.pridelzh.top/tags/%E5%AF%84%E5%AD%98%E5%99%A8-%E5%AF%BB%E5%9D%80%E6%96%B9%E5%BC%8F/"/>
    
  </entry>
  
  <entry>
    <title>3-简单传送，加减指令&amp;标志寄存器</title>
    <link href="https://blog.pridelzh.top/posts/98989.html"/>
    <id>https://blog.pridelzh.top/posts/98989.html</id>
    <published>2025-08-31T08:00:00.000Z</published>
    <updated>2025-08-31T08:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、简单传送指令"><a href="#一、简单传送指令" class="headerlink" title="一、简单传送指令"></a>一、简单传送指令</h1><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250831163115110.webp" alt="1747051478856.webp"></p><ul><li>示例</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">MOVEAX, <span class="number">12345678</span>H<span class="comment">//EAX =12345678H</span></span><br><span class="line">MOVEBX, EAX<span class="comment">//EBX =12345678H</span></span><br><span class="line">MOVESI, <span class="number">256</span><span class="comment">//ESI =00000100H </span></span><br><span class="line">MOVECX, <span class="number">-1</span><span class="comment">//ECX =FFFFFFFFH</span></span><br><span class="line">MOVBX, ‘b’<span class="comment">//EBX =12340062H</span></span><br><span class="line">MOVAH, AL<span class="comment">//EAX =12347878H</span></span><br><span class="line">MOVCX, AX<span class="comment">//ECX =FFFF7878H</span></span><br><span class="line">MOVAX, SI<span class="comment">//EAX =12340100H</span></span><br><span class="line">MOVSI, BX<span class="comment">//ESI =00000062H</span></span><br><span class="line">MOVAL, BH<span class="comment">//EAX =12340100H</span></span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747051629241.webp" alt="1747051629241.webp"></p><ul><li>示例</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">XCHG    AL, AH          <span class="comment">//8位交换</span></span><br><span class="line">XCHG    SI, BX          <span class="comment">//16位交换</span></span><br><span class="line">XCHG    EAX, EBX        <span class="comment">//32位交换</span></span><br><span class="line"></span><br><span class="line">XCHG    AL, [EBX]       <span class="comment">//AL与由EBX指定的字节存储单元交换</span></span><br><span class="line">XCHG    [ESI], BX       <span class="comment">//BX与由ESI指定的字存储单元交换</span></span><br><span class="line">XCHG    EDX, [EDI]      <span class="comment">//EDX与由EDI指定的双字存储单元交换</span></span><br></pre></td></tr></table></figure><h1 id="二、加减指令"><a href="#二、加减指令" class="headerlink" title="二、加减指令"></a>二、加减指令</h1><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747051822803.webp" alt="1747051822803.webp"></p><ul><li>示例</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">ADD ECX,<span class="number">200</span><span class="comment">//使ECX加上200</span></span><br><span class="line">ADD EBX,ECX<span class="comment">//使EBX加上ECX的值</span></span><br><span class="line">ADD SI,<span class="number">10</span><span class="comment">//使SI加上10</span></span><br><span class="line">ADD DH,DL<span class="comment">//使DH加上DL的值</span></span><br><span class="line">ADD AL,<span class="number">5</span><span class="comment">//使AL加上5</span></span><br><span class="line">ADD EAX,[EBX]<span class="comment">//使EAX加上EBX的值指定的存储单元的值</span></span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747051907692.webp" alt="1747051907692.webp"></p><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057025764.webp" alt="1747057025764.webp"><br>示例：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">SUB    EDX,<span class="number">1000</span>       <span class="comment">//使EDX减去1000</span></span><br><span class="line">SUB    ESI,EBX        <span class="comment">//使ESI减去EBX值</span></span><br><span class="line">SUB    DI,<span class="number">20</span>          <span class="comment">//使DI减去20</span></span><br><span class="line">SUB    DH,CL          <span class="comment">//使DH减去CL值</span></span><br><span class="line">SUB    AL,<span class="number">7</span>           <span class="comment">//使AL减去7</span></span><br><span class="line">SUB    ECX,[EDI]      <span class="comment">//使ECX减去由EDI指定的双字存储单元值</span></span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057112278.webp" alt="1747057112278.webp"><br>示例：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">INC    ESI <span class="comment">//使寄存器ESI值加1</span></span><br><span class="line">INC    DI   <span class="comment">//使寄存器DI值加1</span></span><br><span class="line">INC    CL   <span class="comment">//使寄存器CL值加1</span></span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057173044.webp" alt="1747057173044.webp"><br>示例：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">DEC   EDI     <span class="comment">//使寄存器EDI值减1</span></span><br><span class="line">DEC   SI      <span class="comment">//使寄存器SI值减1</span></span><br><span class="line">DEC   AL      <span class="comment">//使寄存器AL值减1</span></span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057279293.webp" alt="1747057279293.webp"><br>示例：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MOV   AL,<span class="number">3</span>       <span class="comment">//AL=03H</span></span><br><span class="line">NEG   AL         <span class="comment">//AL=FDH（-3）</span></span><br><span class="line">MOV   EDX,<span class="number">-5</span>     <span class="comment">//EDX=FFFFFFFBH</span></span><br><span class="line">NEG   EDX        <span class="comment">//EDX=00000005H</span></span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057360282.webp" alt="1747057360282.webp"><br>示例：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">dp26</span></span><br><span class="line"><span class="comment">利用带符号加法ADC，处理可能产生的进位</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">unsigned</span> <span class="type">char</span> v1 = <span class="number">188</span>, v2 = <span class="number">172</span>, v3 = <span class="number">233</span>;<span class="comment">//3个字节变量</span></span><br><span class="line"><span class="type">unsigned</span> <span class="type">int</span> sum = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">_asm</span><br><span class="line">&#123;</span><br><span class="line">XOR EDX,EDX<span class="comment">//EDX清零，DX用于存放累加和</span></span><br><span class="line">ADD DL,v1<span class="comment">//加第一个字节</span></span><br><span class="line">ADC DH,<span class="number">0</span><span class="comment">//高8位相加</span></span><br><span class="line"></span><br><span class="line">ADD DL, v2<span class="comment">//加第二个字节</span></span><br><span class="line">ADC DH, <span class="number">0</span><span class="comment">//高8位相加（意在处理进位）</span></span><br><span class="line"></span><br><span class="line">ADD DL, v3<span class="comment">//加第三个字节</span></span><br><span class="line">ADC DH, <span class="number">0</span><span class="comment">//高8位相加（意在处理进位）</span></span><br><span class="line"></span><br><span class="line">MOV sum,EDX</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 通过ADC带位加法，使得可以保证处理进位，将仅为成功加到高位中</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;sum=%u\n&quot;</span>, sum);</span><br><span class="line">system(<span class="string">&quot;pause&quot;</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057421118.webp" alt="1747057421118.webp"><br>示例：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MOV AX,<span class="number">620</span>H</span><br><span class="line">SUB AL,<span class="number">21</span>H<span class="comment">//AX=06FFH, CF=1</span></span><br><span class="line">SBB AH,<span class="number">2</span><span class="comment">//AX=03FFH，CF=0(注意这里CF的影响)</span></span><br><span class="line">SBB AH,<span class="number">2</span><span class="comment">//AX=01FFH，CF=0</span></span><br></pre></td></tr></table></figure><h1 id="三、标志寄存器及使用"><a href="#三、标志寄存器及使用" class="headerlink" title="三、标志寄存器及使用"></a>三、标志寄存器及使用</h1><h1 id="1-标志寄存器"><a href="#1-标志寄存器" class="headerlink" title="1. 标志寄存器"></a>1. 标志寄存器</h1><ul><li>一个32位寄存器</li><li>用于<strong>反映处理器的状态和运算结果的某些特征</strong></li><li>可以暂时认为主要是：<strong>状态标志</strong> &amp; <strong>控制标志</strong></li><li>低16位对应8086的FLAGS寄存器<br>  <img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057567052.webp" alt="1747057567052.webp"><br>  <img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057610001.webp" alt="1747057610001.webp"></li></ul><h2 id="2-状态标志"><a href="#2-状态标志" class="headerlink" title="2. 状态标志"></a>2. 状态标志</h2><p>（1）CF进借位标志<br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057727585.webp" alt="1747057727585.webp"><br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057749584.webp" alt="1747057749584.webp"></p><p>（2）ZF零标志<br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057808999.webp" alt="1747057808999.webp"><br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057827002.webp" alt="1747057827002.webp"></p><p>（3）SF符号标志<br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057877487.webp" alt="1747057877487.webp"><br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057899713.webp" alt="1747057899713.webp"></p><p>（4）OF溢出标志<br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057931478.webp" alt="1747057931478.webp"><br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747057961047.webp" alt="1747057961047.webp"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、简单传送指令&quot;&gt;&lt;a href=&quot;#一、简单传送指令&quot; class=&quot;headerlink&quot; title=&quot;一、简单传送指令&quot;&gt;&lt;/a&gt;一、简单传送指令&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://raw.gitcode.com/Pridelzh/blo</summary>
      
    
    
    
    <category term="汇编语言" scheme="https://blog.pridelzh.top/categories/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
    
    <category term="寄存器" scheme="https://blog.pridelzh.top/tags/%E5%AF%84%E5%AD%98%E5%99%A8/"/>
    
  </entry>
  
  <entry>
    <title>云支教之旅</title>
    <link href="https://blog.pridelzh.top/posts/78862.html"/>
    <id>https://blog.pridelzh.top/posts/78862.html</id>
    <published>2025-08-29T09:25:34.797Z</published>
    <updated>2025-08-30T00:01:05.897Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>“一代人有一代人的使命，一代人有一代人的担当。”<br>——题记</p></blockquote><p>盛夏的阳光炽烈而温柔，映照着城市的屋檐，也照进了屏幕两端的世界。这个暑假，我选择了一条不同寻常的道路——踏入“云支教”的实践旅程。借助互联网的桥梁，我与一位来自邻近城市的中学生相遇，在“云端”牵起了一段关于学习、成长与责任的缘分。</p><p>我仍清晰记得第一次连线时的情景。少年拘谨地坐在摄像头前，声音轻得像风拂过水面。我微笑着打破僵局，告诉他我们是一同学习的伙伴，而不是居高临下的讲解者。从那天起，我们制定了详尽的暑期学习计划，规划了语数英的复习安排，划定了每周的“云课堂”时段。课堂上，我引导他巩固基础知识，帮助他找到学习的薄弱环节，并带着他一步步拆解难题。渐渐地，我看到他的眼神里闪烁出自信的光芒，那是一束正在被点燃的希望。</p><p>除了课堂，我们还有一项特别的“云运动”计划。虽然身处不同的城市，但我们约定每周三下午共同完成一次“线上运动挑战”。我们会各自记录跑步的里程，分享自己的锻炼成果；有时候还会比拼平板支撑、俯卧撑次数，哪怕隔着屏幕，汗水依然是彼此的见证。一次次的挑战，让少年不再沉溺于手机和游戏，而是学会了用汗水收获力量。</p><p>更让我印象深刻的，是我们共同完成的“家乡风物探索”活动。我请他用手机拍摄自己家乡的风景和人文故事，再由我来帮他梳理资料，一起在“云端”制作出一份关于家乡文化的小小展示文案。他第一次认真去走访村头的古祠堂、河畔的老榕树、集市上的传统小吃摊；他第一次用镜头记录自己熟悉却从未留意的事物。当他骄傲地在屏幕前向我展示这些作品时，我仿佛也在和他一同重新认识这片土地。<strong>这种探索，不只是看风景，而是理解生活、理解家乡、理解文化本身。</strong></p><p>就在这样一段段看似平凡的互动中，我逐渐意识到，所谓“支教”，并不是“我给予，你接受”的单向流动，而是一场双向奔赴的成长之旅。少年的自信在慢慢发芽，而我在教与学之间也愈加明白自己的责任感与使命感。</p><p>这种实践经历，让我对“新时代文明实践”有了更加具体、真切的感受。文明实践并不是书本上的抽象概念，也不仅仅是高悬在标语上的口号。它深深嵌入到社会的肌理里，融入到一场场具体的活动中，渗透进一个个普通人的生活里。正如习近平总书记所强调的，新时代文明实践中心要成为“传播党的创新理论、弘扬时代新风、推动文化自信的重要阵地”。而我们所做的云支教，正是把这种理念落到实处，让文明之花在看似平凡的日常里悄然绽放。</p><p>国家提出建设新时代文明实践中心，是为了打通思想宣传、文化服务、社会治理的“最后一公里”。而“云支教”就是一条跨越空间的数字纽带，让教育资源与文化交流突破时空限制，实现更大范围的共享。尤其在“双减”政策落地的背景下，教育公平成为社会关注的焦点。云支教的形式，使更多中学生能够获得优质的学习资源与精神陪伴，让“每一个孩子都能在同一片星空下闪耀”不再是遥不可及的梦想。</p><p>与此同时，《志愿服务条例》也为我们提供了制度保障，明确指出志愿服务是促进社会进步、提升社会文明程度的重要力量。这让我更加坚信，云支教不仅是一次志愿行动，更是文明传递的生动实践。技术让世界的距离缩短，而志愿精神让心与心的距离拉近。数字文明与志愿服务交织在一起，正在为新时代社会治理注入新的温度与力量。</p><p>我渐渐发现，文明实践的意义不仅在于“给予”，更在于“激发”。通过陪伴和引导，我看到少年慢慢养成自主学习的习惯，学会利用身边的资源，探索自己家乡的独特魅力。这种改变，不是单纯的成绩提升，而是一种对生活的热爱与对未来的渴望。<strong>这正是新时代文明实践的核心所在：让更多人有能力、有勇气去追求更加美好的生活。</strong></p><p>站在更高的维度回望，云支教不仅关乎一个中学生的成长，更是社会文明程度提升的缩影。教育资源的共享、文化认同的建立、青少年精神世界的丰富，这些细微而深远的变化，最终会汇聚成推动社会前行的磅礴力量。</p><p>而在这股力量中，我们青年一代责无旁贷。青年是国家的未来与希望，我们不仅是文明果实的享用者，更是文明火炬的传递者。每一次云端的耐心讲解、每一段远隔千里的情感连接，都是对社会文明的默默贡献。正如习近平总书记所言：“青年兴则国家兴，青年强则国家强。”我们的志愿服务，不只是帮助别人，更是在塑造自己的精神品质，锤炼责任与担当。</p><p>或许，云支教本身无法改变社会的宏观格局，但它能点亮力所能及的角落。无数青年志愿者的一束束星光，终将汇聚成照亮时代前行的文明之河。当这样的星光愈加璀璨，新时代文明的航程，便会在我们这一代人的书写下，延伸得更加辽阔而壮丽。</p><p>“长风破浪会有时，直挂云帆济沧海。”云课堂暂告一段落，但文明的实践永远不会结束。那一端少年的眼神依旧闪亮，而我的心中也燃起更炽烈的信念：让青春与文明同行，让责任与梦想并肩，用我们一代人的脚步，去丈量新时代文明实践的广阔大地。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;“一代人有一代人的使命，一代人有一代人的担当。”&lt;br&gt;——题记&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;盛夏的阳光炽烈而温柔，映照着城市的屋檐，也照进了屏幕两端的世界。这个暑假，我选择了一条不同寻常的道路——踏入“云支教”的实践旅程。借助互联网</summary>
      
    
    
    
    
    <category term="云支教" scheme="https://blog.pridelzh.top/tags/%E4%BA%91%E6%94%AF%E6%95%99/"/>
    
    <category term="社会实践" scheme="https://blog.pridelzh.top/tags/%E7%A4%BE%E4%BC%9A%E5%AE%9E%E8%B7%B5/"/>
    
    <category term="志愿服务" scheme="https://blog.pridelzh.top/tags/%E5%BF%97%E6%84%BF%E6%9C%8D%E5%8A%A1/"/>
    
  </entry>
  
  <entry>
    <title>2-IA32处理器及寄存器</title>
    <link href="https://blog.pridelzh.top/posts/78454566788.html"/>
    <id>https://blog.pridelzh.top/posts/78454566788.html</id>
    <published>2025-05-23T08:42:39.000Z</published>
    <updated>2025-05-23T08:42:39.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、IA32处理器简介"><a href="#一、IA32处理器简介" class="headerlink" title="一、IA32处理器简介"></a>一、IA32处理器简介</h1><ol><li>IA32系列处理器</li></ol><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250831153222357.webp" alt="1747050199903.webp"></p><ol start="2"><li>保护地址方式&amp;实地址方式</li></ol><ul><li>IA32系列CPU有3种工作方式：<strong>保护方式（包含一种子工作方式：虚拟8086方式）</strong>、<strong>实地址方式</strong>、<strong>系统管理方式</strong></li></ul><p>（1）保护方式</p><ol><li>保护方式是IA32系列处理器的<strong>常态工作方式</strong>，可以<strong>发挥全部性能和特点</strong> ,windows、Linux都运行于保护方式</li><li>全部32根地址线有效，<strong>可寻址4GB物理地址空间</strong></li><li>支持存储器分段管理 &amp; 可选的存储器分页管理机制</li><li>支持虚拟存储器的实现，<strong>用于指定存储单元的是面向虚拟存储器的虚拟地址</strong></li><li>提供完善的保护机制</li><li>支持操作系统实现多任务管理</li><li>支持虚拟8086方式</li></ol><p>（2）实地址方式</p><ol><li>实地址方式是最初的工作方式：<ol><li>开机&#x2F;重新设置系统后，IA32工作于实地址方式</li><li>很久以前8086&#x2F;8088等只支持实地址方式</li></ol></li><li>实地址方式下<strong>只能访问最低1MB物理地址空间</strong>（00000H-FFFFFH）</li><li>实地址方式下只支持存储器分段管理，且每个段大小限于64KB，段内有效地址范围0000H-FFFFH。不支持分页</li><li>储管理机制。<strong>可以认为实地址方式下用于指定要访问存储单元的线性地址就是真实地址</strong></li><li>实地址方式无法发挥IA32处理器 全部性能</li><li>指令集、执行环境和保护方式相同</li><li>实地址方式常被称为<strong>实方式</strong></li></ol><p>（3）工作方式的切换<br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747050901961.webp" alt="1747050901961.webp"></p><h1 id="二、通用寄存器及使用"><a href="#二、通用寄存器及使用" class="headerlink" title="二、通用寄存器及使用"></a>二、通用寄存器及使用</h1><ul><li>寄存器是处理器内的特殊存储单元</li><li>处理器内有多种不同用途的寄存器</li><li>寄存器分别有各自的名称，以便表示和访问</li></ul><ol><li>通用寄存器简介<ol><li>IA32系列CPU有<strong>8个32位通用寄存器</strong></li><li>通用寄存器用于<strong>存储数据</strong>、<strong>参与算数逻辑运算</strong>、<strong>给出存储单元地址</strong></li><li>名称：</li></ol><ul><li>32位：<code>EAX</code>,<code>EBX</code>,<code>ECX</code>,<code>EDX</code>,<code>ESI</code>,<code>EDI</code>,<code>EBP</code>,<code>ESP</code></li><li>16位：<code>AX</code>,<code>BX</code>,<code>CX</code>,<code>DX</code>,<code>BP</code>,<code>SI</code>,<code>DI</code>,<code>SP</code></li><li>8位：<code>AH</code>,<code>AL</code>,<code>BH</code>,<code>BL</code>,<code>CH</code>,<code>CL</code>,<code>DH</code>,<code>D</code><br> <img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747051079008.webp" alt="1747051079008.webp"></li></ul><ol start="4"><li>通用寄存器的高16位不可以单独使用，因为没有名字；但是可以单独用低16位、还可再拆开高低8位单独使用</li><li>通用寄存器的低16位叫：<code>AX</code>,<code>BX</code>,<code>CX</code>,<code>DX</code>,<code>BP</code>,<code>SI</code>,<code>DI</code>,<code>SP</code>，<strong>对应8086中的8个通用寄存器</strong></li><li>各寄存器作用简要说明</li></ol><ul><li><code>EAX</code>,<code>EBX</code>,<code>ECX</code>,<code>EDX</code>主要用于算术逻辑运算之中，如ADD&#x2F;SUB&#x2F;XOR&#x2F;OR等</li><li><code>ESI</code>,<code>EDI</code>,<code>EBP</code>,<code>ESP</code>主要用作保存内存地址的指针。</li></ul></li></ol><p><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747051190119.webp" alt="1747051190119.webp"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、IA32处理器简介&quot;&gt;&lt;a href=&quot;#一、IA32处理器简介&quot; class=&quot;headerlink&quot; title=&quot;一、IA32处理器简介&quot;&gt;&lt;/a&gt;一、IA32处理器简介&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;IA32系列处理器&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img</summary>
      
    
    
    
    <category term="汇编语言" scheme="https://blog.pridelzh.top/categories/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
    
  </entry>
  
  <entry>
    <title>1-基础知识</title>
    <link href="https://blog.pridelzh.top/posts/7845654.html"/>
    <id>https://blog.pridelzh.top/posts/7845654.html</id>
    <published>2025-05-23T08:38:58.000Z</published>
    <updated>2025-05-23T08:38:58.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、CPU简介"><a href="#一、CPU简介" class="headerlink" title="一、CPU简介"></a>一、CPU简介</h1><ol><li><p>目标代码<br> &#x3D;&#x3D;目标代码&#x2F;目标程序：由机器指令组成的程序&#x3D;&#x3D;</p><ul><li>CPU只能执行机器指令</li><li>高级语言编写的程序，最后都要转换成机器指令组成的程序，即目标代码，这样才能执行</li><li>目标代码是二进制编码的</li></ul></li></ol><ul><li>程序编译过程<ul><li><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250831190751749.webp" alt="1747049018950.webp"></li></ul></li></ul><ol start="2"><li><p>CPU基本功能<br> CPU的基本功能包括：&#x3D;&#x3D;执行机器指令、暂存少量数据、访问寄存器&#x3D;&#x3D;</p><p> 执行机器指令<br> 机器指令：CPU能直接识别并执行的指令<br> 指令集：一款CPU能执行的全部指令的集合<br> 指令的分类：<br> （1）数据传送指令<br> （2）转移指令<br> （3）处理器控制指令<br> （4）其他指令<br> 暂存少量数据<br> 大部分指令是对数据进行运算和处理。运算数据和运算结构存在<br> （1）寄存器（CPU中）<br> （2）存储器中（内存）<br> 利用CPU内寄存器存取运算数据和结果效率最高。汇编器会充分利用CPU中仅有的寄存器，编写汇编时也要注意<br> 访问存储器<br> 存储器：CPU可以直接访问的计算机系统的物理内存<br> 由机器指令组成的目标程序存储于存储器中，部分待处理数据也是<br> 存储器（内存）由一系列存储单元线性组成，最基本的存储单元为一个字节。为了标识和存取每一个单元，给每个单元一个编号（即地址）</p></li></ol><h1 id="二、汇编语言的概念"><a href="#二、汇编语言的概念" class="headerlink" title="二、汇编语言的概念"></a>二、汇编语言的概念</h1><ol><li>机器指令</li></ol><ul><li>由CPU直接识别并执行的指令称为机器指令，采用<strong>二进制编码</strong></li><li>一般由<code>操作码</code>和<code>操作数</code>两部分构成<br>  （1）<code>操作码</code>指出要进行的 <code>操作</code>&#x2F;<code>运算</code>…<br>  （2）<code>操作数</code>指出参与操作的 <code>对象</code>&#x2F;<code>结果存放位置</code>&#x2F;<code>数据</code>…</li><li>通常用<strong>十六进制形式写出机器指令</strong></li><li><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747049313452.webp" alt="1747049313452.webp"></li></ul><ol start="2"><li><p>汇编指令格式</p><ol><li>用指令助记符、地址符号等表示的指令称为汇编格式指令</li><li>格式：<code>[标号:] 指令助记符 [操作数表]</code>，其中<code>助记符</code>是必须的，<code>操作数</code>随指令而定，<code>标号</code>可有可无</li></ol></li><li><p>汇编语言的优缺点</p><ol><li>优：效率高</li><li>缺：繁琐、难调试</li></ol></li></ol><h1 id="三、数据的表示和存储"><a href="#三、数据的表示和存储" class="headerlink" title="三、数据的表示和存储"></a>三、数据的表示和存储</h1><ol><li><p>数值数据的表示</p><ul><li>数的二进制表示</li><li>有符号数的补码表示</li><li>符号扩展（扩展符号位）</li><li>数值数据表示范围</li><li>BCD码（常用8421）</li><li>十六进制表示(4位二进制转换1位十六进制，通常后加H)</li></ul></li><li><p>非数值数据表示</p><ul><li>ASCII码</li><li>变形国标码</li></ul></li><li><p>基本数据类型</p><ul><li>计算机存取的以二进制位表示的信息位数一般是8的倍数，有专门名称<br> <img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747049719596.webp" alt="1747049719596.webp"></li></ul></li><li><p>数据的存储</p></li></ol><ul><li>使用小端存储：数据的低位保存在内存的低地址中，而数据的高位保存在内存的高地址中，这种存储模式将地址的高低和数据位权有效地结合起来，高地址部分权值高，低地址部分权值低，和我们的逻辑方法一致。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、CPU简介&quot;&gt;&lt;a href=&quot;#一、CPU简介&quot; class=&quot;headerlink&quot; title=&quot;一、CPU简介&quot;&gt;&lt;/a&gt;一、CPU简介&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;目标代码&lt;br&gt; &amp;#x3D;&amp;#x3D;目标代码&amp;#x2F;目标程序：由机器指令</summary>
      
    
    
    
    <category term="汇编语言" scheme="https://blog.pridelzh.top/categories/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
    
  </entry>
  
  <entry>
    <title>0-基础概念</title>
    <link href="https://blog.pridelzh.top/posts/78687865.html"/>
    <id>https://blog.pridelzh.top/posts/78687865.html</id>
    <published>2025-05-23T08:36:49.000Z</published>
    <updated>2025-05-23T08:36:49.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="这里有汇编语言的基础概念"><a href="#这里有汇编语言的基础概念" class="headerlink" title="这里有汇编语言的基础概念"></a>这里有汇编语言的基础概念</h1><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250831153040439.webp" alt="1747048719342.webp"><br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747048749989.webp" alt="1747048749989.webp"><br><img src="https://cdn.jsdelivr.net/gh/pridelzh/blogbed@main/1747048777898.webp" alt="1747048777898.webp"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;这里有汇编语言的基础概念&quot;&gt;&lt;a href=&quot;#这里有汇编语言的基础概念&quot; class=&quot;headerlink&quot; title=&quot;这里有汇编语言的基础概念&quot;&gt;&lt;/a&gt;这里有汇编语言的基础概念&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://raw.gitcode.</summary>
      
    
    
    
    <category term="汇编语言" scheme="https://blog.pridelzh.top/categories/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/"/>
    
    
    <category term="概念" scheme="https://blog.pridelzh.top/tags/%E6%A6%82%E5%BF%B5/"/>
    
  </entry>
  
  <entry>
    <title>6-LALR预测分析表</title>
    <link href="https://blog.pridelzh.top/posts/51.html"/>
    <id>https://blog.pridelzh.top/posts/51.html</id>
    <published>2025-05-16T13:28:41.000Z</published>
    <updated>2025-05-16T13:28:41.000Z</updated>
    
    <content type="html"><![CDATA[<ul><li><p>研究LALR的原因</p><ul><li>规范LR分析表的状态数偏多</li></ul></li><li><p>LALR特点</p><ul><li>LALR和SLR的分析表有同样多的状态，比规范LR分析表要小得多</li><li>LALR的能力介于SLR和规范LR之间</li><li>LALR的能力在很多情况下已经够用</li></ul></li><li><p>LALR分析表构造方法</p><ul><li>通过合并规范LR(1)项目集来得到</li><li>合并核心项</li></ul></li></ul><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250514123918667.webp" alt="image.png"></p><p>写出自动机，发现有共同点<br><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250514124019042.webp" alt="image.png"></p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250514124348631.webp" alt="image.png"></p><p>合并之后的<br><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250514124451227.webp" alt="image.png"></p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250514124510893.webp" alt="image.png"></p><p>分析表<br><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250514124529509.webp" alt="image.png"></p><p>该报错的依旧是报错<br><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250514124554044.webp" alt="image.png"></p><p>是否会引起冲突</p><p>对于 LR(1) 文法, 合并得到的 LALR(1) 分析表是否会引入冲突？</p><p>·     <strong>不会</strong>引入<strong>移入&#x2F;归约</strong>冲突</p><p>假设合并后出现冲突，[A → α·, a] 与 [B → β · aγ, b]</p><p>则在 LR(1) 自动机中, 存在某状态同时包含 [A → α·, a] 与 [B → β · aγ, c] (c随便是什么)</p><p>·     <strong>可能会</strong>引入<strong>归约&#x2F;归约</strong>冲突</p><p><img src="https://raw.gitcode.com/Pridelzh/blogbed/raw/main/20250516204236967.webp" alt="image.png"></p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>语法分析: </p><p>自顶向下:  LL(1)</p><p>自底向上:  LR(0),  SLR(1), LALR(1), LR(1)</p><p>LL(1), LR(0), SLR(1), LALR(1), LR(1)文法之间的关系</p><p><a href="https://blog.csdn.net/zuzhiang/article/details/79047743">https://blog.csdn.net/zuzhiang/article/details/79047743</a></p><p>LR(0) &lt; SLR(1) &lt; LALR(1) &lt; LR(1)</p><p>即某个文法，如果是LR(0)文法，</p><p>那其一定是SLR(1)文法，</p><p>反过不成立。</p><p>什么是LR(0)文法?</p><p>LR(0)自动机, LR(0)分析表</p><p>LL(1) 和他们没关系</p><p>所有的二义性文法都不是上述文法.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;ul&gt;
&lt;li&gt;&lt;p&gt;研究LALR的原因&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;规范LR分析表的状态数偏多&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LALR特点&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LALR和SLR的分析表有同样多的状态，比规范LR分析表要小得多&lt;/li&gt;
&lt;li&gt;LALR的</summary>
      
    
    
    
    <category term="编译原理" scheme="https://blog.pridelzh.top/categories/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86/"/>
    
    
    <category term="编译原理" scheme="https://blog.pridelzh.top/tags/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86/"/>
    
    <category term="LALR分析表" scheme="https://blog.pridelzh.top/tags/LALR%E5%88%86%E6%9E%90%E8%A1%A8/"/>
    
  </entry>
  
</feed>
