Accede a todo EDteam con un único pago¡Sube a premium!

¿Qué son los punteros en Go?

Un puntero en Go es una variable que nos permite acceder a la dirección en memoria de otra variable.

Diseño web
6 minutos
Hace 6 años
¿Qué son los punteros en Go?

Un puntero en Go es una variable que nos permite acceder a la dirección en memoria de otra variable.

¿Para qué nos sirven los punteros?

Los punteros son muy útiles en los casos en los que queremos pasar una variable como argumento a una función para que su valor sea modificado, lo que se conoce como pasar valores por referencia a una función.

Cuando creamos una función y le pasamos una variable como argumento, lo que hace la función es hacer una copia del valor de la variable y trabajar con ese valor, por lo que la variable que pasamos como argumento no se modifica.

Veamos un ejemplo para entender esta situación.

1package main 2 3import ( 4 "fmt" 5) 6 7func Increase(v int) { 8 v++ 9} 10 11func main() { 12 var v int = 19 13 Increase(v) 14 fmt.Println("El valor de v es:", v) 15} 16

Este código produce el siguiente resultado.

El valor de v es: 19 

Podemos ver entonces que el valor de la variable v no se incrementa y sigue siendo 19, ya que la función Increase hace una copia de la variable v y realiza el incremento sobre la copia y no sobre la variable.

A continuación veremos algunos conceptos que debemos entender para trabajar con punteros, y al finalizar refactorizaremos el código anterior para lograr incrementar el valor de la variable v.

¿Cómo creamos un puntero?

Como Go es un lenguaje estáticamente tipado las variables puntero tienen que ser de un tipo de dato especifico.

Para crear un puntero utilizamos el operador \* antes del tipo de dato que necesitamos almacenar en esa dirección de memoria.

1var p *int 2

Adicionalmente, Go nos proporciona dos formas más para crear punteros:

  • Mediante la función new() que recibe como argumento un tipo de dato.
  • Y a través del del short hand de declaración de variables :=.

A continuación veremos un ejemplo de su uso:

1package main 2 3import ( 4 "fmt" 5) 6 7func main() { 8 v := 19 9 10 var p1 *int 11 var p2 = new(int) 12 p3 := &v 13 14 // %T nos permite imprimir el tipo de dato de la variable 15 fmt.Printf("p1: %T \n", p1) 16 fmt.Printf("p2: %T \n", p2) 17 fmt.Printf("p3: %T \n", p3) 18} 19

Este código produce el siguiente resultado.

p1: *int 
p2: *int 
p3: *int

Podemos ver entonces que el tipo de dato de las variables p1, p2 y p3 es un puntero de tipo entero \*int.

El operador de dirección &

Para obtener la referencia o dirección de memoria de una variable, debemos anteponer a la variable el operador de dirección &.

Veamos un ejemplo:

1package main 2 3import ( 4 "fmt" 5) 6 7func main() { 8 var v int = 19 9 fmt.Println("La dirección de memoria de v es: ", &v) 10} 11

Este código produce el siguiente resultado.

La dirección de memoria de v es:  0x10414020

En este ejemplo, podemos ver que la variable v de tipo entero tiene el valor 19 y se almacena en la dirección de memoria 0x10414020.

El operador de desreferenciación *

Desreferenciar un puntero es obtener el valor que esta almacenado en la dirección de memoria a donde hace referencia el puntero, para hacerlo debemos anteponer el operador \* a la variable puntero.

Veamos un ejemplo:

1package main 2 3import ( 4 "fmt" 5) 6 7func main() { 8 var v int = 19 9 var p *int 10 11 // Hacemos que el puntero p, referencie la dirección 12 // de memoria de la variable v. 13 p = &v 14 15 fmt.Printf("La variable v es: %d \n", v) 16 fmt.Printf("La dirección de memoria de v es: %v \n", &v) 17 fmt.Printf("El puntero p referencia a la dirección de memoria: %v \n", p) 18 fmt.Printf("Al desrefenciar el puntero p obtengo el valor: %d \n", *p) 19} 20

Este código produce el siguiente resultado.

La variable v es: 19 
La dirección de memoria de v es: 0x10414020 
El puntero p referencia a la dirección de memoria: 0x10414020 
Al desrefenciar el puntero p obtengo el valor: 19

Podemos ver que el puntero p referencia a la dirección de memoria 0x10414020 , y para obtener el valor 19 almacenado en esta dirección, hacemos uso del operador de desreferenciación \*p.

Incrementemos la variable v

Refactoricemos la función Increase con el uso de punteros:

1package main 2 3import ( 4 "fmt" 5) 6 7// Increase recibe un puntero de tipo entero 8func Increase(v *int) { 9 // Desreferenciamos la variable v para obtener 10 // su valor e incrementarlo en 1 11 *v++ 12} 13 14func main() { 15 var v int = 19 16 17 // La función Increase recibe un puntero 18 // utilizamos el operador de dirección & 19 // para pasar la dirección de memoria de v 20 Increase(&v) 21 22 fmt.Println("El valor de v ahora vale:", v) 23} 24

Este código produce el siguiente resultado.

El valor de v ahora vale: 20 

Y, ¿Cómo utilizo punteros en estructuras?

El uso de punteros en estructuras maneja los mismos conceptos que hemos aprendido, veamos su aplicación a través del siguiente ejemplo:

Crearemos una estructura llamada User y utilizaremos una función llamada UpdateStatus para actualizar el estado del usuario, así que esta función recibirá un puntero de tipo User y actualizará el campo IsActive.

Veamos el código:

1package main 2 3import ( 4 "fmt" 5) 6 7type User struct { 8 FirstName string 9 LastName string 10 Email string 11 IsActive bool 12} 13 14func UpdateStatus(u *User) { 15 (*u).IsActive = !(*u).IsActive 16} 17 18func main() { 19 20 u := User{ 21 FirstName: "Alejandro", 22 LastName: "Rodríguez", 23 Email: "aj@ed.team", 24 IsActive: false, 25 } 26 27 fmt.Println("User antes de actualización: ", u) 28 29 UpdateStatus(&u) 30 31 fmt.Println("User después de actualización: ", u) 32} 33

Este código produce el siguiente resultado.

User antes de actualización:  {Alejandro Rodríguez aj@ed.team false}
User después de actualización:  {Alejandro Rodríguez aj@ed.team true}

La función UpdateStatus recibe un puntero de tipo User, lo que nos permite acceder a la dirección de memoria en donde se encuentra almacenada la estructura, para modificar el valor del campo IsActive desreferenciamos el puntero con (\*u).IsActive y le asignamos el mismo valor con el operador de negación !, así invertimos el estado del usuario.

Cuando estamos trabajando con punteros de estructura, Go nos permite trabajar sin el operador de desreferenciación \*, con esto logramos una sintaxis más limpia, así que la función anterior podríamos refactorizarla como:

1func UpdateStatus(u *User) { 2 u.IsActive = !u.IsActive 3} 4

Espero puedas poner en práctica esta información, y así mejorar tus habilidades en Go.

Comentarios de los usuarios

Pregunta a ChatEDT