阅读本笔记之前确保已经了解了CPU指令的概念
寻址包含两种,指令寻址和操作数寻址,相对来说,任何CPU,指令寻址都要比操作数寻址简单,注意,以下讨论中以80x86为例,并且讨论中不包括四地址这种情况
一.指令寻址
1.1顺序寻址
这是最基本的寻址方式,当一条指令执行完毕之后,程序计数器(PC)自动增加,于是就形成了下一条指令的地址,这样CPU就可以顺序的执行下一条指令地址了
1.2跳跃寻址
1.2.1无条件跳跃寻址
C语言中的goto就是跳跃寻址,说白了就是不需要任何条件,直接跳转到其他分支
int main()
{
printf("A");
goto C;
printf("B");
C:
printf("C");
}
这段代码就是执行了跳跃寻址,正常情况下应该打印出“B”的,但是由于goto关键词的存在,所以跳过了B,直接打印出了C,下面是上述C代码的反汇编代码片段,我们可以看到jmp指令就是跳跃指令
000718A3 call @__CheckForDebuggerJustMyCode@4 (071217h)
printf("A\n");
000718A8 push offset string "A\n" (077B30h)
000718AD call _printf (07104Bh)
000718B2 add esp,4
goto C;
000718B5 jmp C (0718C6h) //表示跳转到0718C6地址执行
000718B7 jmp C (0718C6h) //表示跳转到0718C6地址执行
printf("B\n");
000718B9 push offset string "B\n" (077BD0h)
000718BE call _printf (07104Bh)
000718C3 add esp,4
C:
printf("C\n");
// 会跳转到这里执行代码
000718C6 push offset string "C\n" (077BD4h)
000718CB call _printf (07104Bh)
000718D0 add esp,4
1.2.2有条件跳跃寻址
很多高级语言都有if关键字,if就是有条件跳跃寻址,比如下面的代码
int main()
{
int a = 3;
if (a==0) {
a == 1;
}
}
汇编指令如下
int a = 3;
00B917B8 mov dword ptr [a],3
if (a==0) {
00B917BF cmp dword ptr [a],0 // 注意此处cmp操作ZF标志位
00B917C3 jne main+3Ch (0B917CCh)
a = 1;
00B917C5 mov dword ptr [a],1
}
}
上述代码首先,if(a==0)会执行cmp操作,用a减0,将结果存放到ZF标志位中,然后下一条指令去读取ZF标志位,由ZF标志位决定是否跳转,而不是CPU判断a是否等于0,来决定跳转,切记
我们可以简单的理解成,无论a是否==于0,都会进入到if里,至于是否再继续执行,则是CPU去判断ZF标志位来决定的了
下面介绍if,else语句,很有意义,希望仔细阅读
int main()
{
int a = 3;
if (a==0) {
a = 1;
}
else {
a = 2;
}
}
其对应的汇编如下
int a = 3;
00BD17B8 mov dword ptr [a],3
if (a==0) {
00BD17BF cmp dword ptr [a],0
00BD17C3 jne main+3Eh (0BD17CEh)
a = 1;
00BD17C5 mov dword ptr [a],1
}
else {
00BD17CC jmp main+45h (0BD17D5h)
a = 2;
00BD17CE mov dword ptr [a],2
}
0BD17D5
首先执行cmp命令,将比较结果存到ZF,如果CPU判断ZF为1则相等,那么jne指令则无法满足,则不跳转,执行代码a=1,执行完代码a=1之后,你以为不进入else了????其实代码会进入到else的,但是,else里面的第一条指令就是jmp,直接跳转到0BD17D5位置了,也就是说没有执行else里的其他代码
如果CPU根据ZF标志位加上一些其他标志位判断出a不等于0,则触发jne这个跳转命令,跳转到0BD17CE位置,这个位置就是执行了mov操作,也就是代码里的a=2
循环语句也会导致有条件跳跃寻址
还有一个我们大家每天都在用的,函数调用(方法调用),也会导致有条件跳跃寻址,博客里就不写了,内容太多也不爱看,他们的本质都是与if相同的
二.操作数寻址
2.1立即寻址
操作数里面装的内容就是该值本身,比如代码
a=1+2
那么操作数寻址就是add 1 2,当然,add是我臆想出来的指令,不过只是举例,就是说,add后面接的两个操作数,是1和2两个值本身
2.2直接寻址
还是上述代码a=1+2
那么直接寻址就是add 0x1234 0x5678,然后再通过0x1234找到值1,通过0x5678找到值2,之后再相加
2.3间接寻址
还是上述代码a=1+2
简介寻址就是add 0x1234 0x5678,然后通过0x1234找到值0x4444(注意此处是根据地址又找到了一个地址,理论上可以无限往复循环找地址),然后在根据0x4444继续往下找,一直找到真正的值1,这就叫间接寻址
2.4寄存器寻址
只的是操作数里装的是地址,但是这个地址不是主存的地址,而是某个寄存器的地址
寄存器直接寻址:假设有个操作是op a,表示对a进行op操作,那么这a其实是某个寄存器的地址,通过a,在某个寄存器中找到a真正的值
寄存器间接寻址:假设有个操作是op a,表示对a进行op操作,那么这a其实是某个寄存器的地址,通过a,在某个寄存器中找到a在主存中的地址,然后在根据这个主存中的地址,去主存中拿到a真正的值
2.5偏移寻址
对于偏移寻址,此处我不太明白,待以后再研究吧
偏移寻址包括相对寻址,基址寄存器寻址,变址寻址;
相对寻址:经常被用在跳转指令中,跳转后的目标地址与当前地址有一定的距离,这个距离就叫做相对偏移量。
基址寄存器寻址:
变址寻址:
2.6堆栈寻址
现代计算机完成函数调用时所使用的寻址方式
首先计算机必须提供堆栈结构,堆栈结构是现代数据结构中的一个概念,堆栈可以使用寄存器来实现,这叫做硬堆栈,堆栈也可是使用主存来实现,这叫做软堆栈,它是一种先进后出的读数读写顺序模型
代码揭秘P181