inc si指令的作用_C|函数调用、汇编指令、栈空间及操作

函数是任何编程语言不可缺少的语法机制,函数通常包括函数声明、定义、调用。调用及多层嵌套调用时会有一个回溯的问题,也就是回到原来函数调用的位置,如何实现呢,使用栈的后进先出机制。类似于你去一个地方,经过了n个叉路口,你在每个叉路口用一个信封标识你选择的路线,叠在一起,返回时,依次从上面取出信封,按信封标识的信息即可回到起始点。

我们知道,计算机识别的是0、1串的机器语言表示的CPU指令集,0、1串抽象为字符版就是汇编指令及其语法机制,由汇编器翻译。汇编指令的进一步抽象就是高级语言指令及语法机制,由编译器或解释器翻译。

了解高级语言的函数调用,对应的汇编指令、栈空间及操作,可以更好地理解程序的运行机制。

我们知道,数据及指令的存储在计算机系统概念中是一个分层的体系结构,由内到外依次是C寄存器(晶体管实现的循环电路)、CPU缓存(SRAM,晶体管实现的双稳态电路)、内存(DRAM,电容存储,需周期刷新充电)、硬盘缓存、硬盘(机械、磁存储)等外部存储,各层离CPU由近到远,速度由快到慢。

一、常用寄存器

EAX(accumulator):通用寄存器。相对其他寄存器,在进行运算方面比较常用。在保护模式中,也可以作为内存偏移指针(此时,DS作为段寄存器或选择器)

EBX(base):通用寄存器。通常作为内存偏移指针使用(相对于EAX、ECX、EDX),DS是默认的段寄存器或选择器。在保护模式中,同样可以起这个作用。

ECX(count):通用寄存器。通常用于特定指令的计数。在保护模式中,也可以作为内存偏移指针(此时,DS作为寄存器或段选择器)。

EDX(data):通用寄存器。在某些运算中作为EAX的溢出寄存器(例如乘、除)。

SI(Source Index):源变址寄存器 ,通常在内存操作指令中作为“源地址指针”使用。当然,ESI可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。

EDI(Destination Index):通常在内存操作指令中作为“目的地址指针”使用。当然,EDI也可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。ES是默认段寄存器或选择器。

EBP(Base Pointer)和ESP(Stack Pointer):作为指针的寄存器,也可作为16位寄存器BP, SP使用,常用于椎栈操作。通常,它被高级语言编译器用以建造‘堆栈帧'来保存函数或过程的局部变量。

EIP是一个32位宽的寄存器,同CS一同指向即将执行的那条指令的地址,存放指令的偏移地址。微处理器工作于实模式下,EIP是IP(16位,Instruction Pointer,指令指针寄存器 )寄存器。不能够直接修改这个寄存器的值,修改它的方法是跳转或分支指令。(CS(Code Segment)代码段寄存器 )

二、汇编语言命令

1、mov——传送指令

定义:把一个字节、字或双字的操作数从源位置传送到目的位置,可以实现立即数到通用寄存器或主存的传送,通用寄存器与通用寄存器、主存或段寄存器之间的传送,主存与段寄存器之间的传送。

举例:mv ebp, esp

解释:相当于C语言中赋值语句=,ebp=esp

2、push——进栈指令

定义:进栈指令push先将ESP减小作为当前栈顶,然后可以将立即数、通用寄存器和段寄存器或存储器操作数传送到当前栈顶。

格式:push src

举例:push ebp

解释:相当于C语言中esp+=4, *esp=ebp

作用:为ebp当前存放的地址,在栈顶开辟空间存入它,用作调用子函数时的现场保护

3、pop——出栈指令

定义:与入栈指令相反,它先将栈顶的数据传送到通用寄存器、存储单元或段寄存器中,然后ESP增加作为当前栈顶。

格式:pop src

举例:pop ebp

解释:相当于C语言中 ebp=*esp, esp+=4

作用:调用子函数结束后,恢复主函数的ebp

4、add——加法指令

格式:add dest, src

解释:相当于dest+=src

5、sub——减法指令

格式:sub dest, src

解释:相当于dest-=src

6、call——函数调用指令

格式:call 函数名

作用:(1)将程序当前执行的位置IP压入堆栈中;(2)转移到调用的子程序。

三、函数调用实例

#include using namespace std;int d=11;int add(int a, int b) {int c=a+b; return c;}int main() { int x=10; int y=20; int z=add(x,y)+d; return 0; }
9c89252045d7a9b7b342a148eb398d13.png

四、函数调用实例与汇编代码

