能力值:
( LV12,RANK:650 )
2 楼
Thinking in ASM
1.关于C中的函数调用(call by value)
值传递,一个太老的话题了,我只不过是把多数编程书上讲的用汇编语言展开了,希望您能耐着性子看下去。
先看一个例子:
#include <stdio.h>
void swap(int a,int b)
{
int temp;
temp=a;
a=b;
b=temp;
}
int main()
{
int a,b;
a=1; b=2;
swap(a,b); /* 交换了吗? */
printf("a=%d b=%d\n",a,b);
return 0;
}
我们设计了一个函数swap(a,b),希望它能对给定的两数交换次序,但实际输出为"a=1 b=2",显然没有成功。刚学编程的初学者可能被这个问题困扰,为什么在函数中对参数的改变不会影响到原参数的实际值?
下面我们用汇编来看一下。(注:这里我用的是VC6,在调试时用Disassembly[Alt+8]得到的。如果用默认的Debug设置,产生的多余代码太多,但Release设置又不含调试信息,只好自己定义了一种方式,把优化方案改为Default,就可以带上调试信息了,代码也与Release版的差别不大。如果哪位有更好的方法请告诉我。)
1: #include <stdio.h>
2:
3: void swap(int a,int b)
4: {
00401000 push ebp ;把当前ebp入栈,保留起来
00401001 mov ebp,esp ;esp指向栈顶,这句执行完后下面对变量的访问均由ebp指示
00401003 sub esp,44h ;为函数内局部变量留出空间
00401006 push ebx ;保护现场
00401007 push esi ;保护现场
00401008 push edi ;保护现场
5: int temp;
6: temp=a;
00401009 mov eax,dword ptr [ebp+8] ;[ebp+8]值为第一个参数a
0040100C mov dword ptr [ebp-4],eax ;通过eax把[ebp+8]的值给了局部变量[ebp-4]
7: a=b;
0040100F mov ecx,dword ptr [ebp+0Ch] ;[ebp+c]值为第二个参数b
00401012 mov dword ptr [ebp+8],ecx ;通过ecx把[ebp+c]的值给了[ebp+8]
8: b=temp;
00401015 mov edx,dword ptr [ebp-4] ;[ebp-4]是局部变量,现在值为a
00401018 mov dword ptr [ebp+0Ch],edx ;通过edx把[ebp-4]的值给了[ebp+c]
9: }
0040101B pop edi ;恢复现场
0040101C pop esi ;恢复现场
0040101D pop ebx ;恢复现场
0040101E mov esp,ebp ;恢复esp,这样[ebp-xx]的局部变量不再有效
00401020 pop ebp ;恢复第一行保留的ebp
00401021 ret
10:
11: int main()
12: {
00401030 push ebp
00401031 mov ebp,esp
00401033 sub esp,48h
00401036 push ebx
00401037 push esi
00401038 push edi
13: int a,b;
14: a=1; b=2;
00401039 mov dword ptr [ebp-4],1 ;这是变量a
00401040 mov dword ptr [ebp-8],2 ;这是变量b
15: swap(a,b); /* 交换了吗? */
00401047 mov eax,dword ptr [ebp-8] ;取出b的值
0040104A push eax ;变量b入栈
0040104B mov ecx,dword ptr [ebp-4] ;取出a的值
0040104E push ecx ;变量a入栈
0040104F call swap (00401000) ;调用401000处的swap
00401054 add esp,8 ;函数外平衡堆栈,结果swap函数内[ebp+8][ebp+c]全无效
16: printf("a=%d b=%d\n",a,b);
00401057 mov edx,dword ptr [ebp-8]
0040105A push edx
0040105B mov eax,dword ptr [ebp-4]
0040105E push eax
0040105F push offset string "a=%d b=%d\n" (004060cc)
00401064 call _printf (00401075)
00401069 add esp,0Ch
17: return 0;
0040106C xor eax,eax
18: }
0040106E pop edi
0040106F pop esi
00401070 pop ebx
00401071 mov esp,ebp
00401073 pop ebp
00401074 ret
首先明确一下,esp始终指向栈顶,在C中每个函数内都用ebp指针来指示传给它的参数和它自己的局部变量,不同的函数ebp值不同。在上面例子中swap函数的[ebp+8][ebp+C]是参数a,b,[ebp-4]是局部变量temp,main函数的[ebp-8][ebp-4]是局部变量a,b。
可以看出,我们想交换的变量在main函数的局部变量[ebp-8]和[ebp-4]中,而我们调用swap之前先访问这两个地址取出两个值,然后把这两个值压入堆栈,接着一进函数马上把main函数的ebp保存起来,而用当前esp代替ebp在swap函数内指示变量,我们根本没有机会得到main函数中的ebp是多少,swap只交换了它自己堆栈里两个参数的值,而当返回后平衡堆栈时esp+8,这两个参数地址都无效了。main函数的变量[ebp-8][ebp-4]在整个过程中并没有被改动过,只是值被复制了一份传给函数swap而已,因此当然不会被交换,交换的只是两个临时的复制品。这就是K&R的《The C Programming Language》里所说的call by value,即只传递参数的值,不传递参数的地址。
最后写个BT的解决方案:
void swap(int a,int b)
{
int temp;
temp=a;
a=b;
b=temp;
}
int main()
{
int a,b;
a=1; b=2;
swap(a,b); /* 交换了吗? */
__asm {
MOV EAX,[ESP-8]
MOV a,EAX
MOV EAX,[ESP-4]
MOV b,EAX
}
printf("a=%d b=%d\n",a,b);
return 0;
}
呵呵,开个玩笑啦,这么写程序会让人发疯的。关于如何让函数返回多个值的解释,请关注下文。
第一篇写得有些无聊,因为大家肯定都很熟悉了,以后我好好学习,争取写一些复杂点的,比如C++里的一些特性甚至分析MFC啦……
能力值:
( LV12,RANK:370 )
3 楼
不错的文章...支持...
能力值:
( LV4,RANK:50 )
4 楼
支持ROBA ,收藏起来有时间好好看看。
能力值:
( LV4,RANK:50 )
5 楼
工程->设置->c/c++-> gategory 选择listing file
下面选择assembly with source code
就可以得到一个asm文件 源代码和汇编代码对照
而且这个asm文件可以用ml编译链接。
能力值:
( LV12,RANK:660 )
6 楼
呵呵,支持啊!!!
能力值:
( LV12,RANK:650 )
7 楼
最初由 乱乱 发布 工程->设置->c/c++-> gategory 选择listing file 下面选择assembly with source code 就可以得到一个asm文件 源代码和汇编代码对照 而且这个asm文件可以用ml编译链接。
Thx very much!
btw: 上面最后那个BT程序在release模式下结果错误,因为RELEASE很聪明,看到我那个SWAP其实什么也没干,于是CALL里面直接RET回来了,堆栈中没有相应的数据。
能力值:
(RANK:460 )
8 楼
好文章。
能力值:
(RANK:10 )
9 楼
支持你...
能力值:
( LV2,RANK:10 )
10 楼
a=a^b
b=a^b
a=a^b
上C语言课时老师让我们回去想
隔了两年才看到这个东西
能力值:
( LV2,RANK:10 )
11 楼
没有哪个初学者写个交换函数会是这么写的
能力值:
( LV8,RANK:130 )
12 楼
这个题目真的是很吓人
希望楼主能继续把帖子写下去,想老外的那本 thinking in java/...
能力值:
( LV4,RANK:50 )
13 楼
很不错,支持一下
能力值:
(RANK:300 )
14 楼
最初由 d1y2j3 发布 a=a^b b=a^b a=a^b 上C语言课时老师让我们回去想 ........
我学 C 时第一次看见这几行,我的印象很深,觉得 xor 是很奇妙
这种东西不是万能,是整数才可以,
如果 a b 是 double,那就…
能力值:
( LV9,RANK:410 )
15 楼
不错,期待下篇!希望能有MFC,VB,Dephi等各种语言的相应分析!
能力值:
( LV6,RANK:90 )
16 楼
就是VB下的结果确是正常的
Function swap(a, b As Integer)
Dim temp As Integer
temp = a
a = b
b = temp
End Function
Private Sub Command1_Click()
Dim a, b As Integer
a = 1
b = 2
Call swap(a, b)
Print "a=" & a & " b=" & b
End Sub
能力值:
( LV2,RANK:10 )
17 楼
分析得 很好啊 楼主 支持你
能力值:
( LV13,RANK:460 )
18 楼
交换方法很有个性啊:)
能力值:
( LV6,RANK:90 )
19 楼
最初由 RoBa 发布 void swap(int a,int b) { int temp; temp=a; a=b; b=temp; } int main() { int a,b; a=1; b=2; swap(a,b); /* 交换了吗? */ __asm { MOV EAX,[ESP-8] MOV a,EAX MOV EAX,[ESP-4] MOV b,EAX } printf("a=%d b=%d\n",a,b); return 0; } 用不着这么麻烦
void swap(int &a,int &b)
{
int temp;
temp=a;
a=b;
b=temp;
}
int main()
{
int a,b;
a=1; b=2;
swap(a,b); /* 交换了吗? */
printf("a=%d b=%d\n",a,b);
return 0;
}
传址就可以了
能力值:
(RANK:300 )
20 楼
最初由 larblue 发布 用不着这么麻烦 void swap(int &a,int &b) { int temp; temp=a; a=b; b=temp; } int main() { int a,b; a=1; b=2; swap(a,b); /* 交换了吗? */ printf("a=%d b=%d\n",a,b); return 0; } ........
正确的写法
#include <stdio.h>
void swap(int *a,int *b)
{
int temp;
temp=*a;
*a=*b;
*b=temp;
}
int main()
{
int a,b;
a=1; b=2;
swap(&a,&b);
printf("a=%d b=%d\n",a,b);
return 0;
}
能力值:
( LV12,RANK:650 )
21 楼
楼上两位别着急呀,我留到下次写。
能力值:
(RANK:300 )
22 楼
我当然知道
~~ roba 写那个 bt 方案是讲学用的
我只是修正一下楼上那个C 代码的 pointer 错误用法,以免一些兄弟误学了
能力值:
( LV6,RANK:90 )
23 楼
最初由 riijj 发布 我当然知道 ~~ roba 写那个 bt 方案是讲学用的 我只是修正一下楼上那个C 代码的 pointer 错误用法,以免一些兄弟误学了
呵呵
我的没有错你的也没有错
1、传值和传址:
lippman在说明这个问题的时候用了一个探索的过程,让初学者没有一点障碍的被领进了这个问题。
什么是形参?什么是实参呢?简单的说,编写函数的时候说明的参数就是形参,在调用函数的时候的参数就是实参。
当调用一个函数的时候,会在内存中建立一块特殊的区域,叫程序栈。他提供没个函数参数的储存空间。
在默认情况下,参数都会被复制一份传入程序栈,这就是所谓的传值,架设给一个数组排序,用传值的方式是不会改变原有数列的,这是就要用到传址。在参数前面加一个“&”即可。
什么时候该用到传址?当希望对传入的对象修改时,或者是如果传入参数对象过于庞大,用到传址就会大大提高程序的效率。
当然也可以用指针来传递参数,其实也是一样的,因为指针的本质就是地址。
引自重读《Essential C++ 读书笔记2》 by sssa2000
兄弟可以编译一下试试
能力值:
(RANK:300 )
24 楼
兄弟,我解释一下
Roba 写的是 “ 关于C中的函数调用 “ ,那是 C 代码,不是 C++
你这种写法只是 C++ 正确,在 C 是错误的
能力值:
( LV2,RANK:10 )
25 楼
Roba,前面都是捧你的话,下面我打击你一下,在这里的朋友如果真想知道高级语言为了做了一些什么,你可以把你要分析的主题以高级语言代码形式完成,直接反汇编,自己分析去喽,看得多了,不懂也懂了!
前些天我写过书评《?客反汇编揭密》,全书基本就是这样的写法,说实话,这种写法,给我恶心坏了,没什么实在的东西,当然这也因人而异,我说的并不能代表其他人的感受,总之,其实这是件不难的事情,有心人都可以做到
To Roba:其实我很羡慕你,年纪不大,希望很大,祝你走得更好