汇编基础(二)形参与实参

本文详细解析了形参与实参的区别及在程序中的作用,通过C语言代码和对应的汇编代码实例,展示了函数调用时的数据传递过程。文章首先介绍了形参和实参的基本概念,然后通过分析C语言函数及其实现的汇编代码,揭示了主调函数如何通过实参向被调函数传递数据,最后展示了函数调用的内存布局和栈操作。通过这种方式,读者可以更深入地理解函数调用的底层机制。
摘要由CSDN通过智能技术生成

这篇文章小编写了三个小时,汇编代码分析确实很容易将自己绕进去,但请大家不要放弃,自己分析一遍之后,绝对会收获很多,小编在分析的过程中也遇到一些问题,但是最后也是将其一一解决掉。感觉自己也有很大的收获

一、形参与实参的区别

  • 形参:形参出现在函数定义中,在整个函数体内都可以使用, 离开该函数则不能使用。
  • 实参: 实参出现在主调函数中,进入被调函数后,实参变量也不能使用 。
    注意:实参可以是一个实数,可以是一个变量,也可以是一个表达式

二、形参与实参在程序中的作用

在学习了一年的编程后,无论是C语言的函数还是Java的方法(本质都是一样的)都会大量的使用。所以也就对他们的内部一些关系慢慢有了感悟。下面我就把自己的理解,和自己找的相关资料总结一下,如下所示:

形参和实参的功能是作数据传送。主函数通过实参的传递,从而间接性的将两部分
不同的代码块给联系起来,从而达到既不互相干扰(如果你将变量的地址随意传入,
并对其造成影响发生干扰,这只能说是你在故意的恶搞),但又相辅相成。在汇编
级的代码大家很容易就会发现,主函数的代码块和数据块,与其他函数的代码块数据
块处于不同的地方,但是在call指令的作用下,主函数又与各个函数联系起来,这
就再一次证明我所说的“既不相互干扰,但又相辅相成”。发生函数调用时, 主调
函数通过把实参的值传送给被调函数的形参,从而实现主调函数向被调函数的数据传
送 。因此使各个之独立的功能代码块形成巨大的工程项目。

三、汇编代码与C语言代码结合分析

接下来,让我们先来看一段C语言代码:

#include <stdio.h>

int fun(int count1, int count2);

int fun(int count1, int count2) {

	int sum = count2 + count1;
	
	return sum;
}


int main() {
	int num1 = 1;
	int num2 = 2;

	fun(num1, num2);

	return 0;
}

接下来,在看与之对应的部分汇编代码(对于部分基础指令在上一篇文章里已经介绍过:基础汇编(一))。看不懂也没关系,接下来我将会对每一部分进行解释。

_TEXT	SEGMENT
_count1$ = 8
_count2$ = 12
_sum$ = -4
_fun	PROC NEAR
; File a.c
; Line 5
	push	ebp
	mov	ebp, esp
	push	ecx
; Line 7
	mov	eax, DWORD PTR _count2$[ebp]
	add	eax, DWORD PTR _count1$[ebp]
	mov	DWORD PTR _sum$[ebp], eax
; Line 9
	mov	eax, DWORD PTR _sum$[ebp]
; Line 10
	mov	esp, ebp
	pop	ebp
	ret	0
_fun	ENDP
_TEXT	ENDS
PUBLIC	_main
_TEXT	SEGMENT
_num1$ = -4
_num2$ = -8
_main	PROC NEAR
; Line 13
	push	ebp
	mov	ebp, esp
	sub	esp, 8
; Line 14
	mov	DWORD PTR _num1$[ebp], 1
; Line 15
	mov	DWORD PTR _num2$[ebp], 2
; Line 17
	mov	eax, DWORD PTR _num2$[ebp]
	push	eax
	mov	ecx, DWORD PTR _num1$[ebp]
	push	ecx
	call	_fun
	add	esp, 8
; Line 19
	xor	eax, eax
; Line 20
	mov	esp, ebp
	pop	ebp
	ret	0
_main	ENDP
_TEXT	ENDS
END

C语言执行是从主函数开始,所以我们先从主函数看起,主函数的汇编代码如下;