1: #include 2: using namespace std;3: int d=11;4: int add(int a, int b)5: {00401250 push ebp//栈底指针寄存器ebp入栈00401251 mov ebp,esp// ebp=esp(栈(顶)指针寄存器)00401253 sub esp,44h// esp-=44h00401256 push ebx// "基地址"(base)寄存器00401257 push esi//源通用寄存器00401258 push edi//目的通用寄存器00401259 lea edi,[ebp-44h]//把ebp-44h加载到edi中,目的是保存局部变量的区域0040125C mov ecx,11h 00401261 mov eax,0CCCCCCCCh //0xCCCCCCCC代表着未被初始化00401266 rep stos dword ptr [edi]//rep是重复其上面的指令,ECX是重复的次数//STOS指令将eax中的值拷贝到ES:EDI指向的地址6: int c=a+b;00401268 mov eax,dword ptr [ebp+8] //是指将ebp偏移8个字节的指针对应的4个字节赋给eax0040126B add eax,dword ptr [ebp+0Ch]0040126E mov dword ptr [ebp-4],eax7: return c;00401271 mov eax,dword ptr [ebp-4]8: }00401274 pop edi00401275 pop esi00401276 pop ebx00401277 mov esp,ebp00401279 pop ebp0040127A ret--- No source file ------------------------------------------------------------------------------0040127B int 3……--- F:WebsiteotesVC语法estest.cpp -------------------------------------------------------9:10: int main()11: {00401290 push ebp00401291 mov ebp,esp00401293 sub esp,4Ch00401296 push ebx00401297 push esi00401298 push edi00401299 lea edi,[ebp-4Ch]0040129C mov ecx,13h004012A1 mov eax,0CCCCCCCCh004012A6 rep stos dword ptr [edi]12: int x=10;004012A8 mov dword ptr [ebp-4],0Ah13: int y=20;004012AF mov dword ptr [ebp-8],14h14: int z=add(x,y)+d;004012B6 mov eax,dword ptr [ebp-8]004012B9 push eax004012BA mov ecx,dword ptr [ebp-4]004012BD push ecx004012BE call @ILT+0(add) (00401005)004012C3 add esp,8004012C6 add eax,dword ptr [d (00434dc0)]004012CC mov dword ptr [ebp-0Ch],eax15: return 0;004012CF xor eax,eax16: }004012D1 pop edi004012D2 pop esi004012D3 pop ebx004012D4 add esp,4Ch004012D7 cmp ebp,esp004012D9 call __chkesp (004081b0)004012DE mov esp,ebp004012E0 pop ebp004012E1 ret

-End-

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
```c #include <stdio.h> #include "do_loop.c" int main() { short x, y, k; printf("Please enter x, y, k:\n"); scanf("%hd %hd %hd", &x, &y, &k); short result = do_loop(x, y, k); printf("Result is %hd\n", result); return 0; } ``` 使用命令行编译: ``` gcc -O1 -g do_loop.c main.c -o main ``` 运行程序: ``` ./main Please enter x, y, k: 2 4000 3 Result is 0 ``` 进入gdb调试模式,设置断点: ``` gdb main (gdb) break do_loop (gdb) run Please enter x, y, k: 2 4000 3 Breakpoint 1, do_loop (x=2, y=4000, k=3) at do_loop.c:2 2 do { ``` 进入TUI模式,显示汇编代码和寄存器: ``` (gdb) layout asm (gdb) layout regs ``` 单步执行,查看寄存器变化: ``` (gdb) si 0x00005555555551a8 in do_loop (x=2, y=4000, k=3) at do_loop.c:2 2 do { (gdb) print $edx $1 = 0x0 (gdb) si 0x00005555555551b5 in do_loop (x=2, y=4000, k=3) at do_loop.c:3 3 x*=(y%k) ; k--; (gdb) print $edx $2 = 0x0 (gdb) si 0x00005555555551c2 in do_loop (x=2, y=4000, k=3) at do_loop.c:4 4 } while ((k>0) && (y>k)); (gdb) print $edx $3 = 0x0 (gdb) si 0x00005555555551d2 in do_loop (x=0, y=4000, k=-1) at do_loop.c:5 5 return x; (gdb) print $edx $4 = 0x0 ``` 回答问题: 1. a) %edx 的值为 0。 b) 执行完 cltd 指令后,%edx 的值变成了 0xFFFF,即-1的补码。 c) cltd 指令作用是将%eax中的有符号数符号扩展到%edx中,对于正数,%edx中的所有位被清零,对于负数,%edx中的所有位被置为1。 d) 执行%idiv指令后,除法的商存储在%eax中,余数存储在%edx中。由于在本题中,%eax 中的值一直为0,所以执行%idiv指令后,%edx中的值不变,仍为0。 2. 执行与第一部分相同的步骤,得到的结果为: a) %edx 的值为 0。 b) 执行完 cltd 指令后,%edx 的值变成了 0xFFFF,即-1的补码。 c) cltd 指令作用是将%eax中的有符号数符号扩展到%edx中,对于正数,%edx中的所有位被清零,对于负数,%edx中的所有位被置为1。 d) 执行%idiv指令后,除法的商存储在%eax中,余数存储在%edx中。由于在本题中,%eax 中的值一直为0,所以执行%idiv指令后,%edx中的值不变,仍为0。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值