簡體   English   中英

C和C ++調用約定有什么區別?

[英]What are the differences between C, and C++ calling conventions?

據我所知,調用約定依賴於編譯器和體系結構。 但C和C ++調用約定之間是否存在明顯差異?

但C和C ++調用約定之間是否存在明顯差異?

一般來說,沒有。 C ++被有意設計為盡可能與C兼容,特別是它在所有系統上使用C應用程序二進制接口。

但是C ABI不能滿足C ++需要的許多功能(特別是重載,命名空間和函數模板),因此C ++編譯器對函數名稱進行了一些更改。 這稱為名稱修改

因此,為了使C和C ++代碼之間的函數調用起作用,必須將這些函數聲明為extern "C" ,它禁用名稱修改並確保調用約定是C所期望的那些(但我希望后一個方面自動出現) ,即使標准沒有強制要求這一點)。

C ++還為C中不存在的成員函數(有時稱為thiscall )提供了額外的調用約定。但是,自由函數使用與C相同的調用約定,無論哪個在給定系統上。

在任何標准中都沒有要求C和C ++調用約定在給定編譯器上是相同的,除了聲明extern "C"的C ++函數必須使用與C函數相同的調用約定調用。

這就是為什么指向函數的指針和帶有相同參數和返回類型的帶C指令的指針具有不同的類型。 當調用該函數時,編譯器可以從類型中知道調用它的調用約定,如果它們不同。

在實踐中 ,我不認為我曾經故意處理過使用具有和不具有C鏈接的自由函數之間的不兼容調用約定的示例。 通常,C ++實現從其計划運行的系統的ABI采用其調用約定,以便生成系統的其他用戶可以根據ABI理解的可鏈接對象(可執行文件和庫)。

這不是必需的 - 標准不關心是否存在系統ABI,並且系統通常不關心如何在自包含的可執行文件[*]內進行調用。 除非有一些特殊的理由不這樣做,否則這樣做是明智的。 給定系統上的系統ABI可能會也可能不會提到C ++ - 如果沒有,那么就非C語言鏈接函數而言,C ++實現是獨立的,但正如我所說,制作可能的函數通常是明智的。 C鏈接使用相同的調用約定,就好像它們有C鏈接一樣。

我說“不兼容”而不是“不同”,因為當然有一些事情在C調用約定中沒有提到但需要在C ++調用約定中指定。 例如,如何傳遞指向成員函數的指針。 很可能這不是系統ABI所固定的,因此留待C ++實現。 這樣做的原因是將指向成員函數的指針實現到C ++實現,而不是系統ABI執行制造商認為是C ++編譯器編寫器的工作。

有了調用約定,請注意,在有或沒有C鏈接的自由函數之間,名稱修改不可避免地存在差異。 原因是C ++名稱修改必須包含參數的類型(因為函數重載),而C名稱修改必須不能(因為extern函數聲明帶有未指定的參數)。

[*]雖然我已經看過使用“錯誤”調用約定會破壞事物的例子。 ISTR是Windows移動設備,如果您將堆棧格式化為與操作系統預期的不同,那么某些硬件異常會占用設備,因為操作系統試圖回溯違規線程的堆棧,而不能。 所以至少在操作系統版本上,可能在2005年左右,如果你想要操作系統的診斷工作,那么你必須在內部使用Windows調用約定。 或者無論如何,調用約定的部分與堆棧幀格式有關。 當然,這完全是我們的搞砸了,但我不知道我們是否可以通過為硬件異常安裝我們自己的處理程序來正確修復它,而不是通過首先不引起異常來解決它們。 這確實意味着用戶模式進程可以通過堆棧溢出來簡單地降低操作系統,並且調試代碼比在其他Window上更難。 所以我們稍微指責操作系統。

據我所知,調用約定依賴於編譯器和體系結構。

是。

但C和C ++調用約定之間是否存在明顯差異?

更好的問題是“有任何相似之處嗎?”

是:

在C ++應用程序中聲明為extern "C"函數具有與該體系結構上的C函數相同的調用約定。

您對調用約定做出的任何其他假設都是推測性的,可能恰好相同,但您需要根據具體情況來擔心。

語言之間不同調用約定的整個概念超出了任何給定的語言(及其規范)。 這應該是這樣的,這些事情與任何值得它名字的語言的規范無關。 基本上,你是對的,它是在規范的特定實現的領域 - 編譯器體系結構。 當然,在鏈接規范等給定語言的規范/標准中討論的一些概念會影響調用約定,但這不是計划后果。

ABI的職責是使這些概念標准化/形式化,然后再次,人們沒有義務尊重(並且可能因為豐富的實施方案變化很大)

實際上,規范需要忽略參數傳遞的方式,最后是堆棧或寄存器,兩者的組合,分配的順序等。這是從事實現的人員和設計人員的工作。實際的硬件,更重要的是指令集。

因此:標准化的C和C ++都沒有任何關於如何實現的說法。 因此,語言必然沒有區別。 只是在他們應用的方式。 這就是編譯器架構的領域。

沒有C或C ++標准定義ABI。 這是一個有意的設計決策,它允許編譯器編寫者自由地創建盡可能高效的實現。

隨着時間的推移,一個名為cdecl的C調用約定已成為x86體系結構的事實標准。 在AMD64機器上運行的C代碼有類似的事實標准 ,但它們在UNIX和Windows操作系統之間有所不同。

在Unix機器上,有一些基於英特爾發布的Itanium C ++ ABI的通用C ++ ABI。 但是,由於C ++的額外復雜性,同一編譯器的不同版本甚至不同的編譯器開關通常會生成符合不兼容ABI的機器代碼。

通常可以依賴使用extern "C" { ... }來強制編譯器使用事實上的C ABI來實現函數,但即使這樣也不是C ++標准所要求的。

如果您對當前C ++ ABI的全面描述感興趣,那么您應該看看Agner Fog的“針對不同C ++編譯器和操作系統的調用約定”

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM