b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

对一部分C语言和汇编语言的对应分析,揭示了函数调用的本质

电脑杂谈  发布时间:2020-04-04 05:00:59  来源:网络整理

c语言对应汇编语句_c语言嵌入汇编_汇编语言和c语言区别

最近,网易云课程开设了一个名为的课程. 我一直对操作系统和计算机的性质很感兴趣,所以我进去看了看. 第一课,老师请学生写一个关于第一课的博客作为作业. 作者对这种新颖的作品形式感到非常惊讶. 好吧,作为一项任务,让我们完成它,只需消化一下即可. 本文将根据要求将一段C语言代码编译为汇编,并进行分析和自己的思考.

首先,列出将涉及的一些CPU寄存器和汇编的基本知识:

准备一段C代码:

int g(int x)
{
  return x+5;
}
int f(int x)
{
  return g(x);
}
int main(void)
{
  return f(10)+1;
}

使用实验性建筑环境

使用以下命令编译以上c代码

gcc -S -o main.s main.c -m32

删除不重要的部分后,您将得到:

c语言对应汇编语句_c语言嵌入汇编_汇编语言和c语言区别

汇编代码的结果是:

g:
  pushl   %ebp
  movl	%esp, %ebp
  movl	8(%ebp), %eax
  addl	$5, %eax
  popl	%ebp
  ret
f:
  pushl   %ebp
  movl	%esp, %ebp
  subl	$4, %esp
  movl	8(%ebp), %eax
  movl	%eax, (%esp)
  call	g
  leave
  ret
main:
  pushl   %ebp
  movl	%esp, %ebp
  subl	$4, %esp
  movl	$10, (%esp)
  call	f
  addl	$1, %eax
  leave
  ret

具体的分步分析保存在这里,老师已详细解释了. 这里的重点是思考和总结.

首先,我们看到3个C函数相应地生成了3个汇编代码部分,这些部分以函数名作为标签分开

int g(int x) -> g:
int f(int x) -> f:
int main(void) -> main:

我们知道程序是从main函数执行的,因此当程序被加载并运行时,以上的汇编代码将被加载到内存的某个区域中. 此外,CPU中的许多寄存器将被初始化. 当然,最重要的一个是eip,因为eip指的是将执行下一条命令的内存地址,因此此时的eip应该指向主标签下的pushl%ebp:

main:
eip ->  pushl %ebp

程序开始执行...

让我们一起观看,首先看看这两个:

pushl   %ebp
movl    %esp, %ebp

汇编语言和c语言区别_c语言嵌入汇编_c语言对应汇编语句

再次查看整个代码,您是否发现不仅这两个指令是主函数,而且函数f和g的开头也是如此. 对其进行分析,不难发现这两条指令涉及将当前堆栈基地址压入堆栈并将基地址重新定位到堆栈顶部. 这实际上意味着保存当前的基地址并重新启动新的堆栈. 由于函数可以调用函数,因此此处的当前基址实际上是前一个函数的堆栈基址. 例如c语言对应汇编语句,f函数中的两条指令实际上保存了main函数的堆栈基地址.

接下来,分析两个句子:

subl    $4, %esp
movl    $10, (%esp)

与C代码相比,不难发现这是将参数压入堆栈,并且立即数保存到堆栈的顶部(esp指向的内存地址是堆栈的顶部). 堆栈). 在f函数中可以找到类似的语句:

subl    $4, %esp
movl    8(%ebp), %eax
movl    %eax, (%esp)

因此,我们可以得出结论,在调用该函数之前,我们需要将参数一个接一个地推入,并且根据作者的测试,堆叠的顺序是从右到左.

接下来调用该调用指令,跳转到f函数,我们知道该调用指令等效于以下伪代码:

pushl %eip+1
movl %eip f

也就是说,在将调用指令的下一条指令压入堆栈后,会将eip分配给目标函数的第一条指令的地址. 这很明显: 被调用函数结束时,您需要返回到当前函数以继续执行,因此必须保存下一条指令,否则返回时将找不到它.

使用f函数时,首先需要保存main函数的堆栈基地址,然后需要调用g函数,因此需要高级堆栈参数:

c语言嵌入汇编_汇编语言和c语言区别_c语言对应汇编语句

subl    $4, %esp
movl    8(%ebp), %eax
movl    %eax, (%esp)

在这里,我们要关注f函数如何获取主函数传递的参数,

movl    8(%ebp), %eax

为什么从8(%ebp)获得参数?我们知道8(%ebp)意味着8个字节基于ebp追溯到堆栈的底部. 为什么是8个字节?

回想一下,在主函数中c语言对应汇编语句,将参数压入堆栈后,完成了两件事:

由于调用f指令的功能,调用f的下一条指令的地址被压入堆栈. 当一个字节的占用率进入f函数后,主函数的堆栈基地址立即被压入堆栈,而ebp则向栈顶倾斜. esp占据堆栈的顶部,

然后,上一个函数的第一个整数参数的值可以找到8(%ebp).

一张图片告诉你发生了什么事

我已经看到了输入一个函数并调用一个函数的过程,然后看看该函数如何退出. 观察main和f,不难发现以下命令用于退出函数

汇编语言和c语言区别_c语言嵌入汇编_c语言对应汇编语句

leave
ret

leave命令等效于以下命令:

movl    %ebp, %esp
popl    %ebp

然后ret是等效的,恢复指令指向:

popl %eip

为什么g函数不离开?由于g函数没有任何变量声明,并且函数调用堆栈始终为空,因此编译器会优化指令

最后,通过此示例,总结函数调用的过程:

输入功能:

当前堆栈基地址被压入堆栈(当前堆栈基地址实际上是上一个函数的堆栈基地址)

调用其他功能:


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-163245-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...