能力值:
( LV2,RANK:10 )
2 楼
m的值是多少就是多少,调试下不就知道了。有的书引错了,也可以作者在吹牛。或者他是自己写了个有BUG的编译噐。编译出来的产品
能力值:
( LV4,RANK:50 )
3 楼
偶试了一下确实如此,很诡异的说
能力值:
( LV2,RANK:10 )
4 楼
我也试了ARM-GCC编译器。没错是输出12345。很奇怪。 只能说编译器存在问题
能力值:
( LV12,RANK:779 )
5 楼
未初始化的变量。
VC6:
Debug:
-858993460
Press any key to continue
Release:
4199033
Press any key to continue
能力值:
( LV2,RANK:10 )
6 楼
谢谢各位测试,我这里用Radasm编译(实际上也就是VC6),得到的是m的值,但是钱能的书上是掷地有声地说:一定返回12345
然后讲这是个栈的原因。
可以就算是讲述栈,每调用一个函数,就会建立一个自已的栈空间,就会有保护返回值,返回地址,函数的局部变量
……
钱能的书出了这么久,难道就没有人试验一下,会有这么个解释不清的问题?
能力值:
( 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;
}
能力值:
(RANK:450 )
8 楼
这个问题的确是编译器优化导致的,
早期的编译器,都是把局部变量都放在堆栈中了,对esp指针的移动也是通过sub指令来操作的,因此就一定会输出12345
由于对内存操作的缓慢,现在编译器会把一些局部变量放到寄存器中,就会导致这种现象
还有就是移动esp指针的问题,现在对函数内部只有一两个变量的情况,也不用sub指令了,而是直接用push reg指令了,这样可以减少代码长度,sub指令至少要3个字节,而push reg只需一个字节,不过这种替换虽然可以减小程序体积,但是降低了程序效率,push reg指令要直接操作内存,慢,花费时钟周期多...
能力值:
( LV9,RANK:850 )
9 楼
解释得很好,赞
能力值:
( LV2,RANK:10 )
10 楼
懂了!呵呵!还没有试过把自己编写的程序反汇编看看...
学习老!