当我们在Go语言中使用map和channel时,你可能会注意到,将这些类型作为函数参数传递时,并不需要在形参中为map和channel添加指针符号*,依然能在函数体内改变外部map和channel的值。

这种现象可能会让一些人联想到C++中的引用变量和引用传递。比如,考虑以下Go代码片段:

  1. package main
  2. import "fmt"
  3. func changeMap(data map[string]interface{}) {
  4. data["c"] = 3
  5. }
  6. func main() {
  7. counter := map[string]interface{}{"a": 1, "b": 2}
  8. fmt.Println("起始状态:", counter)
  9. changeMap(counter)
  10. fmt.Println("修改后:", counter)
  11. }

运行结果展示了函数changeMap成功地修改了外部map类型counter的值:

  1. 起始状态: map[a:1 b:2]
  2. 修改后: map[a:1 b:2 c:3]

那么,这是否意味着Go中的map传参采用了引用传递呢?在探究这一问题之前,让我们先回顾一下什么是引用变量和引用传递。

什么是引用变量和引用传递

引用变量和引用传递是C中的两个基本概念。以下是一个简单的C例子,展示了引用变量和引用传递:

  1. #include <iostream>
  2. using namespace std;
  3. void changeValue(int &n) {
  4. n = 2;
  5. }
  6. int main() {
  7. int a = 1;
  8. int &b = a; // b是a的引用变量
  9. cout << "a=" << a << " 地址:" << &a << endl;
  10. cout << "b=" << b << " 地址:" << &b << endl;
  11. changeValue(a);
  12. cout << "修改后 a=" << a << " 地址:" << &a << endl;
  13. cout << "b=" << b << " 地址:" << &b << endl;
  14. }

从运行结果来看,变量b作为引用变量,与原变量a共享相同的地址。当通过引用传递改变了a的值时,b的值也随之改变。

Go有引用变量和引用传递吗?

答案是否定的。在Go语言中,不存在引用变量和引用传递的概念。

在Go语言中,不存在两个变量共享相同内存地址的情况,但是两个变量可以指向同一内存地址,这是两个不同的概念。例如:

  1. package main
  2. import "fmt"
  3. func main() {
  4. a := 10
  5. var p1 *int = &a
  6. var p2 *int = &a
  7. fmt.Println("p1 指向:", p1, " 地址:", &p1)
  8. fmt.Println("p2 指向:", p2, " 地址:", &p2)
  9. }

在这个Go代码中,虽然p1和p2变量的值(指针)相同,都指向变量a的地址,但它们自身的地址是不同的。

有map不是使用引用传递的反例吗?

请看以下代码:

  1. package main
  2. import "fmt"
  3. func initMap(data map[string]int) {
  4. data = make(map[string]int)
  5. fmt.Println("函数内部, data == nil:", data == nil)
  6. }
  7. func main() {
  8. var data map[string]int
  9. fmt.Println("初始化前, data == nil:", data == nil)
  10. initMap(data)
  11. fmt.Println("初始化后, data == nil:", data == nil)
  12. }

这个例子显示,即使initMap函数在内部对data变量进行了重新赋值,这对外部的data变量并无影响,进一步证明了Go语言中不存在引用传递。

那map和channel是如何工作的呢?

事实上,Go语言中的map和channel本质上都是指针,指向Go运行时(runtime)的具体结构。当我们使用make函数初始化这些类型时,Go编译器会将其转换为对应的运行时函数调用,实际返回的是一个指向运行时数据结构的指针。

因此,虽然Go语言中不存在引用变量和引用传递,但对map和channel的操作可以影响到这些数据结构的状态,因为它们本质上是通过指针间接访问的。

总结

在Go语言中,所有的传递都是按值传递。map和channel虽然在行为上类似于引用传递,但实际上是因为它们本质上是指针,指向Go运行时的特定结构。此外,Go语言确实没有引用变量和引用传递的概念,这一点与C++等语言有所不同。

参考链接