[英]Why doesn't C code return a struct?
雖然它非常方便,但我很少(如果有的話)遇到在C中返回struct
(或union
s)的函數,無論它們是動態鏈接函數還是靜態定義函數。
它們通過指針參數返回數據。
(Windows中的動態示例是GetSystemInfo
。)
這背后的原因是什么?
是因為性能問題,ABI兼容性問題還是其他問題?
我會說“性能”,加上有時甚至有可能讓C程序員感到驚訝。 它不是......在C的一般“味道”中,對許多人來說,扔掉諸如結構之類的大事物就像它們僅僅是價值一樣。 根據語言,他們確實是。
同樣,許多C程序員似乎在需要復制結構時自動求助於memcpy()
,而不僅僅是使用賦值。
至少在C ++中,有一種稱為“返回值優化”的東西能夠以這種方式靜默轉換代碼:
struct Point { int x, y; };
struct Point point_new(int x, int y)
{
struct Point p;
p.x = x;
p.y = y;
return p;
}
成:
void point_new(struct Point *return_value, int x, int y)
{
struct Point p;
p.x = x;
p.y = y;
*return_value = p;
}
它消除了結構值的(可能是堆棧飢餓的)“真實”返回。 我想更好的是這個,不確定他們是否那么聰明:
void point_new(struct Point *return_value, int x, int y)
{
return_value->x = x;
return_value->y = y;
}
我不確定C編譯器是否可以執行任何此類操作,如果它們不能,那么我認為這可能是針對結構返回的真正參數,對於性能非常關鍵的程序。
原因主要是歷史原因。 Rob Pike在他的論文“The Text Editor sam
”中寫道
編程風格的相關問題:
sam
經常按值傳遞結構,這簡化了代碼。 傳統上,C程序通過引用傳遞結構,但堆棧上的隱式分配更容易使用。 結構傳遞是C的一個相對較新的特性(它不在C 14的標准參考手冊中),並且在大多數商業C編譯器中得不到很好的支持。 但它既方便又富有表現力,通過完全避免分配器並消除指針別名來簡化內存管理。
話雖如此,該技術存在缺陷; 返回淫穢的大型結構機會堆棧溢出。
AC函數可以返回一個結構(C ++也是如此,它很常見)。 也許在C的最初幾年,它不可能。
適用於Linux及相關系統的x86-64 ABI規范 (第21頁)甚至表示,適合兩個-64位字的結構通常可以在兩個寄存器中返回,而無需通過內存(甚至是堆棧)。 很可能這比通過堆棧更快。
正如unwind在他的回答中所回答的那樣 ,ABI經常要求將結構結果靜默地轉換為不可見的指針。
甚至可以定義另一個調用約定,它在更多寄存器中返回更多數據。 但是這樣的新約定會破壞所有目標代碼並需要重新編譯所有內容(甚至包括Linux上的libc.so.6
等系統庫),當然還需要更改編譯器。
當然,ABI約定與處理器,系統和編譯器有關。
我不知道Windows,我不知道Windows定義為什么是ABI。
在ANSI-C之前,您無法返回結構類型的對象,也無法傳遞結構類型的參數。
來自Chris Torek在comp.lang.c中的引用:
請注意,V6 C也不支持結構值參數和結構值返回值。
現在它們使用不當的原因是人們更喜歡返回指向結構的指針,該結構只涉及指針副本而不是整個結構對象的副本。
除了可能存在性能損失或者在預標准日期間通常不支持按值返回結構的想法之外,C函數不使用結構的值返回的另一個原因是,如果返回struct你不能輕易返回成功/失敗指標。 我知道我偶爾會開始設計一個函數來返回在函數中初始化的結構,但是后來我遇到了如何指示函數是否成功的問題。 你幾乎有以下選擇:
只有選項1才能使界面不再是一個kludge。 第二種選擇類型失敗了按值返回結構的目的,實際上使得函數更難以用於處理失敗。 第三種選擇在幾乎所有情況下都顯然不是一個好的設計。
通常,Windows函數不返回任何內容或錯誤代碼,特別是在返回結構或類時。
效率可能是一個問題,盡管RVO 應該消除開銷。
我認為主要原因是保持方法與之前使用的編碼風格內聯。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.