首页
社区
课程
招聘
[求助]C++函数调用机制
发表于: 2007-9-11 21:17 7201

[求助]C++函数调用机制

2007-9-11 21:17
7201

#include<iostream.h>
int func1();
int func2();
void main()
{
        func1();
        cout<<func2()<<endl;
}
int func1()
{
        int n=12345;
        return n;
}
int func2()
{
        int m;
        return m;
}

会输出什么呢?
00401000  /$  E8 3E000000   call    00401043                      ;调用func1()
00401005  |.  E8 3F000000   call    00401049                       ;调用func2()
0040100A  |.  50            push    eax                              ; /Arg1
0040100B  |.  B9 B8B94000   mov     ecx, 0040B9B8                    ; |
00401010  |.  E8 D9000000   call    004010EE                         ; \qianleng.004010EE
00401015  |.  68 3A104000   push    0040103A
0040101A  |.  6A 0A         push    0A                               ; /Arg1 = 0000000A
0040101C  |.  8BC8          mov     ecx, eax                         ; |
0040101E  |.  E8 2D000000   call    00401050                         ; \qianleng.00401050
00401023  |.  8BC8          mov     ecx, eax
00401025  |.  E8 01000000   call    0040102B
0040102A  \.  C3            retn
0040102B  /$  56            push    esi
0040102C  |.  8BF1          mov     esi, ecx
0040102E  |.  56            push    esi
0040102F  |.  FF5424 0C     call    dword ptr [esp+C]
00401033  |.  59            pop     ecx
00401034  |.  8BC6          mov     eax, esi
00401036  |.  5E            pop     esi
00401037  \.  C2 0400       retn    4
0040103A   .  8B4C24 04     mov     ecx, dword ptr [esp+4]
0040103E   .  E9 DE020000   jmp     00401321
00401043  /$  B8 39300000   mov     eax, 3039                    ;func1()     0x3039即12345
00401048  \.  C3            retn
00401049  /$  51            push    ecx                                      ;func2()
0040104A  |.  8B4424 00     mov     eax, dword ptr [esp]
0040104E  |.  59            pop     ecx
0040104F  \.  C3            retn

我理解为:一定会输出一个不可确定的数(m的值),但是钱能的书上说会输出:12345
不明白。
期待高人解说。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (9)
雪    币: 161
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
PFC
2
m的值是多少就是多少,调试下不就知道了。有的书引错了,也可以作者在吹牛。或者他是自己写了个有BUG的编译噐。编译出来的产品
2007-9-11 21:28
0
雪    币: 4560
活跃值: (1002)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
偶试了一下确实如此,很诡异的说
2007-9-11 22:39
0
雪    币: 161
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
PFC
4
我也试了ARM-GCC编译器。没错是输出12345。很奇怪。

只能说编译器存在问题
2007-9-11 23:04
0
雪    币: 11705
活跃值: (975)
能力值: ( LV12,RANK:779 )
在线值:
发帖
回帖
粉丝
5
未初始化的变量。

VC6:

Debug:
-858993460
Press any key to continue

Release:
4199033
Press any key to continue
2007-9-12 01:28
0
雪    币: 8
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
谢谢各位测试,我这里用Radasm编译(实际上也就是VC6),得到的是m的值,但是钱能的书上是掷地有声地说:一定返回12345
然后讲这是个栈的原因。
可以就算是讲述栈,每调用一个函数,就会建立一个自已的栈空间,就会有保护返回值,返回地址,函数的局部变量
……
钱能的书出了这么久,难道就没有人试验一下,会有这么个解释不清的问题?
2007-9-12 06:29
0
雪    币: 11705
活跃值: (975)
能力值: ( LV12,RANK:779 )
在线值:
发帖
回帖
粉丝
7
C++已有很多经典著作。没看过钱能的书。这里作者是在臆想妄断了。

1.如果没有优化
func1()返回以后,是有12345在栈里。这时候还没有覆盖。
但是接下来调用fun2(),会有一系列的PUSH,前面的数据已经覆盖。
返回值完全取决于eax赋值前的那个PUSH。

因为没有初始化,所以取决于编译器怎么编译。
VC6是采用ecx的值给它初始化。
如下这样的一种编译, 打印值是ecx的值。

// fun1()
PUSH    EBP                          
MOV     EBP, ESP                     
PUSH    ECX                             
MOV     DWORD PTR [EBP-4], 3039         
MOV     EAX, DWORD PTR [EBP-4]         
MOV     ESP, EBP                        
POP     EBP                             
RETN     
        
int func1()
{
  int n=12345;
  return n;
}

// fun2()                       
PUSH    EBP                     
MOV     EBP, ESP                        
PUSH    ECX                        
MOV     EAX, DWORD PTR [EBP-4]    //EAX的值与刚才ECX的值一致      
MOV     ESP, EBP                        
POP     EBP                             
RETN     

int func2()
{
  int m;
  return m;
}

2.如果优化
编译器会优化这个函数,直接mov 到eax返回,根本不会有12345在栈里。
MOV EAX,3039
RETN

int func1()
{
  int n=12345;
  return n;
}

// 后面还是取决于push
// 这时候 EAX取了栈里的值,自然也不是12345
可以这样:
PUSH   ECX  
MOV    EAX, DWORD PTR [ESP]            
POP     ECX                             
RETN   

也可以这样优化
PUSH   ECX  
MOV    EAX, DWORD PTR [ESP]            
ADD    ESP, 4                             
RETN   

或者这样
SUB    ESP,4
MOV    EAX, DWORD PTR [ESP]            
ADD    ESP, 4                             
RETN  

甚至这样:
MOV   EAX, DWORD PTR [ESP - 4]   
RETN
   
int func2()
{
  int m;
  return m;
}
2007-9-12 12:13
0
雪    币: 1746
活跃值: (287)
能力值: (RANK:450 )
在线值:
发帖
回帖
粉丝
8
这个问题的确是编译器优化导致的,

早期的编译器,都是把局部变量都放在堆栈中了,对esp指针的移动也是通过sub指令来操作的,因此就一定会输出12345

由于对内存操作的缓慢,现在编译器会把一些局部变量放到寄存器中,就会导致这种现象

还有就是移动esp指针的问题,现在对函数内部只有一两个变量的情况,也不用sub指令了,而是直接用push reg指令了,这样可以减少代码长度,sub指令至少要3个字节,而push reg只需一个字节,不过这种替换虽然可以减小程序体积,但是降低了程序效率,push reg指令要直接操作内存,慢,花费时钟周期多...
2007-9-16 14:07
0
雪    币: 2943
活跃值: (1788)
能力值: ( LV9,RANK:850 )
在线值:
发帖
回帖
粉丝
9
解释得很好,赞
2007-9-16 16:53
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
懂了!呵呵!还没有试过把自己编写的程序反汇编看看...
学习老!
2008-3-12 23:33
0
游客
登录 | 注册 方可回帖
返回
//