题:要求采取过程调用使得输入的两个数值颠倒顺序。
过程调用参数传递有两种方法,一是按地址传递,二是按值传递,下面通过对两个代码反汇编来比较两种方法的区别。
#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函数汇编指令
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
//按值传递参数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
我们可以比较发现按参数传递参数时,运行swap函数将改变主函数里的值,而我们按值传递参数时改变的ESP及ESP+4值的顺序,故不能实现目的。