当前位置: 首页 > 编程日记 > 正文

一步步学习汇编(8)之指令

要理解ret,retfcall指令,必须要先理清以下汇编基础知识:

一.             [bx]和内存单元<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

  [bx]是什么呢?

       [0]有些类似,[0]表示内存单元,它的偏移地址是0

n       我们要完整地描述一个内存单元,需要两种信息:

n       1)内存单元的地址;

n       2)内存单元的长度(类型)。

n       我们用[0]表示一个内存单元时,0 表示单元的偏移地址,段地址默认在ds中,单元的长度(类型)可以由具体指令中的其他操作对象(比如说寄存器)指出

   所以,按照这样的原理,我们对如下指令可以这样理解:

   Mov ax,0]:表示将一个内存单元的内容送入ax寄存器。其中内存单元的长度为2字节,偏移地址为0,段地址在ds中。

   Mov al,0]:表示将一个内存单元的内容送入al寄存器。其中内存单元的长度为1字节,偏移地址为0,段地址在ds中。

   Mov ax,[bx]:表示将一个内存单元的内容送入ax寄存器。其中内存单元的长度为2字节,偏移地址在bx,段地址在ds中。

例子:比如AX=1234BX=1000 DS1000内容为1111

      mov [bx],axAX的值赋予BX所指向的内存单元 
 
那么执行后AX=1234BX=1000DS1000的内容为1234

      这种做法就叫做:寄存器间接寻址

二. 描述性符号()

     为了描述上的简洁,我们将使用一个描述性的符号 “() ”来表示一个寄存器或一个内存单元中的内容。

     例如:

     (ax):表示寄存器ax中的内容。

     20000h)表示内存单元20000h中的内容,(?)括号中的表示物理地址。

     ((ds)*16+(bx)),如果ds中的内容为1230,bx中的内容为4567,(1230*16+4567)可以理解为:

                    Ds中的内容作为段地址,bx中的内容作为偏移地址,这样形成的物理地址的内容。

     为了加强理解,我们看看下面的如何描述:

n        我们看一下(X)的应用,比如:

n        1ax中的内容为0010H,我们可以这样来描述:(ax)=0010H

n        22000:1000 处的内容为0010H,我们可以这样来描述:(21000H)=0010H

n        3)对于mov ax,[2]的功能,我们可以这样来描述:(ax)=((ds)*16+2)

n        4)对于mov [2],ax 的功能,我们可以这样来描述:((ds)*16+2)=(ax)

n        5)对于 add ax,2 的功能,我们可以这样来描述:(ax)=(ax)+2

n        6)对于add ax,bx的功能,我们可以这样来描述:(ax)=(ax)+(bx)

n        7)对于push ax的功能,我们可以这样来描述:
      (sp) = (sp)-2

          ((ss)*16(sp))=(ax)

三. mov ax,2 那其机器代码是 B8 02 00,这个设计计算机指令,汇编语言教材第一章一般就讲这个,B8表示把后面的一个WORD存放到AX里面,比如MOV AL,1的机器指令是B0 01B0就表示把后面的字节保存到AL里面,而MOV AH,1的机器指令是B4 01

四.总结:现在我们通过一个实例,将上述的指令综合一下:

题目:从offffh:0006内存单元中取一个值,并相加三次

分析:(1)运算后的结果是否会超出dx所能存储的范围?

       Ffff:0006 单元中的数是一个字节型的数据,范围在0~255之间,则用它和3相乘结果不会大于65535,可以在dx 中存放下

2)我们用循环累加来实现乘法,用哪个寄存器进行累加?
我们将ffff:0006单元中的数赋值给ax,用dx进行累加。先设(dx)=0,然后做3(dx)=(dx)+(ax)

3 ffff:0006单元是一个字节单元,ax是一个 16 位寄存器,数据长度不一样,如何赋值?

我们说的是赋值,就是说,让 ax 中的数据的值(数据的大小)和ffff:0006 单元中的数据的值(数据的大小)相等。

8 位数据01H16位数据0001H的数据长度不一样,但它们的值是相等的。

n        那么我们如何赋值?

