本文译自(Go 没有传引用)版权@归原文所有.
要清楚的是, Go 没有引用变量, 所以 Go 没有传递引用函数的调用语义.
什么是引用变量(reference variable) ?
在像 C++ 这样的语言中, 你可以声明一个别名, 或者一个现有变量的替代名称. 这被称为引用变量.
#include <stdio.h>
int main() {
int a = 10;
int &b = a;
int &c = b;
printf("%p %p %p\n", &a, &b, &c); // 0x7ffe114f0b14 0x7ffe114f0b14 0x7ffe114f0b14
return 0;
}
你可以看到 a, b 和 c 都指向相同的内存位置. 写入 a 会改变 b 和 c 的内容. 当你想在函数调用不同的作用域声明引用变量时, 这是很有用的.
Go 没有引用变量
与 C++ 不同, Go 程序中定义的每个变量都占用一个唯一的内存位置.
package main
import "fmt"
func main() {
var a, b, c int
fmt.Println(&a, &b, &c) // 0x1040a124 0x1040a128 0x1040a12c
}
创建一个两个变量在内存中共享相同的存储位置的 Go 程序是不可能的. 可以创建两个变量, 其内容指向相同的存储位置, 但与共享相同存储位置的两个变量不同.
package main
import "fmt"
func main() {
var a int
var b, c = &a, &a
fmt.Println(b, c) // 0x1040a124 0x1040a124
fmt.Println(&b, &c) // 0x1040c108 0x1040c110
}
在这个例子中, b 和 c 保持相同的值, 即 a 的地址. 但是, b 和 c 本身存储在不同的位置. 更新 b 的内容对 c 没有影响.
但是 map 和 channel 是引用吗 ?
错, map 和 channel 不是引用. 如果他们是, 那么下面这个程序将打印 false .
package main
import "fmt"
func fn(m map[int]int) {
m = make(map[int]int)
}
func main() {
var m map[int]int
fn(m)
fmt.Println(m == nil)
}
如果map m 是一个 C++ 风格的引用变量, 则在 main 中声明的 m 和 在 fn 中声明的 m 将在内存中占据相同的存储位置. 但是, 因为对 fn 内的 m 的赋值对 m 的值没有影响, 所以我们可以看到 m 不是引用变量.
结论
Go 没有传递引用语义, 因为 Go 没有引用变量.