Go 没有传引用

本文译自(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 没有引用变量.

comments powered by Disqus