n        ffff:0006单元中的数据是XXH,若要ax中的值和ffff:0006单元中的相等,ax中的数据应为00XXH

   所以,若实现ffff:0006单元向ax 赋值,我们应该令(ah)=0(al)=(ffff6H)

n        实现计算ffff:0006单元中的数乘以3,结果存储在dx中的程序代码。

代码:

assume cs:codesg

codesg segment

     mov ax,0ffffh

     mov ds,ax

       mov bx,6

       mov al,[bx]

       mov ah,0

       mov dx,0

       mov cx,3

      s:add dx,ax

       loop s

       mov ax,<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />4c00h

       int 21h

codesg ends

end

现在我们用debug来跟踪这个程序:

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />91.jpg

我们来分析一下:

1.       图中ds=165h,所以程序cs=166f,ss的值也为166f,为什么?如果还不清楚的可以看看我们前面的学习汇编系列(大略提一下:不明白cs的值的,请仔细回头去看.exe文件装载的那张图,ss的值不明白的请回头看栈的那张图),实际上程序运行是,先要找到一块起始地址的内存,此处起始地址为:ds:0,但是在.exe加载时,因为dos需要与.exe文件通讯,所以会有一块psp区用来存放通讯的内容。所以此处cs的值实际上是cs=ds+10h=166F,我们说cpu取得内存单元,就是通过cs:ip,所以cs:0实际上就是指程序的第一条指令。

2.       同样的道理,此时指针也是指向程序的第一条指令,所以指针的段地址也为166f,不明白的回头去看栈的原理,这里就不多说了。

3.       破解软件时,我们要先学会用g命令,为了使大家看得更清楚一点,我们将循环次数改为10

我们可以使用g 0012,则会自动执行到偏移地址为0012处。

当我们遇到loop时,因为后面的循环次数太多,我们可以用p命令。也可以用g 0016

4.       同一种写法,不同的处理方式:

对于这种写mov al,[0]debug表示:把ds:0内存单元处的内容放入al.

                      源程序中表示:就是把数据0放入al中。

解决方法:1.用过渡寄存器,比如:bx,如下:

            Mov bx,0

            Mov ax,[bx]

          2Mov ax,ds:[0]

五.活用:loop[bx]的联合应用

例子:计算ffff:0~ffff:b单元中的数据的和,结果存储在dx中。

n        我们还是先分析一下

1.运算后的结果是否会超出 dx 所能存储的范围?

 ffff:0ffff:b内存单元中的数据是字节型数据,范围在0255之间,12个这样的数据相加,结果不会大于 65535 ,可以在dx中存放下。

2. 我们是否将 ffff:0ffff:b中的数据直接累加到dx中?

 当然不行,因为ffff:0ffff:b中的数据是8位的,不能直接加到16位寄存器dx中。

