提问人:N P 提问时间:11/15/2017 最后编辑:ruakhN P 更新时间:7/11/2023 访问量:51819
在 Go 函数中按引用和值传递
Passing by reference and value in Go to functions
问:
我对在 Go 中传递引用和值有点困惑。
我已经看到过对类型前面的 * 的解释。
* 在类型名称的前面,表示声明的变量将存储该类型的另一个变量的地址(而不是该变量的值) 类型)。
这对我来说没有意义。
在 Java 中,如果我将数据库实例传递到函数中,我会这样做
databaseFunction(DatabaseType db) {
// do something
}
但是,在我拥有的 go 示例中,它是这样传递的。
func PutTasks(db *sql.DB) echo.HandlerFunc {
}
为什么我们需要在类型前面加上星号?
根据这张备忘单,我找到了。
func PrintPerson(p *Person) 仅接收指针地址 (参考资料)
我不明白为什么我只想发送指针地址作为参数。
答:
引用语义的目的是允许函数在其自身范围之外操作数据。比较:
func BrokenSwap(a int, b int) {
a, b = b, a
}
func RealSwap(a *int, b *int) {
*a, *b = *b, *a
}
调用 时,没有任何效果,因为该函数接收并操作数据的私有副本。相反,当您调用时,您实际上交换了调用方的 和 的值。在调用站点上显式获取变量的地址会通知读者这些变量可能已发生变异。BrokenSwap(x, y)
RealSwap(&x, &y)
x
y
首先,从技术上讲,Go 只有传递值。将指针传递到对象时,将按值传递指针,而不是按引用传递对象。这种差异是微妙的,但偶尔是相关的。例如,您可以覆盖对调用方没有影响的指针值,而不是取消引用它并覆盖它指向的内存。
// *int means you *must* pass a *int (pointer to int), NOT just an int!
func someFunc(x *int) {
*x = 2 // Whatever variable caller passed in will now be 2
y := 7
x = &y // has no impact on the caller because we overwrote the pointer value!
}
至于你的问题“为什么我们需要在类型前面加上星号?”:星号表示该值的类型指针指向 ,而不是 类型的值。这些是不可互换的!sql.DB
sql.DB
为什么要发送指针地址?这样,您就可以在函数的调用方和函数体之间共享值,并在函数内部所做的更改反映在调用方中(例如,指针是“setter”方法可以对对象工作的唯一方式)。这实际上也是你的 Java 代码正在做的事情;在 Java 中,您总是通过引用(指针)访问对象,因此 Java 会自动执行此操作,而不是让您显式指示它。但是在 Go 中,您也可以不通过指针访问对象,因此您必须显式。如果调用函数并直接传入对象,则该函数将获得该对象的副本,如果该函数修改该对象,则调用方将看不到这些更改。因此,如果希望更改在函数外部传播,则必须传递指针。这样,指针将被复制,但它指向的对象将被共享。
另请参阅:指针的 Go 导览部分、指针的 Go 规范部分、地址运算符的 Go 规范部分
评论
参考传递 :- 当您将相同的变量传递到不同名称的函数中时。
下面是C++的例子(因为Go没有这个概念),其中a和a1是相同的变量。
void swap(int& a1, int& b1)
{
int tmp = a1;
a1 = b1;
b1 = tmp;
}
int main()
{
int a = 10, b = 20;
swap(a, b);
cout << "a " << a << " b " << b ;
}
Go 将所有内容作为数据传递(意味着它将数据从当前活动帧复制到新函数的新活动帧)。因此,如果您传递值,它会复制该值,并且优点是不会意外修改。当它传递变量的地址时,它也会复制到新的指针变量中,但由于指针的大小较小,因此具有效率优势。
评论