Qingyuan original, learning from the past, Go lang1.18 introductory refining tutorial, from Bai Ding to Hongru, whether to pass reference types in Golang EP18

Work together to create and grow together! This is the 28th day of my participation in the "Nuggets Daily New Plan · August Update Challenge", click to view the event details

At the beginning, there has never been a so-called "pass by reference" in Go lang, and there has always been only one way of passing variables, which is pass by value. Because the premise of passing by reference is the existence of a "reference variable", but the so-called "reference variable" has never appeared in Go lang, so it is impossible to pass a variable by reference.

reference type

First of all, the basic data types of Go lang are value types, such as integers, floats, strings, booleans, arrays, and error types. They are essentially primitive types, that is, immutable, so operations on them generally return A newly created value, so when you pass these values ​​to a function, you are actually passing a copy of the value, which is basically not controversial.

The reference type refers to its modification action can affect any reference to its variable. In the Go language, reference types are slice, dictionary (map), interface (interface), function (func) and channel (chan).

The problem is, if we modify the externally defined reference type data in a function body:

package main  
  
import "fmt"  
  
func changeMap(data map[string]string) {  
	data["123"] = "333"  
}  
  
func main() {  
	a := map[string]string{}  
	a["123"] = "123"  
	fmt.Println("begin:", a)  
	changeMap(a)  
	fmt.Println("after:", a)  
}
复制代码

The program returns:

begin: map[123:123]  
after: map[123:333]
复制代码

Obviously, the function changeMap changes the value of the external dictionary type, so we can conclude that the reference type parameter is passed by reference?

Reference variables and pass-by-reference

In fact, reference variables and pass-by-reference do exist, but only in other languages, such as Python:

a = [2]  
print(id(a))  
  
def change(a):  
    print(id(a))  
    a.append(1)  
  
  
if __name__ == '__main__':  
  
    print(a)  
  
    change(a)  
  
    print(a)
复制代码

Here we define a variable data type: list a, and then pass it into the function change to perform the modification operation, and at the same time use the built-in id() method of the system to print the value and memory address before modification, as well as the modified value and memory address, the program returns:

4311179392  
[2]  
4311179392  
[2, 1]
复制代码

这说明什么?说明变量a是引用变量(reference variable),同时它作为参数的传递方式是引用传递(pass-by-reference),证据就是它原始的内存地址和传递到函数内的内存地址是一致的,都是4311179392。

所以引用变量和引用传递应该具备如下特点:引用变量和原变量的内存地址一样。就像上面的例子里函数内引用变量a和原变量a的内存地址相同。函数使用引用传递,可以改变外部实参的值。就像上面的例子里,change函数使用了引用传递,改变了外部实参a的值。

Golang是否存在引用变量(reference variable)

Go lang中不存在引用变量:

package main  
  
import "fmt"  
  
func main() {  
	a := 1  
	var a1 *int = &a  
	var a2 *int = &a  
	fmt.Println("值", a1, " 内存地址:", &a1)  
	fmt.Println("值:", a2, " 内存地址:", &a2)  
}
复制代码

程序返回:

值 0x140000140b8  内存地址: 0x1400000e028  
值: 0x140000140b8  内存地址: 0x1400000e030
复制代码

和Python不同的是,在Go lang里,不可能有两个变量有相同的内存地址,所以也就不存在引用变量了。变量a1和a2的值相同,都指向变量a的内存地址,但是变量a1和a2自己本身的内存地址是不一样的,而Python里的引用变量和原变量的内存地址是相同的。

因此,在Go语言里是不存在引用变量的,也就自然没有引用传递了。

字典为什么可以做到值传递但是可以更改原对象?

因为字典虽然名字叫做字典,或者叫做map,但那并不重要,其实它是指针:

package main  
  
import (  
	"fmt"  
	"unsafe"  
)  
  
func main() {  
	data := make(map[string]int)  
	var p uintptr  
	fmt.Println("字典大小:", unsafe.Sizeof(data))  
	fmt.Println("指针大小:", unsafe.Sizeof(p))  
}
复制代码

程序返回:

字典大小: 8  
指针大小: 8
复制代码

从占据内存空间大小就可以看出,字典和指针其实就是一种东西,那如果字典是指针,那make返回的不应该是*map[string]int吗?为什么我们使用字典传实参,从来都不加*?

在Go lang早期,的确对于字典是使用过指针形式的,但是最后Golang的设计者发现,几乎没有人使用字典不加指针,因此就直接去掉了形式上的指针符号*,类比的话,我们会发现现实中几乎从来就没有人管AC米兰叫AC米兰,都是直呼米兰,因为大家都认为米兰就是AC米兰,所以都自动省略了形式上的“AC”。

In essence, we can understand that the dictionary is passed as a parameter by value, but the reference type passes a pointer to the underlying data, so when we operate, we can modify the value of the shared underlying data, which affects all references. to this variable that shares the underlying data, which is why dictionary operations within functions can affect the original object.

Epilogue

The reason why a reference type can be referenced is because we create a variable of a reference type, which is actually a header value. The header value contains a pointer to the underlying data structure. When we pass a reference type in a function, it actually passes It is a copy of this header value, and the underlying structure it points to is not copied and passed, which is why reference type passing is efficient. In other words, Go lang introduced the concept of pointers in order to ensure the purity of value passing. If there are reference variables and reference passing in Go lang, aren't pointers just superfluous?

Guess you like

Origin juejin.im/post/7136851067584643086