[英]Output parameter or return struct in C11?
我知道C ++ 11具有移動語義,這意味着您可以直接從函數返回結構,而不必擔心它會被復制(假設是簡單的結構),而不是通過輸出參數編寫結構。
C11有這樣的東西嗎? 還是返回的結構仍然每次都被復制? 輸出參數仍然是“最佳實踐”嗎?
我認為這里有些混亂應予以澄清。 C ++的語義和實現是不同的。
在C ++中,“移動”與“復制”只是您要調用哪個構造函數(或operator=
)的問題。
是否復制結構成員的表示形式的問題是一個完全獨立的問題。 換句話說,“處理器是否必須移動這些字節?” 不是語言語義的一部分。
MyClass func() {
MyClass x;
x.method(...);
...
return x;
}
如果可用,將使用移動語義返回,但即使在C ++ 11之前,返回值優化也是可用的。
我們之所以喜歡使用移動語義的原因是因為移動對象不會引起深層復制,例如,如果移動std::vector<T>
,則不必復制所有T
但是,您仍在復制數據! 因此, std::move(x)
從語義上講是一個移動操作(認為它是使用線性而不是經典的邏輯),但是仍然可以通過將數據復制到內存中來實現。
除非您的ABI允許您避免復制。 這帶我們去...
當您調用返回大結構的函數時(術語“大”是相對的,可能只是幾個詞),大多數ABI都會要求通過引用該函數來傳遞該結構。 因此,當您編寫這樣的內容時:
MyClass func() { ... }
一旦在組裝中查看它,它看起來可能會像這樣:
void func(MyClass *ptr) { ... }
當然,這是一種簡化! 指針通常是隱式的。 但是重要的一點是, 有時我們已經避免了復制。
這是一個簡單的示例:
struct big {
int x[100];
};
struct big func1(void);
int func2() {
struct big x = func1();
struct big y = func1();
return x.x[0] + y.x[0];
}
在x64上使用gcc -O2
進行編譯時,得到以下程序集輸出:
subq $808, %rsp
movq %rsp, %rdi
call func1
leaq 400(%rsp), %rdi
call func1
movl 400(%rsp), %eax
addl (%rsp), %eax
addq $808, %rsp
ret
您可以看到, 沒有任何地方可以復制struct big
。 func2()
的結果只是放在func1()
的堆棧上,然后堆棧被移動,因此下一個結果將放在其他位置。
在常見的ABI中,大型函數的結果不會通過多個函數棧傳遞。 從上面的func2()
返回x
或y
將導致結構被復制。
但要記住! 這與移動語義與復制語義無關,因為要復制的結構數據只是實現細節,而不是語言語義。 在C ++中,使用std::move()
可能仍會導致結構被復制,只是不會調用復制構造函數。
結論:從C或C ++返回大型結構可能會導致復制結構, 具體取決於 ABI的詳細信息,函數的優化方式以及所涉及的代碼。 但是,只要結構只有幾句話,我就不會擔心。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.