![](/img/trans.png)
[英]Why does godbolt show parameter being passed in edi if C++ uses cdecl calling convention?
[英]In C++, do variadic functions (those with … at the end of the parameter list) necessarily follow the __cdecl calling convention?
我知道__stdcall函數不能有省略號,但我想確保沒有支持stdarg.h函數的平台來調用除__cdecl或__stdcall之外的約定。
調用約定必須是調用者從堆棧中清除參數的約束(因為被調用者不知道將傳遞什么)。
但這並不一定與微軟稱之為“__cdecl”的內容相對應。 例如,在SPARC上,它通常會傳遞寄存器中的參數,因為這就是SPARC設計工作的方式 - 它的寄存器基本上充當調用堆棧,如果調用足夠深,它會溢出到主存儲器他們將不再適合登記。
雖然我對它不太確定,但我期望在IA64(Itanium)上大致相同 - 它也有一個巨大的寄存器集(如果內存服務則需要幾百個)。 如果我沒有弄錯的話,它對你如何使用寄存器更加寬容,但我希望它至少在很多時候都能被類似地使用。
為什么這對你很重要? 使用stdarg.h及其宏的目的是隱藏調用約定與代碼之間的差異,因此它可以移植地使用變量參數。
編輯,基於評論:好的,現在我明白你在做什么(至少足以改善答案)。 鑒於您已經(顯然)擁有處理默認ABI變體的代碼,事情就更簡單了。 這只留下了可變函數是否總是使用“默認ABI”的問題,無論發生在手頭平台上的是什么。 以“stdcall”和“default”為唯一選項,我認為答案是肯定的。 例如,在Windows上, wsprintf
和wprintf
打破了經驗法則,並使用cdecl調用約定而不是stdcall。
您可以確定的最明確的方法是分析調用約定。 要使可變函數起作用,您的調用約定需要幾個屬性:
printf
的第一個參數,格式規范之類的東西。此外,變量參數列表本身的地址也必須從已知位置派生。) stdcall
將無法工作,因為被調用者負責從堆棧中彈出參數。 在舊的16位Windows時代, pascal
無法工作,因為它從左到右將參數壓入堆棧。
當然,正如其他答案所暗示的那樣,許多平台在調用約定方面沒有給你任何選擇,使得這個問題與那些問題無關。
請考慮x86系統上的以下功能:
void __stdcall something(char *,...);
該函數將自身聲明為__stdcall,這是一個callee-clean約定。 但是變量函數不能被callee-clean,因為被調用者不知道傳遞了多少參數,所以它不知道應該清理多少個參數。
Microsoft Visual Studio C / C ++編譯器通過將調用約定靜默轉換為__cdecl來解決此沖突,__ cdecl是唯一受支持的可變參數調用約定,用於不接受隱藏此參數的函數。
為什么這種轉換是以靜默方式進行而不是產生警告或錯誤?
我的猜測是,將編譯器選項/ Gr(將默認調用約定設置為__fastcall)和/ Gz(將默認調用約定設置為__stdcall)設置為不那么煩人。
將可變參數函數自動轉換為__cdecl意味着您只需將/ Gr或/ Gz命令行開關添加到編譯器選項中,所有內容仍然可以編譯和運行(只需使用新的調用約定)。
另一種看待這種情況的方法不是將編譯器視為將variadic __stdcall轉換為__cdecl,而是簡單地說“對於可變參數函數,__ stdcall是調用者干凈的”。
AFAIK,調用約定的多樣性對於x86上的DOS / Windows是唯一的。 大多數其他平台都有編譯器附帶操作系統並標准化約定。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.