上次说了一个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要简单很多。

|