今天,在学习IOCP的时候,突然发现对c++编译器的一些常用的宏比较陌生,就稍微学习了一下。
ps:在vs 2005环境下,打开“Project → Project Settings”,选择 C/C++ 选项卡,在“Category”栏选择“Listing Files”然后在Listing file type栏选择“Assembly with Machine Code”。重新编译工程后则可以在输出目录看到与每一个.cpp文件同名的.cod文件
1、几种函数调用的声明方式
example:
1: #include "stdafx.h" 2: 3: int maxInt(int a,int b) 4: { 5: return a>b?a:b; 6: } 7: 8: int _tmain() 9: { 10: int x=3;y=9; 11: maxInt(x,y); 12: return 0; 13:}
_cdecl:全称为c declaration。这是c 默认的函数调用的方式,调用这样的函数时,所有参数从右向左压栈,由调用者进行清栈,调用函数时,可以不明确参数的个数和类型,c++编译器将不检查这样的不确定性,像printf和scanf就必须使用这种调用方式。
使用int _cdecl maxInt(int x,int y)声明方式编译后的汇编代码如下(关键看压栈和返回阶段):
_TEXT SEGMENT tv65 = -196 ; size = 4 _a$ = 8 ; size = 4 _b$ = 12 ; size = 4 ?maxInt@@YAHHH@Z PROC ; maxInt, COMDAT ; Line 4 00000 55 push ebp 00001 8b ec mov ebp, esp 00003 81 ec c4 00 00 00 sub esp, 196 ; 000000c4H 00009 53 push ebx 0000a 56 push esi 0000b 57 push edi 0000c 8d bd 3c ff ff ff lea edi, DWORD PTR [ebp-196] 00012 b9 31 00 00 00 mov ecx, 49 ; 00000031H 00017 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH 0001c f3 ab rep stosd ; Line 5 0001e 8b 45 08 mov eax, DWORD PTR _a$[ebp] 00021 3b 45 0c cmp eax, DWORD PTR _b$[ebp] 00024 7e 0b jle SHORT $LN3@maxInt 00026 8b 4d 08 mov ecx, DWORD PTR _a$[ebp] 00029 89 8d 3c ff ff ff mov DWORD PTR tv65[ebp], ecx 0002f eb 09 jmp SHORT $LN4@maxInt $LN3@maxInt: 00031 8b 55 0c mov edx, DWORD PTR _b$[ebp] 00034 89 95 3c ff ff ff mov DWORD PTR tv65[ebp], edx $LN4@maxInt: 0003a 8b 85 3c ff ff ff mov eax, DWORD PTR tv65[ebp] ; Line 6 00040 5f pop edi 00041 5e pop esi 00042 5b pop ebx 00043 8b e5 mov esp, ebp 00045 5d pop ebp 00046 c3 ret 0 ?maxInt@@YAHHH@Z ENDP ; maxInt _TEXT ENDS
调用者的汇编代码:
; Line 11 0002c 8b 45 ec mov eax, DWORD PTR _y$[ebp] 0002f 50 push eax 00030 8b 4d f8 mov ecx, DWORD PTR _x$[ebp] 00033 51 push ecx 00034 e8 00 00 00 00 call ?maxInt@@YAHHH@Z ; maxInt 00039 83 c4 08 add esp, 8
由此,可以看出,所有参数是从右向左压栈,并且由调用者清理栈。
_stdcall:全称为Standard Call。是c++标准的调用方式,所有参数从右向左压栈,this指针最后压栈,被调用者自己清理栈,必须确定参数的个数和类型,调用者不能传递过多或者过少的参数。
使用int _stdcall maxInt(int x,int y)声明方式编译后的汇编代码如下:
_TEXT SEGMENT tv65 = -196 ; size = 4 _a$ = 8 ; size = 4 _b$ = 12 ; size = 4 ?maxInt@@YGHHH@Z PROC ; maxInt, COMDAT ; Line 4 00000 55 push ebp 00001 8b ec mov ebp, esp 00003 81 ec c4 00 00 00 sub esp, 196 ; 000000c4H 00009 53 push ebx 0000a 56 push esi 0000b 57 push edi 0000c 8d bd 3c ff ff ff lea edi, DWORD PTR [ebp-196] 00012 b9 31 00 00 00 mov ecx, 49 ; 00000031H 00017 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH 0001c f3 ab rep stosd ; Line 5 0001e 8b 45 08 mov eax, DWORD PTR _a$[ebp] 00021 3b 45 0c cmp eax, DWORD PTR _b$[ebp] 00024 7e 0b jle SHORT $LN3@maxInt 00026 8b 4d 08 mov ecx, DWORD PTR _a$[ebp] 00029 89 8d 3c ff ff ff mov DWORD PTR tv65[ebp], ecx 0002f eb 09 jmp SHORT $LN4@maxInt $LN3@maxInt: 00031 8b 55 0c mov edx, DWORD PTR _b$[ebp] 00034 89 95 3c ff ff ff mov DWORD PTR tv65[ebp], edx $LN4@maxInt: 0003a 8b 85 3c ff ff ff mov eax, DWORD PTR tv65[ebp] ; Line 6 00040 5f pop edi 00041 5e pop esi 00042 5b pop ebx 00043 8b e5 mov esp, ebp 00045 5d pop ebp 00046 c2 08 00 ret 8 ?maxInt@@YGHHH@Z ENDP ; maxInt _TEXT ENDS
调用函数时的汇编代码:
; Line 11 0002c 8b 45 ec mov eax, DWORD PTR _y$[ebp] 0002f 50 push eax 00030 8b 4d f8 mov ecx, DWORD PTR _x$[ebp] 00033 51 push ecx 00034 e8 00 00 00 00 call ?maxInt@@YGHHH@Z ; maxInt
由此可以看出,参数是从右向左压栈,而且由被调用者自己清理栈。
_fastcall:快速调用,由于一般函数的参数个数都很好,所以使用这个方式可以把前面几个(一般是两个)参数直接放到寄存器中,其余参数依然通过栈来传递,节省压栈退栈时间,实现快速调用,返回方式和_stdcall一样。
使用int _fastcall maxInt(int x,int y)调用方式编译后的汇编代码如下:
_TEXT SEGMENT tv65 = -220 ; size = 4 _b$ = -20 ; size = 4 _a$ = -8 ; size = 4 ?maxInt@@YIHHH@Z PROC ; maxInt, COMDAT ; _a$ = ecx ; _b$ = edx ; Line 4 00000 55 push ebp 00001 8b ec mov ebp, esp 00003 81 ec dc 00 00 00 sub esp, 220 ; 000000dcH 00009 53 push ebx 0000a 56 push esi 0000b 57 push edi 0000c 51 push ecx 0000d 8d bd 24 ff ff ff lea edi, DWORD PTR [ebp-220] 00013 b9 37 00 00 00 mov ecx, 55 ; 00000037H 00018 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH 0001d f3 ab rep stosd 0001f 59 pop ecx 00020 89 55 ec mov DWORD PTR _b$[ebp], edx 00023 89 4d f8 mov DWORD PTR _a$[ebp], ecx ; Line 5 00026 8b 45 f8 mov eax, DWORD PTR _a$[ebp] 00029 3b 45 ec cmp eax, DWORD PTR _b$[ebp] 0002c 7e 0b jle SHORT $LN3@maxInt 0002e 8b 4d f8 mov ecx, DWORD PTR _a$[ebp] 00031 89 8d 24 ff ff ff mov DWORD PTR tv65[ebp], ecx 00037 eb 09 jmp SHORT $LN4@maxInt $LN3@maxInt: 00039 8b 55 ec mov edx, DWORD PTR _b$[ebp] 0003c 89 95 24 ff ff ff mov DWORD PTR tv65[ebp], edx $LN4@maxInt: 00042 8b 85 24 ff ff ff mov eax, DWORD PTR tv65[ebp] ; Line 6 00048 5f pop edi 00049 5e pop esi 0004a 5b pop ebx 0004b 8b e5 mov esp, ebp 0004d 5d pop ebp 0004e c3 ret 0 ?maxInt@@YIHHH@Z ENDP ; maxInt _TEXT ENDS
调用该函数的汇编代码如下:
; Line 11 0002c 8b 55 ec mov edx, DWORD PTR _y$[ebp] 0002f 8b 4d f8 mov ecx, DWORD PTR _x$[ebp] 00032 e8 00 00 00 00 call ?maxInt@@YIHHH@Z ; maxInt
由此可以看出,调用函数时,不用压栈,被调用函数返回时,不用退栈。
__thiscall:这种调用方式是为了解决成员函数中this指针的传递而设置的,this指针被放在特定的寄存器中,这个特定的寄存器在不同的编译器中式不一样的,它只能用在c++类成员函数的调用方式,返回方式与_stdcall相当。
把example修改后如下:
#include "stdafx.h" class CMyMath { public: int __thiscall maxInt(int a,int b) { return a > b ? a:b; } } int _tmain() { int x = 3,y = 9; CMyMath m; m.maxInt(x,y); return 0; }
修改后,maxInt编译后的汇编代码如下:
_TEXT SEGMENT tv65 = -208 ; size = 4 _this$ = -8 ; size = 4 _a$ = 8 ; size = 4 _b$ = 12 ; size = 4 ?maxInt@CMyMath@@QAEHHH@Z PROC ; CMyMath::maxInt, COMDAT ; _this$ = ecx ; Line 8 00000 55 push ebp 00001 8b ec mov ebp, esp 00003 81 ec d0 00 00 00 sub esp, 208 ; 000000d0H 00009 53 push ebx 0000a 56 push esi 0000b 57 push edi 0000c 51 push ecx 0000d 8d bd 30 ff ff ff lea edi, DWORD PTR [ebp-208] 00013 b9 34 00 00 00 mov ecx, 52 ; 00000034H 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 ; Line 9 00023 8b 45 08 mov eax, DWORD PTR _a$[ebp] 00026 3b 45 0c cmp eax, DWORD PTR _b$[ebp] 00029 7e 0b jle SHORT $LN3@maxInt 0002b 8b 4d 08 mov ecx, DWORD PTR _a$[ebp] 0002e 89 8d 30 ff ff ff mov DWORD PTR tv65[ebp], ecx 00034 eb 09 jmp SHORT $LN4@maxInt $LN3@maxInt: 00036 8b 55 0c mov edx, DWORD PTR _b$[ebp] 00039 89 95 30 ff ff ff mov DWORD PTR tv65[ebp], edx $LN4@maxInt: 0003f 8b 85 30 ff ff ff mov eax, DWORD PTR tv65[ebp] ; Line 10 00045 5f pop edi 00046 5e pop esi 00047 5b pop ebx 00048 8b e5 mov esp, ebp 0004a 5d pop ebp 0004b c2 08 00 ret 8 ?maxInt@CMyMath@@QAEHHH@Z ENDP ; CMyMath::maxInt _TEXT ENDS
调用该函数时的汇编代码如下:
; Line 17 0002c 8b 45 ec mov eax, DWORD PTR _y$[ebp] 0002f 50 push eax 00030 8b 4d f8 mov ecx, DWORD PTR _x$[ebp] 00033 51 push ecx 00034 8d 4d e3 lea ecx, DWORD PTR _m$[ebp] 00037 e8 00 00 00 00 call ?maxInt@CMyMath@@QAEHHH@Z ; CMyMath::maxInt
由此可以看出,在vs 2005下,使用ecx来传递this指针。
(未完待续)