3.我们能否将ffff:0ffff:b中的数据累加到dl中,并设置(dh=0,从而实现累加到dx中的目标?


 这也不行,因为
dl8位寄存器,能容纳的数据的范围在小 255 之间,ffff : 0ffff:b中的数据也都是 8 位,如果仅向dl中累加12 8 位数据,很有可能造成进位丢失。

4. 我们到底怎样将用ffff:0ffff:b中的8位数据,累加到16位寄存器dx中? 


我们将内存单元中的
8 位数据赋值到一个16位寄存器ax中,再将ax中的数据加到dx上,从而使两个运算对象的类型匹配并且结果不会超界。

分析后,我们现在来写出代码:

Mov ax,0ffffh

Mov ds,ax

Mov bx,0

Mov dx,0

Mov cx,0

S:Mov al,[bx]

Mov ah,0

Add dx,ax

Inc bx

Loop s

请大家自行加上伟指令运行。

通过前面的介绍,我们知道了[寄存器]的含义以及各个寄存器之间如何灵活计算的方式,这是学习破解的基础,那么破解时入口地址的确认以及如何查看反汇编的数据段,代码段,栈段呢?看下面的几个基础点。

六.在一个段中存放数据、代码、栈,我们先来体会一下不使用多个段时的情况;

1. 考虑这样一个问题,编程计算以下8个数据的和,结果存在ax 寄存器中:

   0123H0456H0789H0abcH0defH0fedH0cbaH0987H

看下面的程序:

assume cs:codesg

codesg segment

       dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

      mov bx,0

       mov ax,0

       mov cx,8

 s: add ax,cs:[bx]

       add bx,2

       loop s

       mov ax,4c00h

       int 21h

codesg ends

end

n        解释一下,程序第一行中的 “dw”的含义是定义字型数据。dwdefine word

n        在这里,我们使用dw定义了8个字型数据(数据之间以逗号分隔),它们所占的内存空间的大小为16个字节

n        8个数据的偏移地址是多少呢?

   因为用dw定义的数据处于代码段的最开始,所以偏移地址为0,这8 个数据就在代码段的偏移02468ACE处。

   程序运行时,它们的地址就是CS:0CS:2CS:4CS:6CS:8CS:ACS:CCS:E

2.如何让这个程序在编译后可以存系统中直接运行呢?我们可以在源程序中指明界序的入口所在.

n           Debug 载后,我们可以将 IP 设置为10h,从而使CS:IP指向程序中的第一条指令。然后再用T命令、P命令、或者是G 命令执行。

n        可是这样一来,我们就必须用Debug 来执行程序。

   程序 6.1 编译成可执行文件后,在系统中直接运行可能会出现问题,因为程序的入口处不是我们所希望执行的指令

n        我们在程序的第一条指令的前面加上了一个标号start,而这个标号在伪指令end的后面出现。end 除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。

2.通过上面的分析,我们明白了在代码段中使用数据的情况,现在在来看看在代码段中使用栈的情况。

n        完成下面的程序,利用栈,将程序中定义的数据逆序存放。

   assume cs:codesg

   codesg segment

      dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

      ?

   code ends

   end

分析:

(1).程序运行时,定义的数据存放在cs:0~cs:15单元中,共8个字单元。依次将这8个字单元中的数据入栈,然后再依次出栈到这 8 个字单元中,从而实现数据的逆序存放。

(2).问题是,我们首先要有一段可当作栈的内存空间。如前所述,这段空间应该由系统来分配。我们可以在程序中通过定义数据来取得一段空间,然后将这段空间当作栈空间来用。

程序如下所示:

assume cs:codesg

codesg segment

     dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

     dw 0,0,0,0,0,0,0,0

  start:mov ax,cs

  mov ss,ax

  mov sp,32

  mov bx,0

  mov cx,8

 

  s: push cs:[bx]

     add bx,2

     loop s

     mov bx,0

     mov cx,8

   s0:pop cs:[bx]

      add bx,2

      loop s0

      mov ax,4c00h

      int 21h

codesg ends

end start

解释:(1.其中mov ax,cs

           mov ss,ax

           mov sp,32

   我们要讲 cs:16 ~ cs:31 的内存空间当作栈来用,初始状态下栈为空,所以 ss:sp要指向栈底,则设置ss:sp指向cs:32


七。看下面的代码,实现了两个字符串变大写和变小写的方法。

ContractedBlock.gifExpandedBlockStart.gifCode
assume cs:codesg,ds:data
data segment
db 
'BaSiC'
db 
'iNfOrMaTiOn'
data ends
codesg segment
  start:mov ax,data
        mov ds,ax
 mov bx,
0
        mov cx,
5
    s:mov al,[bx]
 and al,11011111B
 mov [bx],al
 inc bx
 loop s
 mov bx,
5
 mov cx,
11
     s0:mov al,[bx]
 or al,00100000B
 mov [bx],al
 inc bx
 loop s0
        mov ax,4c00h
        
int 21h
codesg ends
end start


1.  数据段是放在程序开头的,程序指令执行段是在数据段之后,所以数据段的首地址为:ds+10h.

2.  注意data,and,or ,[bx]的用法。

3.  [bx][bx+idata]的用法。后者只是取出bx中的数值(即偏移地址)后+一个数=新的偏移地址而已。例如:

2000:1000 00 be 00 06 00 05

Mov ax,2000h

Mov ds,ax

Mov bx,1000h

Mov cx,[bx] 那么此时cx=00

Mov cx,[bx+1],那么此时cx=be

 4.sidi,与bx相似,只是不能分成两个8位寄存器。所以在传输时,一般按字传。(比如将一个字符串复制到另外的空间地址)。

5.mov ax,[bx+si],bxsi]此种方式与数组类似。一般si计数,然后通过si找新偏移地址的数据。例如:

  Mov ax,2000h

  Mov ds,ax

  Mov bx,1000h

  Mov si,0

 

  Mov ax,[bx+si]

  Inc si

 

6.[bx+si+idata]与[bx+si]类似。

最后总结一下:

n          如果我们比较一下前而用到的几种定位内存地址的方法(可称为寻址方式),就可以发现有以下几种方式:

n              1[iata] 用一个常量来表示地址,可用于直接定位一个内存单元;

n              2[bx]用一个变量来表示内存地址,可用于间接定位一个内存单元;

n              3[bx+idata] 用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元;

n              4[bx+si]用两个变量表示地址;

n              5[bx+si+idata] 用两个变量和一个常量表示地址。



转载于:https://www.cnblogs.com/mfm11111/archive/2009/03/27/1423099.html

相关文章:

【转】不分主副卡!全网通5.0时代到来

全网通在今天已经不是噱头了&#xff0c;它经历了有5年时间&#xff0c;从过去的全网通1.0到现在的全网通5.0&#xff0c;可以说有这长足的发展。今年&#xff0c;小米率先了支持全网通5.0的小米MIX 2S和红米Note5&#xff0c;可以双卡双待4G&#xff0c;一边打电话一边打游戏不…

1048 Find Coins(散列解法)

1. 开始测试点3答案错误&#xff0c;看参考书发现是&#xff0c;读题时根据硬币最大面值500设置的数组大小&#xff0c;但是后来会用总面值减去硬币面值&#xff0c;这个大小在[1,999)&#xff0c;因此散列表的大小应该设为1010。 2. 学会了一个小技巧。main函数可以不止一处r…

简单ThreadPool实现

由于最近需要用多线程处理一些问题&#xff0c;一开始我用了.net默认的ThreadPool&#xff0c;感觉不是很适合。于是我自己实现了一个简单的ThreadPool。 写的比较简单&#xff0c;有兴趣的朋友一起看看&#xff0c;共同改进。 代码主要由ThreadPoolEx,WorkItem,WorkQueue组成。…

CSS中强大的EM

使用CSS也好久了&#xff0c;但一直都是在使用“px”来设置Web元素的相关属性&#xff0c;未敢使用“em”。主要原因是&#xff0c;对其并不什么了解&#xff0c;只知道一点概念性的东西&#xff0c;前段时间在项目中要求使用“em”作为单位设置元素&#xff0c;所以从头对“em…

16.1、python初识面向对象(1)

初识面向对象 楔子 你现在是一家游戏公司的开发人员&#xff0c;现在需要你开发一款叫做<人狗大战>的游戏&#xff0c;你就思考呀&#xff0c;人狗作战&#xff0c;那至少需要2个角色&#xff0c;一个是人&#xff0c; 一个是狗&#xff0c;且人和狗都有不同的技能&#…

1080 Graduate Admission

1.因为这道题第一次认真想了高考录取的规则&#xff0c;对学生按照先总分再gE的原则进行从高到低排序&#xff0c;排名允许重复。再按照排名高到低对每个学生的每个志愿进行遍历&#xff0c;当一个学生处理完&#xff0c;再进行下一个。 2.由于最后是要输出学生的原始序号&…

vc++实现无进程无DLL无硬盘文件无启动项的ICMP后门后门程序

客户端 #include <winsock2.h>#include <stdio.h>#include <stdlib.h> #pragma comment(lib,"ws2_32.lib") char SendMsg[256]; /* The IP header */typedef struct iphdr {unsigned int h_len:4; //4位首部长度unsigned int version:4; //IP版本…

arm linux 启动之一:汇编启动到start_kernel

描述arm linux启动的概要过程&#xff0c;以S5PV210(Cortex A8)为例&#xff0c;本文描述第一个阶段。 一、arm linux的引导 uboot在引导arm linux&#xff08;uImage镜像&#xff09;到SDRAM之后&#xff0c;通过bootm命令对uImage镜像的64个字节头进行解释&#xff0c;获取li…

Sql Server 因为触发器问题导致数据库更新报错“在触发器执行过程中引发了错误,批处理已中止”的问题处理...

在维护一个非常旧的项目时&#xff0c;由于该项目版本已经非常老了&#xff0c;而且在客户现场运行的非常稳定&#xff0c;更要命的是本人目前没有找到该项目的代码&#xff0c;为了处理一个新的需求而且还不能修改程序代码&#xff0c;于是决定从数据库入手&#xff0c;毕竟该…

1070 Mooncake

1. 一道典型的贪心题&#xff0c;策略是尽可能地多出售单价高的月饼。 2. 开始有一个用例没有通过&#xff0c;看了参考书&#xff0c;说是质量虽然给的都是整数&#xff0c;但是为了计算不出错&#xff0c;需要声明为浮点型。改了以后果然就通过了&#xff0c;但是个中原理不…

Java数组合并,完成排序,从时间复杂度,和空间复杂度考虑

2019独角兽企业重金招聘Python工程师标准>>> 提供方法&#xff0c;直接调用&#xff0c;支持任意个数组的合并成一个数组&#xff0c;并且完成排序&#xff0c;每个数组元素个数不定。需要提供两个方法&#xff0c;分别做到时间复杂度最低、空间复杂度最低。并说明两…

WPF中Auto与*的差别

Auto 表示自己主动适应显示内容的宽度, 如自己主动适应文本的宽度,文本有多长,控件就显示多长. * 则表示按比例来分配宽度. <ColumnDefinition Width"3*" /> <ColumnDefinition Width"7*" /> 相同,行能够这样定义 <RowDefinition Height&qu…

个人电脑优化方案

2009年4月13日 文件删除--系统默认磁盘清理--批处理清除无用文件--使用优化软件如优化大师 Codeecho off echo 正在清除系统垃圾文件&#xff0c;请稍等 del /f /s /q %systemdrive%\*.tmp del /f /s /q %systemdrive%\…

1037 Magic Coupon

1. 贪心算法题&#xff0c;贪心策略&#xff1a;两组乘子相乘&#xff0c;每个数字至多用一次&#xff0c;希望得到最大的乘积。那么让A组绝对值最大的正数和B组最绝对值最大的正数相乘&#xff0c;次大的和次大的相乘……同样的让A组绝对值最大的负数和B组绝对值最大的负数相乘…

综合布线系统入门及应用(二)

一、工程材料用量估计 1、信息模块及水晶头用量统计 信息插座与工位数量1:1&#xff0c;在增加5%的余量 跳线&#xff1a;一般需要2条&#xff0c;工位信息面板到设备&#xff0c;交换机到配线架&#xff0c;每根条线2个水晶头&#xff0c;预留10%-15%。 2、线槽用量统计 根据办…

如何查看服务器有多少网站--免费工具

一台虚拟主机上到底有多少个网站或者说同一ip下有多少个域名和网站&#xff1f;这是站长们都很关心的&#xff0c;因为这样可以知道你的站到底和谁是邻居&#xff0c;有时候如果你和百度黑名单上的垃圾站在同一空间下&#xff0c;你也会受到牵连。 那么怎 ...中间左侧广告 一台…

二分法在算法题中的4种常见应用(cont.)

目录 1.查找单调序列中是否存在满足某条件的元素 2.寻找序列中第一个(最后一个)满足某条件的元素的位置 3.给定一个定义在[L,R]上的单调函数f(x)&#xff0c;求方程f(x)0的根 4.快速幂的递归和迭代求法 1.查找单调序列中是否存在满足某条件的元素 //二分区间为左闭右闭的[l…

Minimum Path Sum

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path. Note: You can only move either down or right at any point in time. 格子取数问题&#xff0c;另dp[i][j]表示走…

Thorntail 2.2.0提供从WildFly Swarm自动迁移的特性

自6月底宣布把WildFly Swarm2018.5.0改名为Thorntail2.0.0以来&#xff0c;Red Hat在8月中旬以后的三个周里发布了Thorntail 2.1.0版本和2.2.0版本。除了许多Bug修复外&#xff0c;尤其是和MicroProfile相关的&#xff0c;新特性还包括&#xff1a;\\符合MicroProfile 1.3\通过…

Depth Bias

在dx中的depth bias要以如下形式调用 inline DWORD F2DW( float f ) { return *((DWORD*)&f); } m_pD3DDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, F2DW(1)); m_pD3DDevice->SetRenderState(D3DRS_DEPTHBIAS, F2DW(0.001)); 总之&#xff0c;奇怪的api。转载…

