為什么 C 和 Go 中的約定是傳遞一個指向變數的指標并更改它而不是回傳一個帶有該值的新變數?
在 C 中:
#include <stdio.h>
int getValueUsingReturn() {
int value = 42;
return value;
}
void getValueUsingPointer(int* value ) {
*value = 42;
}
int main(void) {
int valueUsingReturn = getValueUsingReturn();
printf("%d\n", valueUsingReturn);
int valueUsingPointer;
getValueUsingPointer(&valueUsingPointer);
printf("%d\n", valueUsingPointer);
return 0;
}
在圍棋中:
package main
import "fmt"
func getValueUsingReturn() int {
value := 42
return value
}
func getValueUsingPointer(value *int) {
*value = 42
}
func main() {
valueUsingReturn := getValueUsingReturn()
fmt.Printf("%d\n", valueUsingReturn)
var valueUsingPointer int
getValueUsingPointer(&valueUsingPointer)
fmt.Printf("%d\n", valueUsingPointer)
}
執行其中一項是否有任何性能優勢或限制?
uj5u.com熱心網友回復:
首先,我對 Go 的了解不夠,無法對其進行判斷,但答案將適用于 C 的情況。
如果您只是在處理像ints這樣的原始型別,那么我會說這兩種技術之間沒有性能差異。
當structs 發揮作用時,通過指標修改變數有一個非常小的優勢(純粹基于您在代碼中所做的事情)
#include <stdio.h>
struct Person {
int age;
const char *name;
const char *address;
const char *occupation;
};
struct Person getReturnedPerson() {
struct Person thePerson = {26, "Chad", "123 Someplace St.", "Software Engineer"};
return thePerson;
}
void changeExistingPerson(struct Person *thePerson) {
thePerson->age = 26;
thePerson->name = "Chad";
thePerson->address = "123 Someplace St.";
thePerson->occupation = "Software Engineer";
}
int main(void) {
struct Person someGuy = getReturnedPerson();
struct Person theSameDude;
changeExistingPerson(&theSameDude);
return 0;
}
海灣合作委員會 x86-64 11.2
沒有優化
回傳struct通過該函式的回傳變數比較慢,因為變數有通過分配所需要的值,在此之后,變數被復制到的回傳值是“內置”。
當您通過指標間接修改變數時,除了將所需的值寫入記憶體地址(基于您傳入的指標)之外別無他法
.LC0:
.string "Chad"
.LC1:
.string "123 Someplace St."
.LC2:
.string "Software Engineer"
getReturnedPerson:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-40], rdi
mov DWORD PTR [rbp-32], 26
mov QWORD PTR [rbp-24], OFFSET FLAT:.LC0
mov QWORD PTR [rbp-16], OFFSET FLAT:.LC1
mov QWORD PTR [rbp-8], OFFSET FLAT:.LC2
mov rcx, QWORD PTR [rbp-40]
mov rax, QWORD PTR [rbp-32]
mov rdx, QWORD PTR [rbp-24]
mov QWORD PTR [rcx], rax
mov QWORD PTR [rcx 8], rdx
mov rax, QWORD PTR [rbp-16]
mov rdx, QWORD PTR [rbp-8]
mov QWORD PTR [rcx 16], rax
mov QWORD PTR [rcx 24], rdx
mov rax, QWORD PTR [rbp-40]
pop rbp
ret
changeExistingPerson:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov rax, QWORD PTR [rbp-8]
mov DWORD PTR [rax], 26
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rax 8], OFFSET FLAT:.LC0
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rax 16], OFFSET FLAT:.LC1
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rax 24], OFFSET FLAT:.LC2
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 64
lea rax, [rbp-32]
mov rdi, rax
mov eax, 0
call getReturnedPerson
lea rax, [rbp-64]
mov rdi, rax
call changeExistingPerson
mov eax, 0
leave
ret
輕微優化
但是,今天的大多數編譯器都可以弄清楚您在這里嘗試做什么,并且會平衡兩種技術之間的性能。
如果你想絕對小氣,傳遞指標最多也快幾個時鐘周期。
在從函式回傳一個變數時,你至少還得設定回傳值的地址。
mov rax, rdi
但是在傳遞指標時,甚至沒有這樣做。
但除此之外,這兩種技術沒有性能差異。
.LC0:
.string "Chad"
.LC1:
.string "123 Someplace St."
.LC2:
.string "Software Engineer"
getReturnedPerson:
mov rax, rdi
mov DWORD PTR [rdi], 26
mov QWORD PTR [rdi 8], OFFSET FLAT:.LC0
mov QWORD PTR [rdi 16], OFFSET FLAT:.LC1
mov QWORD PTR [rdi 24], OFFSET FLAT:.LC2
ret
changeExistingPerson:
mov DWORD PTR [rdi], 26
mov QWORD PTR [rdi 8], OFFSET FLAT:.LC0
mov QWORD PTR [rdi 16], OFFSET FLAT:.LC1
mov QWORD PTR [rdi 24], OFFSET FLAT:.LC2
ret
main:
mov eax, 0
ret
uj5u.com熱心網友回復:
我認為對您問題的簡短回答(至少對于 C,我不熟悉 GO 內部結構)是 C 函式按值傳遞,通常也按值回傳,因此必須復制資料物件,人們擔心所有的性能復制。對于大型物件或深度復雜的物件(包含指向其他內容的指標),將被復制的值作為指標通常更有效或合乎邏輯,因此函式可以對資料進行“操作”而無需復制它. 話雖如此,現代編譯器在確定引數資料是否適合暫存器或有效復制回傳的結構等方面非常聰明。底線是現代 C 代碼做對你的應用程式最好的或者對你來說最清楚的。如果至少在開始時降低了可讀性,請避免過早優化。還有編譯器資源管理器(https://godbolt.org/ ) 是您的朋友,如果您想檢查不同樣式的效果,尤其是在優化方面。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/375257.html
