上次说了一个C++对象在内存中的实际形式,现在来说说C++中以对象为参数或返回值的函数是如何实现的。在此之前如果你对函数调用的汇编形式毫无概念的话可以先看看这篇文章:http://www.20cn.net/ns/wz/sys/data/20040208183412.htm
来看下面的代码:
class test //sizeof(test) is 24
{
public:
int m1;
int m2;
int m3;
int m4;
int m5;
int m6;
};
test function1()
{
test cls2;
return cls2; // Line 15
}
int function2(test temp)
{
temp.m1=1; // Line 20
return 0; // Line 21
}
int main()
{
test cls1;
cls1=function1(); // Line 27
function2(cls1); // Line 28
return 0;
}
由于VC编译的未优化代码和优化后的代码相差比较大,所以在编译时增加"/02"参数,使用命令行"cl test.cpp /Fa /02"对源文件进行编译。得到的中间汇编代码如下:
PUBLIC ?function1@@YA?AVtest@@XZ ; function1
; COMDAT ?function1@@YA?AVtest@@XZ
_TEXT SEGMENT
_cls2$ = -24
$T295 = 8
?function1@@YA?AVtest@@XZ PROC NEAR ; function1入口
; File main.cpp
; Line 15
mov eax, DWORD PTR $T295[esp-4] ; 将cls1的地址作为返回值保存到eax中
sub esp, 24 ; 分配cls2的内存
mov ecx, 6
push esi
push edi
lea esi, DWORD PTR _cls2$[esp+32]
mov edi, eax
rep movsd ; 将cls2对象复制到cls1中去
pop edi
pop esi
; Line 16
add esp, 24 ; 00000018H
ret 0
?function1@@YA?AVtest@@XZ ENDP ; function1结束
_TEXT ENDS
PUBLIC ?function2@@YAHVtest@@@Z ; function2
; COMDAT ?function2@@YAHVtest@@@Z
_TEXT SEGMENT
?function2@@YAHVtest@@@Z PROC NEAR ; function2入口
; Line 21
xor eax, eax ; Line 20 的操作被优化掉了
; Line 22
ret 0
?function2@@YAHVtest@@@Z ENDP ; function2结束
_TEXT ENDS
PUBLIC _main
; COMDAT _main
_TEXT SEGMENT
$T301 = -24
_main PROC NEAR ; COMDAT
; Line 25
sub esp, 24 ; 分配cls1的内存空间
; Line 27
lea eax, DWORD PTR $T301[esp+24] ; 将cls1的地址送入eax寄存器
push esi
push edi
push eax ; 将cls1的地址作为参数传递给function1
call ?function1@@YA?AVtest@@XZ ; 调用function1
; Line 28
sub esp, 20 ; 恢复堆栈并分配temp对象的空间(24-4=20)
mov ecx, 6
mov esi, eax
mov edi, esp
rep movsd ; 将cls1对象复制到temp对象中去
call ?function2@@YAHVtest@@@Z ; 调用function2
add esp, 24 ; 00000018H
; Line 29
xor eax, eax
; Line 30
pop edi
pop esi
add esp, 24 ; 00000018H
ret 0
_main ENDP
_TEXT ENDS
END
先来看function1的调用过程:1)程序现在堆栈中给cls1分配了24字节的内存;然后将cls1的地址作为参数传递给function1;随后调用function1。2)进入function1中,程序先给cls2分配内存;然后程序将cls2对象复制到cls1中去;把cls1的地址作为返回值。
根据上述过程,"test function1();"的实际形式其实是:
test *function1(test *ptr_cls)
{
test cls2;
memcpy(ptr_cls,&cls2,sizeof(test));
return ptr_cls;
}
而调用代码的实际形式是:
int main()
{
test cls1;
function1(&cls1);
... ...
return 0;
}
再来看function2的调用过程:程序先给temp分配了一个24字节的对象;然后将cls1对象复制到temp对象中去;随后就调用function2。比起function1,function2要简单很多。