c与汇编的故事

本文详细解析了C语言中函数add与main的调用流程,包括参数传递、寄存器使用及栈帧建立等内容。通过汇编指令的解读,揭示了函数内部执行机制与数据传递的具体方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1  int add(int a, int b) {
2       int result;
3
4       result = a + b;
5
6       return result;
7   }
8
9   int main(int argc, char *argv[]) {
10      int a,b,result;
11
12      a = 1;
13      b = 2;
14      result = add(a,b);
15
16      return 0;
17  }

(gdb) disass add
Dump of assembler code for function add:
   0x00000000004004ed <+0>: push   %rbp
   0x00000000004004ee <+1>: mov    %rsp,%rbp
   0x00000000004004f1 <+4>: mov    %edi,-0x14(%rbp)
   0x00000000004004f4 <+7>: mov    %esi,-0x18(%rbp)
   0x00000000004004f7 <+10>:    mov    -0x18(%rbp),%eax
   0x00000000004004fa <+13>:    mov    -0x14(%rbp),%edx
   0x00000000004004fd <+16>:    add    %edx,%eax
   0x00000000004004ff <+18>:    mov    %eax,-0x4(%rbp)
   0x0000000000400502 <+21>:    mov    -0x4(%rbp),%eax
   0x0000000000400505 <+24>:    pop    %rbp
   0x0000000000400506 <+25>:    retq
End of assembler dump.
(gdb) disass main
Dump of assembler code for function main:
   0x0000000000400507 <+0>: push   %rbp    //建立main函数的栈帧
   0x0000000000400508 <+1>: mov    %rsp,%rbp//建立main函数的栈帧
   0x000000000040050b <+4>: sub    $0x20,%rsp  //为main函数分配栈帧空间,此处为32个字节
   0x000000000040050f <+8>: mov    %edi,-0x14(%rbp)//将系统传给main函数的第一个参数复制到自己的栈帧(可以查看值吗?)
   0x0000000000400512 <+11>:    mov    %rsi,-0x20(%rbp)//将系统传给main函数的第二个参数复制到自己的栈帧,,并存放在栈帧的最顶部(可以查看值吗?)      
   0x0000000000400516 <+15>:    movl   $0x1,-0xc(%rbp) //此处为给main函数的局部变量a赋值过程,将该值复制到main函数的栈帧中;使用movl表明这个变量是4个字节的.
   0x000000000040051d <+22>:    movl   $0x2,-0x8(%rbp) //此处为给main函数的局部变量b赋值过程,将该值复制到main函数的栈帧中;可以看出局部变量的地址是从低向高分配的
   0x0000000000400524 <+29>:    mov    -0x8(%rbp),%edx
   0x0000000000400527 <+32>:    mov    -0xc(%rbp),%eax
   0x000000000040052a <+35>:    mov    %edx,%esi
   0x000000000040052c <+37>:    mov    %eax,%edi
   0x000000000040052e <+39>:    callq  0x4004ed <add>
   0x0000000000400533 <+44>:    mov    %eax,-0x4(%rbp)
   0x0000000000400536 <+47>:    mov    $0x0,%eax
   0x000000000040053b <+52>:    leaveq
   0x000000000040053c <+53>:    retq

 result = add(a,b);
  400524:   8b 55 f8                mov    -0x8(%rbp),%edx   //这里把传递给add函数的第二个参数的值复制到寄存器edx
  400527:   8b 45 f4                mov    -0xc(%rbp),%eax   //这里把传递给add函数的第一个参数的值复制到寄存器eax
  40052a:   89 d6                   mov    %edx,%esi   //接着把传递给add函数的第二个参数值从edx寄存器复制到esi寄存器
  40052c:   89 c7                   mov    %eax,%edi   //接着把传递给add函数的第一个参数值从eax寄存器复制到edi寄存器
  40052e:   e8 ba ff ff ff          callq  4004ed <add>   //这里call命令让整个程序的执行流跳转到地址(地址长度是一个字节)0x4004ed(也就是add函数的地址)处,
  400533:   89 45 fc                mov    %eax,-0x4(%rbp)   //(暂停main函数的分析,进而分析add函数;注意这条指令的地址在该函数的上一条指令被执行前,存入eip寄存器了)
寄存器表:


这里实际的情况印证了之前讲到的,rdi,rsi,rdx,rcx,r8,r9这6个寄存器会依次暂存主调函数传给被调函数的参数。而之所以中间还要转存到edx和eax是因为,cpu从寄存器中读取数据的速度远远大于从内存中读取数据的速度;为了提高性能,一旦内存中一个参数被使用,那么先会被暂存到一个空余的寄存器中,以后再使用时,就不用从内存中读取了。
举例:
--- g:\coding\poet\test\ctest.cpp ----------------------------------------------  
#include <iostream>  
using namespace std;  
int main()  
{  
;将ebp入栈,保存ebp,保护原现场  
00D252A0  push        ebp  
;将esp传送给ebp  
00D252A1  mov         ebp,esp  
;esp减去0C0h,开辟栈空间存放局部变量  
;注意汇编语言中数字常量如果是字母开头必须加上0  
00D252A3  sub         esp,0C0h  
;保存常用的寄存器ebx,esi,edi,保护原现场  
00D252A9  push        ebx  
00D252AA  push        esi    
00D252AB  push        edi  
;将edi赋值为ebp-0C0h(lea取得偏移地址)  
00D252AC  lea         edi,[ebp-0C0h]   
;30h(临时栈区大小,4字节为单位)放入ecx,为rep执行次数  
;注意到30h * 4字节 = 0C0h   
00D252B2  mov         ecx,30h    
;0CCCCCCCCh为系统中断int 3h,也是临时栈区初始值  
00D252B7  mov         eax,0CCCCCCCCh    
;用int 3h循环填充临时栈区  
00D252BC  rep stos    dword ptr es:[edi]     
          return 0;  
;等价于mov eax, 0但是xor更高效  
00D252BE  xor         eax,eax    
}  
;恢复寄存器ebx,esi,edi  
00D252C0  pop         edi    
00D252C1  pop         esi    
00D252C2  pop         ebx   
;用ebp恢复函数调用前的栈指针esp   
00D252C3  mov         esp,ebp    
;恢复ebp  
00D252C5  pop         ebp    
;返回   
00D252C6  ret  


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值