先看一個代碼
void myfun(int i, int ii){ cout << i << " " << ii << endl;}void main(){ int b = 3; int arr[] = {6,7,8,9,10}; int *ptr = arr; *(ptr++) += 123; printf("%d\t%d\n", *ptr, *++ptr); int i = 10; myfun(i, ++i);}
VS2008輸出:
函數的調用規範
函數的調用規範,也稱為呼叫慣例(Calling convention)。函數的調用規範決定了函數調用時,實參壓棧、退棧及堆棧釋放方式,以及函數名改編(Name Mangling)的方案,也即命名規範。
Windows環境下常用的調用規範有:
1)__cdecl:這是C/C++函數預設的調用規範,參數從右向左依次傳遞,壓入堆棧,由調用函數負責堆棧的清退。這種方式適用於傳遞個數可變的參數給被調用函數,因為只有調用函數才知道它傳遞了多少個參數給被調函數。如printf函數。
2)__stdcall:參數從右向左依次傳遞,並壓入堆棧,由被調用函數清退堆棧。當函數有可變個數參數,自動轉化為__cdecl調用規範。
3)__thiscall:這是C++非靜態成員函數的預設調用規範,不能使用個數可變的參數。調用非靜態成員函數的時候,this指標直接儲存在ECX寄存器中,不入棧。其他方面同__stdcall。
4)__fastcall
凡是介面函數都必須顯示指定其調用規範,除非介面函數是類的非靜態成員函數。
關於結果的解釋
據說此題是華為的筆試題。但可能出題者也沒理解此種的奧秘,而只知道結果。關於函數調用的參數求值順序,語言實現標準是未定義的。之所以出現的結果,不是由函數的調用規範決定的(從右向左對參數壓棧),而是由微軟編譯器的具體實現:參數求值按照自右向左順序決定的。函數調用壓棧壓入的是運算式的計算後的值,而不是運算式本身。
另外,如果調用myfun(i, ++i); 改為myfun(i, i++);。將輸入:11 10
進一步的困惑
1: int main()
2: {
3: int i = 0;
4: printf( "%d, %d, %d, %d\n", i++, ++i, i, i++ );
5: return 0;
6: }
它在Visual C++ 2008的環境下編譯時間,輸出結果為2, 3, 3, 0,在MinGW的GCC環境下編譯運行時,輸出結果則為2, 2, 1, 0。可見不同的編譯器在實現時,對printf函數的參數進棧所作策略和抉擇不同,導致了輸出結果不同。結合上面關於對myfun的調用,發現即便在同一個編譯器中,參數的求值順序有時候也會不同。
進一步學習,請參考卡http://fairymemory.net/2010/01/02/c語言函數壓棧求值順序/