(C++)用upper_bound函数取代自己写的二分查找

int a[maxn];int j upper_bound(ai1,an,(long long)a[i]*p)-a; 以上代码的作用是 在a[i1]~a[n-1]找到第一个大于a[i]*p的数&#xff0c;将其下标返回给j 注意&#xff1a; 1.函数是左闭右开的 2.末尾要减去数组的坐标a 3.不加long long强制类型转换可能丢分

gsoap使用总结

WebService、soap、gsoap基本概念 WebService服务基本概念&#xff1a;就是一个应用程序&#xff0c;它向外界暴露出一个可以通过web进行调用的API&#xff0c;是分布式的服务组件。本质上就是要以标准的形式实现企业内外各个不同服务系统之间的互调和集成。 soap概念&#xff…

SQL时间相关 - SQL日期,时间比较

SQL Server 中时间比较 例子: select count(*) from table where DATEDIFF ([second], 2004-09-18 00:00:18, 2004-09-18 00:00:19) > 0 说明 select DATEDIFF(day, time1 , time2) 对应示例语句如下 select DATEDIFF(day, 2010-07-23 0:41:18, 2010-07-23 23:41:18) …

SQL Server 2008 的CDC功能

CDC(Change Data Capture)通过对事务日志的异步读取&#xff0c;记录DML操作的发生时间、类型和实际影响的数据变化&#xff0c;然后将这些数据记录到启用CDC时自动创建的表中。通过cdc相关的存储过程&#xff0c;可以获取详细的数据变化情况。由于数据变化是异步读取的&#x…

