过程调用按地址传递、按值传递参数区别


题:要求采取过程调用使得输入的两个数值颠倒顺序。
过程调用参数传递有两种方法,一是按地址传递,二是按值传递,下面通过对两个代码反汇编来比较两种方法的区别。


按地址传递参数 代码

#include//按地址传递参数 
main()
{
	int a=15,b=22;
	printf("a=%d\tb=%d\n",a,b);
	swap(&a,&b);
	printf("a=%d\tb=%d\n",a,b);		
} 
swap(int*x,int*y)
{
	int t=*x;
	 *x=*y;
	 *y=t;
}

输出

15 22
22 15

按值传递参数 代码

#include//按值传递参数 
main()
{
	int a=15,b=22;
	printf("a=%d\tb=%d\n",a,b);
	swap(a,b);
	printf("a=%d\tb=%d\n",a,b);		
} 
swap(int x,int y)
{
	int t=x;
	 x=y;
	 y=t;
}

输出

15 22
15 22

两个代码看似没有问题但按参数传递值的调用方法无法实现程序。下面进行查看汇编指令以找出原因。


反汇编操作流程:

分别在main函数和swap函数处设置断点,进入调试,在控制台内输入“disassemble”得到相应的汇编指令。


按地址传递参数main函数汇编指令

//按地址传递参数main函数汇编指令
push   %ebp
mov    %esp,%ebp
sub    $0x18,%esp
and    $0xfffffff0,%esp
mov    $0x0,%eax
add    $0xf,%eax
add    $0xf,%eax
shr    $0x4,%eax
shl    $0x4,%eax
mov    %eax,-0xc(%ebp)
mov    -0xc(%ebp),%eax
call   0x4018a4 <_alloca>
call   0x401424 <__main>
movl   $0xf,-0x4(%ebp)
movl   $0x16,-0x8(%ebp)
mov    -0x8(%ebp),%eax//
mov    %eax,0x8(%esp)
mov    -0x4(%ebp),%eax
mov    %eax,0x4(%esp)
movl   $0x403000,(%esp)
call   0x401904 
lea    -0x8(%ebp),%eax//EBP-8里面的内容以地址'&a'形式放入EAX内
mov    %eax,0x4(%esp)//将EAX内的地址送入ESP+4处,ESP+4里的内容变为了'&a'即a的地址
lea    -0x4(%ebp),%eax//EBP-4里面的内容以地址'&b'形式放入EAX内
mov    %eax,(%esp)//将EAX内的地址送入ESP处,ESP里的内容变为了'&b'即b的地址
call   0x401370 //调用swap函数,swap函数里的首地址‘mov    -0x8(%ebp),%eax’送入eip寄存器里去
mov    -0x8(%ebp),%eax
mov    %eax,0x8(%esp)
mov    -0x4(%ebp),%eax
mov    %eax,0x4(%esp)
movl   $0x403000,(%esp)
call   0x401904 
leave  
ret    
//按地址传递参数swap函数汇编指令
push   %ebp//旧的ebp的值压栈,即调用main栈帧中栈底的指针,push完后esp指向栈顶
mov    %esp,%ebp//将esp的值赋给ebp,使得ebp的值也指向栈顶
sub    $0x4,%esp
mov    0x8(%ebp),%eax//ebp+8里的地址'&a'送入eax寄存器内①
mov    (%eax),%eax//eax寄存器里的地址'&a'所对应的值15送入eax寄存器②
mov    %eax,-0x4(%ebp)//eax的值15送入ebp-4内③
mov    0x8(%ebp),%edx//ebp+8里的地址'&b'送入edx寄存器内④
mov    0xc(%ebp),%eax//ebp+12里的地址'&b'送入eax内⑤
mov    (%eax),%eax//eax寄存器里的地址'&b'所对应的值22送入eax寄存器⑥	
mov    %eax,(%edx)//eax的值22赋给edx所对应的地址的值,即a由初始的15变为22⑦
mov    0xc(%ebp),%edx//ebp+12里的地址'&b'送入edx内⑧
mov    -0x4(%ebp),%eax//ebp-4里的值15送入eax⑨
mov    %eax,(%edx)//将eax里的值赋给edx所对应的地址的值,即b由初始的22变为15⑩
leave  
ret    

过程调用按地址传递、按值传递参数区别_第1张图片


按值传递参数main函数汇编指令

//按值传递参数main函数汇编指令
push   %ebp
mov    %esp,%ebp
sub    $0x18,%esp
and    $0xfffffff0,%esp
mov    $0x0,%eax
add    $0xf,%eax
add    $0xf,%eax
shr    $0x4,%eax
shl    $0x4,%eax
mov    %eax,-0xc(%ebp)
mov    -0xc(%ebp),%eax
call   0x40189c <_alloca>
call   0x40141c <__main>
movl   $0xf,-0x4(%ebp)//将15送入ebp-4寄存器内
movl   $0x16,-0x8(%ebp)//将22送入ebp-8寄存器内
mov    -0x8(%ebp),%eax//将ebp-8寄存器内的值22送入eax寄存器内
mov    %eax,0x8(%esp)//将eax寄存器内的值22送入esp+8寄存器内
mov    -0x4(%ebp),%eax//将ebp-4寄存器内的值15送入eax寄存器内
mov    %eax,0x4(%esp)//将eax寄存器内的值15送入esp+4寄存器内
movl   $0x403000,(%esp)
call   0x4018fc 
mov    -0x8(%ebp),%eax
mov    %eax,0x4(%esp)
mov    -0x4(%ebp),%eax
mov    %eax,(%esp)
call   0x401370 
mov    -0x8(%ebp),%eax
mov    %eax,0x8(%esp)
mov    -0x4(%ebp),%eax
mov    %eax,0x4(%esp)
movl   $0x403000,(%esp)
call   0x4018fc 
leave  
ret    

//按值传递参数swap函数汇编指令
push   %ebp
mov    %esp,%ebp
sub    $0x4,%esp
mov    0x8(%ebp),%eax//将ebp+8寄存器内的值15送入eax寄存器内①
mov    %eax,-0x4(%ebp)//将eax寄存器内的值15送入ebp-4寄存器内②
mov    0xc(%ebp),%eax//将ebp+12寄存器内的值22送入eax寄存器内③
mov    %eax,0x8(%ebp)//将eax寄存器内的值22送入ebp+8寄存器内④
mov    -0x4(%ebp),%eax//将ebp-4寄存器内的值15送入eax寄存器内⑤
mov    %eax,0xc(%ebp)//将eax寄存器内的值15送入ebp寄存器内⑥
leave  
ret    

过程调用按地址传递、按值传递参数区别_第2张图片


总结

我们可以比较发现按参数传递参数时,运行swap函数将改变主函数里的值,而我们按值传递参数时改变的ESP及ESP+4值的顺序,故不能实现目的。

你可能感兴趣的:(计算机系统与基础)