PUBLIC	_main
_TEXT	SEGMENT
_num1$ = -4
_num2$ = -8
_main	PROC NEAR
; Line 13
	push	ebp
	mov	ebp, esp
	sub	esp, 8
; Line 14
	mov	DWORD PTR _num1$[ebp], 1
; Line 15
	mov	DWORD PTR _num2$[ebp], 2
; Line 17
	mov	eax, DWORD PTR _num2$[ebp]
	push	eax
	mov	ecx, DWORD PTR _num1$[ebp]
	push	ecx
	call	_fun
	add	esp, 8
; Line 19
	xor	eax, eax
; Line 20
	mov	esp, ebp
	pop	ebp
	ret	0
_main	ENDP
_TEXT	ENDS
END

我们先分析一下这两条语句:_num1$ = -4,_num2$ = -8。很明显这是我们声明在主函数里的两个变量,但是-4,-8是干什么的呢?这里小编告诉大家,这个是用来计算存储地址的。具体是怎么算的,让我们看看Line 17这里面的两条语句:_num2$[ebp], _num1$[ebp]。在上篇文章中我们介绍过这个语句里存在一步计算[ebp + _num2$]和[ebp + _num1$],该计算就是为变量num2和num1计算存储地址的语句。我们接着往下看Line13 这部分首先通过push指令将栈底指针ebp原先的值(我们给原先的值起个名字,叫old)保存在栈底,然后再用mov将栈顶指针esp的值赋值给栈底指针ebp,从而使得他们两个都先指向栈底的首地址,形成一个空栈。如下图
在这里插入图片描述
接着通过这条sub esp, 8,给变量num1,num2申请8B空间。为什么通过减法运算(sub)申请空间?大家只需要记住,给栈顶指针esp进行加法(加正数或者减负数)操作指针往栈底方向移动,从而释放栈空间。给栈顶指针esp进行减法(减正或者加负)操作指针往栈顶方向移动,完成申请空间。(看到这里相信大家对上面的-4,-8理解了吧,esp-4就是num1的首地址,同时将值给eax。然后push放进栈里。esp-8就是num2的首地址,同样的给ecx,并且放到栈里)并且每当给给栈里放一个数据,栈顶指针会自动往上移动4B,所以在下图中,当存入num2后esp指向了12B的位置,申请空间后在Line 14,Line 15部分对变量num1和num2进行空间地址计算,同时赋值。Line 17 这一部分就是将实参给形参,并且放入到堆栈里,如下图所示:
在这里插入图片描述

接着调用call _fun会将调用函数语句的下一行代码的地址(也就是返回地址)放入到栈里,以便在函数块执行完后可以正确的找到返回的位置。之后开始执行fun函数的代码:

_TEXT	SEGMENT
_count1$ = 8
_count2$ = 12
_sum$ = -4
_fun	PROC NEAR
; File a.c
; Line 5
	push	ebp
	mov	ebp, esp
	push	ecx
; Line 7
	mov	eax, DWORD PTR _count2$[ebp]
	add	eax, DWORD PTR _count1$[ebp]
	mov	DWORD PTR _sum$[ebp], eax
; Line 9
	mov	eax, DWORD PTR _sum$[ebp]
; Line 10
	mov	esp, ebp
	pop	ebp
	ret	0
_fun	ENDP
_TEXT	ENDS

首先执行Line 5这部分代码,将ebp的值(此时ebp的值是栈底的首地址)放入到到栈里,然后将esp赋值给ebp,ecx入栈。Line 7 就是完成count1 + count2 然后赋值给sum。Line 9就是将返回值存入到eax。如下图所示:
在这里插入图片描述
接下来将ebp赋值给esp 也就是将栈顶指针下移,从而将ecx空间释放。pop ebp将栈底指针再次下移到开始时候的栈底起始位置。在函数执行完之后,栈顶指针下移到12这个位置。此时再次回到主函数,执行add esp, 8。从而将形参变量的空间也释放掉。如下图:
在这里插入图片描述
Line 19就是主函数里的return 0;Line 19部分,首先把ebp赋值给esp,释放掉num1,和num2的变量空间。然后pop ebp 。再次使得ebp的值变成old,回到之前的位置去。程序结束。
由以上推到过程,我们可以知道,形参的空间申请和将实参赋值给形参是在main函数里面完成的
以上就是这段汇编代码的运行过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值