以下这个程序中使用了参数传值和返回值传值的操作:
class A
{
public:
A()
{
i = 0;
}
A(const A& c)
{
i = c.i;
}
private:
int i;
};
A funbyValue(A c)
{
return c;
}
void main(int argc,char* argv[])
{
A b;
funbyValue(b);
}
该程序相应的汇编代码如下(环境使用vs2003, windows系统):
; Listing generated by Microsoft (R) Optimizing Compiler Version 13.10.3077
TITLE ./main.cpp
.386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
$$TYPES SEGMENT BYTE USE32 'DEBTYP'
$$TYPES ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
; COMDAT ??0A@@QAE@XZ
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ??0A@@QAE@ABV0@@Z
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ?funbyValue@@YA?AVA@@V1@@Z
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT _main
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
sxdata SEGMENT DWORD USE32 'SXDATA'
sxdata ENDS
FLAT GROUP _DATA, CONST, _BSS
ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif
INCLUDELIB LIBCD
INCLUDELIB OLDNAMES
PUBLIC ??0A@@QAE@ABV0@@Z ; A::A
PUBLIC ?funbyValue@@YA?AVA@@V1@@Z ; funbyValue
EXTRN __RTC_InitBase:NEAR
EXTRN __RTC_Shutdown:NEAR
EXTRN __RTC_CheckEsp:NEAR
; COMDAT rtc$IMZ
; File j:/example/contructor/main.cpp
rtc$IMZ SEGMENT
__RTC_InitBase.rtc$IMZ DD FLAT:__RTC_InitBase
rtc$IMZ ENDS
; COMDAT rtc$TMZ
rtc$TMZ SEGMENT
__RTC_Shutdown.rtc$TMZ DD FLAT:__RTC_Shutdown
; Function compile flags: /Odt /RTCsu /ZI
rtc$TMZ ENDS
; COMDAT ?funbyValue@@YA?AVA@@V1@@Z
_TEXT SEGMENT
___$ReturnUdt$ = 8 ; size = 4
_c$ = 12 ; size = 4
?funbyValue@@YA?AVA@@V1@@Z PROC NEAR ; funbyValue, COMDAT
; 19 : {
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 81 ec c0 00 00
00 sub esp, 192 ; 000000c0H
00009 53 push ebx
0000a 56 push esi
0000b 57 push edi
0000c 8d bd 40 ff ff
ff lea edi, DWORD PTR [ebp-192]
00012 b9 30 00 00 00 mov ecx, 48 ; 00000030H
00017 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH
0001c f3 ab rep stosd
; 20 : return c;
0001e 8d 45 0c lea eax, DWORD PTR _c$[ebp]
00021 50 push eax
00022 8b 4d 08 mov ecx, DWORD PTR ___$ReturnUdt$[ebp]
00025 e8 00 00 00 00 call ??0A@@QAE@ABV0@@Z ; A::A
0002a 8b 45 08 mov eax, DWORD PTR ___$ReturnUdt$[ebp]
; 21 : }
0002d 5f pop edi
0002e 5e pop esi
0002f 5b pop ebx
00030 81 c4 c0 00 00
00 add esp, 192 ; 000000c0H
00036 3b ec cmp ebp, esp
00038 e8 00 00 00 00 call __RTC_CheckEsp
0003d 8b e5 mov esp, ebp
0003f 5d pop ebp
00040 c3 ret 0
?funbyValue@@YA?AVA@@V1@@Z ENDP ; funbyValue
; Function compile flags: /Odt /RTCsu /ZI
_TEXT ENDS
; COMDAT ??0A@@QAE@ABV0@@Z
_TEXT SEGMENT
_this$ = -8 ; size = 4
_c$ = 8 ; size = 4
??0A@@QAE@ABV0@@Z PROC NEAR ; A::A, COMDAT
; _this$ = ecx
; 9 : A(const A& c)
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 81 ec cc 00 00
00 sub esp, 204 ; 000000ccH
00009 53 push ebx
0000a 56 push esi
0000b 57 push edi
0000c 51 push ecx
0000d 8d bd 34 ff ff
ff lea edi, DWORD PTR [ebp-204]
00013 b9 33 00 00 00 mov ecx, 51 ; 00000033H
00018 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH
0001d f3 ab rep stosd
0001f 59 pop ecx
00020 89 4d f8 mov DWORD PTR _this$[ebp], ecx
; 10 : {
; 11 : i = c.i;
00023 8b 45 f8 mov eax, DWORD PTR _this$[ebp]
00026 8b 4d 08 mov ecx, DWORD PTR _c$[ebp]
00029 8b 11 mov edx, DWORD PTR [ecx]
0002b 89 10 mov DWORD PTR [eax], edx
; 12 : }
0002d 8b 45 f8 mov eax, DWORD PTR _this$[ebp]
00030 5f pop edi
00031 5e pop esi
00032 5b pop ebx
00033 8b e5 mov esp, ebp
00035 5d pop ebp
00036 c2 04 00 ret 4
??0A@@QAE@ABV0@@Z ENDP ; A::A
_TEXT ENDS
PUBLIC ??0A@@QAE@XZ ; A::A
PUBLIC _main
EXTRN @_RTC_CheckStackVars@8:NEAR
; Function compile flags: /Odt /RTCsu /ZI
; COMDAT _main
_TEXT SEGMENT
$T315 = -212 ; size = 4
_b$ = -8 ; size = 4
_argc$ = 8 ; size = 4
_argv$ = 12 ; size = 4
_main PROC NEAR ; COMDAT
; 24 : {
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 81 ec e4 00 00
00 sub esp, 228 ; 000000e4H
00009 53 push ebx
0000a 56 push esi
0000b 57 push edi
0000c 8d bd 1c ff ff
ff lea edi, DWORD PTR [ebp-228]
00012 b9 39 00 00 00 mov ecx, 57 ; 00000039H
00017 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH
0001c f3 ab rep stosd
; 25 : A b;
0001e 8d 4d f8
lea ecx, DWORD PTR _b$[ebp]
00021 e8 00 00 00 00 call ??0A@@QAE@XZ ; A::A
; 26 : funbyValue(b);
00026 51 push ecx
00027 8b cc
mov ecx, esp
00029 8d 45 f8
lea eax, DWORD PTR _b$[ebp]
0002c 50
push eax
0002d e8 00 00 00 00
call ??0A@@QAE@ABV0@@Z ; A::A
00032 8d 8d 2c ff ff
ff
lea ecx, DWORD PTR $T315[ebp]
00038 51
push ecx
00039 e8 00 00 00 00
call ?funbyValue@@YA?AVA@@V1@@Z ; funbyValue
0003e 83 c4 08 add esp, 8
; 27 : }
00041 33 c0 xor eax, eax
00043 52 push edx
00044 8b cd mov ecx, ebp
00046 50 push eax
00047 8d 15 00 00 00
00 lea edx, DWORD PTR $L318
0004d e8 00 00 00 00 call @_RTC_CheckStackVars@8
00052 58 pop eax
00053 5a pop edx
00054 5f pop edi
00055 5e pop esi
00056 5b pop ebx
00057 81 c4 e4 00 00
00 add esp, 228 ; 000000e4H
0005d 3b ec cmp ebp, esp
0005f e8 00 00 00 00 call __RTC_CheckEsp
00064 8b e5 mov esp, ebp
00066 5d pop ebp
00067 c3 ret 0
$L318:
00068 01 00 00 00 DD 1
0006c 00 00 00 00 DD $L317
$L317:
00070 f8 ff ff ff DD -8 ; fffffff8H
00074 04 00 00 00 DD 4
00078 00 00 00 00 DD $L316
$L316:
0007c 62 DB 98 ; 00000062H
0007d 00 DB 0
_main ENDP
; Function compile flags: /Odt /RTCsu /ZI
_TEXT ENDS
; COMDAT ??0A@@QAE@XZ
_TEXT SEGMENT
_this$ = -8 ; size = 4
??0A@@QAE@XZ PROC NEAR ; A::A, COMDAT
; _this$ = ecx
; 4 : A()
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 81 ec cc 00 00
00 sub esp, 204 ; 000000ccH
00009 53 push ebx
0000a 56 push esi
0000b 57 push edi
0000c 51 push ecx
0000d 8d bd 34 ff ff
ff lea edi, DWORD PTR [ebp-204]
00013 b9 33 00 00 00 mov ecx, 51 ; 00000033H
00018 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH
0001d f3 ab rep stosd
0001f 59 pop ecx
00020 89 4d f8 mov DWORD PTR _this$[ebp], ecx
; 5 : {
; 6 : i = 0;
00023 8b 45 f8 mov eax, DWORD PTR _this$[ebp]
00026 c7 00 00 00 00
00 mov DWORD PTR [eax], 0
; 7 : }
0002c 8b 45 f8 mov eax, DWORD PTR _this$[ebp]
0002f 5f pop edi
00030 5e pop esi
00031 5b pop ebx
00032 8b e5 mov esp, ebp
00034 5d pop ebp
00035 c3 ret 0
??0A@@QAE@XZ ENDP ; A::A
_TEXT ENDS
END
总结:从程序相应的汇编中我们可以看到 funbyValue中参数和返回值的内存都是分配在调用函数中:
分析:由粗线部分我们可以知道类对象的this指针地址值放到寄存器ecx中,在类对象的方法中我们可以看到ecx中的地址值被复制到方法的堆栈中,然后对this指向对象中其他成员的方法。
由红色粗斜线部分我们可以知道类对象的成员方法中,参数是用户定义类型并且是传递引用的情况下,是通过压栈的方法传入的。
在函数参数和返回值都是用户自定义的类型时,并且是传值的情况下,他们都是在调用者的空间中进行分配的:(1)对于参数则是在栈顶分配的,理由是它将esp作为this指针,并且通过拷贝构造函数,将实参数的值拷贝到形参中,这可以由兰粗部分的汇编代码看出。(2)而对于返回值它是调用者的堆栈内部进行分配的,并且通过压栈的方式进入到堆栈中,这可以由粉红部分得知。
这里还有一个规律:那就是每个函数内部都是用ebp来表示它可用堆栈内存的起始部分,esp减去一定的空间,这样,esp是那个函数可以分配局部变量的堆栈的末尾。
C++中的参数和返回值的类型的选择:
(1)引用参数
(2)指针参数
(3)值类型参数
对于数据类型来说,他就是指针类型参数。