计算机基础9

83 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第35天,点击查看活动详情

C语言语句的机器级表示

过程调用的机器级表示

int add(int x,int y){

return x + y;

}

int main(){

int t1 = 125;

int t2 = 80;

int sum =add(t1,t2);

return sum;

}

3个问题: 以下过程(函数)调用对应的机器级代码是什么?

如何将t1(125)、t2(80)分别传递给add中的形式参数x,y

add函数执行的结果如何返回给caller ( main ) ?

过程调用的执行步骤(P为调用者,Q为被调用者)

( 1)P将入口参数(实参)放到Q能访问到的地方;

(2 )P保存返回地址,然后将控制转移到Q;

( 3 )Q保存P的现场,并为自己的非静态局部变量分配空间;

(4)执行Q的过程体(函数体)

(5)Q恢复P的现场,释放局部变量空间

(6 )Q取出返回地址,将控制转移到P。

揭秘:

现场:通用寄存器的内容

为何要保护现场?因为所有过程共享一套通用寄存器

想象:妈妈和你做菜时共用一套盘子的情况。

IA-32的寄存器使用约定

  • 调用者P保存寄存器:EAX、EDX、ECX: Q可直接使用这三个寄存器 若P在从Q返回后还要用的话,P应在转到Q之前先保存,并在从Q返回后先恢复它们的值再使用。
  • 被调用者Q保存寄存器:EBX、ESI、EDI Q必须先将它们的值保存到栈中再使用它们,并在返回P之前恢复它们的值。
  • EBP和ESP分别是帧指针寄存器和栈指针寄存器,分别用来指向当前栈帧的底部和顶部。

根据上面的给出的约定思考一下,为了减少准备和结束阶段的开销,每个过程应先使用哪些寄存器?

EAX,ECX,EDX.

因为Q调用时不用保存,简化了步骤

过程调用过程中栈和栈帧的变化(Q为调用过程)

image.png int add(int x,int y){

return x + y;

}

int caller(){

int t1 = 125;

int t2 = 80;

int sum =add(t1,t2);

return sum;

}

一个C过程的大致结构如下:
  • 准备阶段

    • 形成帧底:push指令和mov指令
    • 形成栈帧(如果需要的话):sub指令或and指令
    • 保存现场(如果有被调用者保存寄存器):mov指令
  • 过程(函数)体

    • 分配局部变量空间,并赋值

    • 具体处理逻辑,如果遇到函数调用时

      • 准备参数:将实参送栈帧入口参数处
      • CALL指令:保存返回地址并转被调用函数
    • 在EAX中准备返回参数

  • 结束阶段

退栈:leave指令或pop指令

取返回地址返回:ret指令

过程调用参数传递举例

按地址传递参数:此时,a和b的值会调换

按值传递参数:此时,a和b的值不会调换。因为交换的是入口参数的位置,而不是交换局部变量所在的位置