Qingyuan original, aprendiendo del pasado, Go lang1.18 tutorial introductorio de refinación, de Bai Ding a Hongru, ya sea para pasar tipos de referencia en Golang EP18

¡Trabajar juntos para crear y crecer juntos! Este es el día 28 de mi participación en el "Nuevo plan diario de Nuggets · Desafío de actualización de agosto", haga clic para ver los detalles del evento

Al principio, nunca ha habido el llamado "paso por referencia" en Go lang, y siempre ha habido una sola forma de pasar variables, que es pasar por valor. Porque la premisa de pasar por referencia es la existencia de una "variable de referencia", pero la llamada "variable de referencia" nunca ha aparecido en Go lang, por lo que es imposible pasar una variable por referencia.

tipo de referencia

En primer lugar, los tipos de datos básicos de Go lang son tipos de valor, como enteros, flotantes, cadenas, booleanos, matrices y tipos de error. Son esencialmente tipos primitivos, es decir, inmutables, por lo que las operaciones en ellos generalmente devuelven A recién valor creado, por lo que cuando pasa estos valores a una función, en realidad está pasando una copia del valor, lo que básicamente no es controvertido.

El tipo de referencia se refiere a su modificación, la acción puede afectar a cualquier referencia a su variable. En el lenguaje Go, los tipos de referencia son segmento, diccionario (mapa), interfaz (interfaz), función (func) y canal (chan).

El problema es que, si modificamos los datos de tipo de referencia definidos externamente en el cuerpo de una función:

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)  
}
复制代码

El programa devuelve:

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

Obviamente, la función changeMap cambia el valor del tipo de diccionario externo, por lo que podemos concluir que el parámetro de tipo de referencia se pasa por referencia.

Variables de referencia y paso por referencia

De hecho, las variables de referencia y el paso por referencia existen, pero solo en otros lenguajes, como Python:

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

Aquí definimos un tipo de dato variable: enumere a, y luego páselo al cambio de función para realizar la operación de modificación, y al mismo tiempo use el método id() incorporado del sistema para imprimir el valor y la dirección de memoria antes modificación, así como el valor modificado y la dirección de memoria, el programa devuelve:

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”。

En esencia, podemos entender que el diccionario se pasa como un parámetro por valor, pero el tipo de referencia pasa un puntero a los datos subyacentes, por lo que cuando operamos, podemos modificar el valor de los datos subyacentes compartidos, lo que afecta a todas las referencias. a esta variable que comparte los datos subyacentes, razón por la cual las operaciones de diccionario dentro de las funciones pueden afectar el objeto original.

Epílogo

La razón por la que se puede hacer referencia a un tipo de referencia es porque creamos una variable de un tipo de referencia, que en realidad es un valor de encabezado. El valor del encabezado contiene un puntero a la estructura de datos subyacente. Cuando pasamos un tipo de referencia en una función, realmente pasa Es una copia de este valor de encabezado, y la estructura subyacente a la que apunta no se copia ni pasa, por lo que el paso de tipo de referencia es eficiente. En otras palabras, Go lang introdujo el concepto de punteros para garantizar la pureza. de paso de valor Si hay variables de referencia y paso de referencia en Go lang, ¿no son simplemente superfluos los punteros?

Supongo que te gusta

Origin juejin.im/post/7136851067584643086
Recomendado
Clasificación