在 C 和 Go 中传递指针而不是返回新变量?

为什么在 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)

}

做其中一项是否有任何性能优势或限制?


哆啦的时光机
浏览 108回答 2
2回答

富国沪深

首先,我对 Go 的了解不够,无法对其做出判断,但答案将适用于 C 的情况。如果您只是在研究像ints 这样的原始类型,那么我会说这两种技术之间没有性能差异。当structs 发挥作用时,通过指针修改变量有一个非常小的优势(纯粹基于您在代码中所做的事情)#include <stdio.h>struct Person {&nbsp; &nbsp; int age;&nbsp; &nbsp; const char *name;&nbsp; &nbsp; const char *address;&nbsp; &nbsp; const char *occupation;};struct Person getReturnedPerson() {&nbsp; &nbsp; struct Person thePerson = {26, "Chad", "123 Someplace St.", "Software Engineer"};&nbsp; &nbsp; return thePerson;}void changeExistingPerson(struct Person *thePerson) {&nbsp; &nbsp; thePerson->age = 26;&nbsp; &nbsp; thePerson->name = "Chad";&nbsp; &nbsp; thePerson->address = "123 Someplace St.";&nbsp; &nbsp; thePerson->occupation = "Software Engineer";}int main(void) {&nbsp; struct Person someGuy = getReturnedPerson();&nbsp;&nbsp;&nbsp; struct Person theSameDude;&nbsp; changeExistingPerson(&theSameDude);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;}GCC x86-64 11.2没有优化通过函数的 return返回struct变量比较慢,因为必须通过分配所需的值来“构建”变量,然后将变量复制到返回值。当您通过指针间接修改变量时,除了将所需的值写入内存地址(基于您传入的指针)之外,别无他法.LC0:&nbsp; &nbsp; &nbsp; &nbsp; .string "Chad".LC1:&nbsp; &nbsp; &nbsp; &nbsp; .string "123 Someplace St.".LC2:&nbsp; &nbsp; &nbsp; &nbsp; .string "Software Engineer"getReturnedPerson:&nbsp; &nbsp; &nbsp; &nbsp; push&nbsp; &nbsp; rbp&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rbp, rsp&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rbp-40], rdi&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;DWORD PTR [rbp-32], 26&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rbp-24], OFFSET FLAT:.LC0&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rbp-16], OFFSET FLAT:.LC1&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rbp-8], OFFSET FLAT:.LC2&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rcx, QWORD PTR [rbp-40]&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rax, QWORD PTR [rbp-32]&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rdx, QWORD PTR [rbp-24]&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rcx], rax&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rcx+8], rdx&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rax, QWORD PTR [rbp-16]&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rdx, QWORD PTR [rbp-8]&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rcx+16], rax&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rcx+24], rdx&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rax, QWORD PTR [rbp-40]&nbsp; &nbsp; &nbsp; &nbsp; pop&nbsp; &nbsp; &nbsp;rbp&nbsp; &nbsp; &nbsp; &nbsp; retchangeExistingPerson:&nbsp; &nbsp; &nbsp; &nbsp; push&nbsp; &nbsp; rbp&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rbp, rsp&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rbp-8], rdi&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rax, QWORD PTR [rbp-8]&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;DWORD PTR [rax], 26&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rax, QWORD PTR [rbp-8]&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rax+8], OFFSET FLAT:.LC0&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rax, QWORD PTR [rbp-8]&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rax+16], OFFSET FLAT:.LC1&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rax, QWORD PTR [rbp-8]&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rax+24], OFFSET FLAT:.LC2&nbsp; &nbsp; &nbsp; &nbsp; nop&nbsp; &nbsp; &nbsp; &nbsp; pop&nbsp; &nbsp; &nbsp;rbp&nbsp; &nbsp; &nbsp; &nbsp; retmain:&nbsp; &nbsp; &nbsp; &nbsp; push&nbsp; &nbsp; rbp&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rbp, rsp&nbsp; &nbsp; &nbsp; &nbsp; sub&nbsp; &nbsp; &nbsp;rsp, 64&nbsp; &nbsp; &nbsp; &nbsp; lea&nbsp; &nbsp; &nbsp;rax, [rbp-32]&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rdi, rax&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;eax, 0&nbsp; &nbsp; &nbsp; &nbsp; call&nbsp; &nbsp; getReturnedPerson&nbsp; &nbsp; &nbsp; &nbsp; lea&nbsp; &nbsp; &nbsp;rax, [rbp-64]&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rdi, rax&nbsp; &nbsp; &nbsp; &nbsp; call&nbsp; &nbsp; changeExistingPerson&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;eax, 0&nbsp; &nbsp; &nbsp; &nbsp; leave&nbsp; &nbsp; &nbsp; &nbsp; ret轻微优化但是,今天的大多数编译器都可以弄清楚您在这里尝试做什么,并将平衡两种技术之间的性能。如果你想绝对小气,传递指针仍然会稍微快几个时钟周期。在从函数返回一个变量时,你至少还需要设置返回值的地址。&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rax, rdi但是在传递指针时,甚至没有这样做。但除此之外,这两种技术没有性能差异。.LC0:&nbsp; &nbsp; &nbsp; &nbsp; .string "Chad".LC1:&nbsp; &nbsp; &nbsp; &nbsp; .string "123 Someplace St.".LC2:&nbsp; &nbsp; &nbsp; &nbsp; .string "Software Engineer"getReturnedPerson:&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;rax, rdi&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;DWORD PTR [rdi], 26&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rdi+8], OFFSET FLAT:.LC0&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rdi+16], OFFSET FLAT:.LC1&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rdi+24], OFFSET FLAT:.LC2&nbsp; &nbsp; &nbsp; &nbsp; retchangeExistingPerson:&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;DWORD PTR [rdi], 26&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rdi+8], OFFSET FLAT:.LC0&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rdi+16], OFFSET FLAT:.LC1&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;QWORD PTR [rdi+24], OFFSET FLAT:.LC2&nbsp; &nbsp; &nbsp; &nbsp; retmain:&nbsp; &nbsp; &nbsp; &nbsp; mov&nbsp; &nbsp; &nbsp;eax, 0&nbsp; &nbsp; &nbsp; &nbsp; ret

繁华开满天机

我认为对您的问题的简短回答(至少对于 C,我不熟悉 GO 内部)是 C 函数是按值传递的,通常也按值返回,因此必须复制数据对象,人们担心所有的性能复制。对于大型对象或深度复杂的对象(包含指向其他内容的指针),将被复制的值作为指针通常更有效或更合乎逻辑,因此函数可以“操作”数据而无需复制它.&nbsp;话虽如此,现代编译器在确定参数数据是否适合寄存器或有效复制返回的结构等内容方面非常聪明。底线是现代 C 代码做最适合您的应用程序或对您来说最清楚的事情。如果至少在开始时会降低可读性,请避免过早优化。还有编译器资源管理器(https://godbolt.org/)是你的朋友,如果你想检查不同风格的效果,特别是在优化方面。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go