1010 Radix

目录 总结 解题过程 总结 1. 短小精悍的一道二分算法题&#xff0c;总体思路是&#xff0c;将字符串1(如果tag不是1将两个字符串调换一下即可)转化为10进制&#xff0c;再用二分法看能否找到另一个进制使得两个字符串的10进制数相等。 2. 本题的三个函数关系是binarySearch…

喜闻乐见的const int *p、int* const p、const int* const p

不废话直接代码示例&#xff1a; 1 void f(const int *p) {2 3 int b 10;4 5 *p 10; // error6 7 p &b; // fine8 9 } 10 11 void f(int* const p) { 12 13 int b 10; 14 15 *p 10; // fine 16 17 p &b; // error 18 19 } 20 21 v…

Microsoft Visual Studio 2012 添加实体数据模型

Microsoft Visual Studio 2012 添加实体数据模型 1、创建一个web项目 2、添加ADO实体数据模型&#xff0c;如下图&#xff1a; 3、选择 从数据库生成&#xff0c;然后下一步 4、新建连接&#xff0c;如下图&#xff1a; 5、填写服务器名等&#xff0c;如下图&#xff1a; 6、选…

5.1软件升级的小阳春

现在正在去白山的车上&#xff0c;刚睡醒。习惯性的拿出手机上网&#xff0c;UCWEB提醒有最新版本升级&#xff0c;使用尚邮接收邮件的时候同样提醒有信版本升级。 公司产品9.0也正式完成&#xff0c;昨天整个小组的同事开始在领地咖啡馆&#xff0c;进行新需求的确认。 4月末5…

1030 完美数列(二分解法)

1. 将整型序列从小到大排序后&#xff0c;这道题的本质是&#xff0c;对于每一个元素i&#xff0c;找出最后一个满足p*A[i]>A[j]的元素j&#xff0c;可以转化为找出第一个不满足p*A[i]>A[j]也即p*A[i]<A[j]的元素j。再用j-1。 2.LL product (LL)p*A[i];这里后面两个…

javascript变量声明 及作用域

javascript变量声明提升(hoisting) http://openwares.net/js/javascript_declaration_hoisting.html 可能要FQ一下 javascript的变量声明具有hoisting机制&#xff0c;JavaScript引擎在执行的时候&#xff0c;会把所有变量的声明都提升到当前作用域的最前面。 先看一段代码 123…