Tutorial de punteros en Go
Tags: spanish, tutorial, go
Published: 2023-04-24 13:09:03

Este artículo está basado en el vídeo de la semana pasada sobre punteros en Go, aquí lo tendréis explicado con texto y acceso al código fuente(Así no tenéis que copiar de la pantalla, que os conozco :D). Si os resulta útil podéis darle a like, compartir, comentar, etc. 

{% embed https://www.youtube.com/watch?v=NZZ_Yrdd-2U %}

## Introducción a los punteros

Los punteros, a diferencia de las variables donde el compilador reserva una posición de memoria para almacenar información, estos no tienen una posición de memoria reservada y pueden apuntar a la posición de memoria que les indiquemos.

Por ejemplo, en el siguiente ejemplo declaramos una variable llamada 'v' con el valor '4', seguidamente imprimimos por pantalla el valor de la variable y su posición de memoria(Con el símbolo '&' accedemos a la posición de memoria de una variable). Después, tenemos declarado el puntero 'p' al que apuntamos a la posición de memoria de la variable 'v' y le asignamos otro valor(Con el símbolo '*' accedemos al valor de la posición de memoria de un puntero).

```
var v int = 4
var p *int

fmt.Printf( "El valor de v es %d \n", v )
fmt.Printf( "La posición de memória de v es %v \n", &v )

p = &v
*p = 8

fmt.Printf( "El valor de p es %d \n", *p )
fmt.Printf( "La posición de memória de p es %v \n", p )
```

## Paso de parámetros por valor o referencia

Una de las aplicaciones del uso de punteros es pasar variables a las funciones por referencia. Cuando pasamos un parámetro a una función por 'valor' el compilador lo que hace es reservar una posición de memoria para ese parámetro y copiarle el valor. Si lo hacemos por 'referencia', es decir, usando punteros estamos usando la misma posición de memoria y, por lo tanto, es mucho más rápido.

Hemos hecho un ejemplo de esto en el vídeo:

```
package main

import "fmt"

func main() {
	var v int = 4
	var p *int

	p = &v
	*p = 8

	IncValor( v )
	IncReferencia( &v )

	fmt.Printf( "El valor de v es %d \n", v )
	fmt.Printf( "La posición de memória de v es %v \n", &v )

        fmt.Printf( "El valor de p es %d \n", *p )
        fmt.Printf( "La posición de memória de p es %v \n", p )
}

func IncValor( v int  ) {
	v++
	fmt.Printf( "El valor incrementado(Por valor) es %d \n", v )
	}

func IncReferencia( v *int ) {
	*v++
	fmt.Printf( "El valor incrementado(Por referencia) es %d \n", *v )
	}
```

## Listas con punteros

Otra aplicación de los punteros donde se demuestra su potencia es creando estructuras de datos. En el vídeo hemos creado un CRUD(Create-Read-Update-Delete) de clientes:

```
package main

import "fmt"

type Cliente struct {
	nombre string
	telefono string
	next *Cliente
}

var primerCliente *Cliente

func main() {
	AddCliente( "Pepe", "555 555 555" )
        AddCliente( "Juan", "555 555 556" )
        AddCliente( "Rodrigo", "555 555 557" )
        AddCliente( "David", "555 555 558" )

	ListaClientes()

	var cliente *Cliente = GetCliente(2)
	(*cliente).nombre = "Albert"

	fmt.Printf( "\nClientes Update\n" )
	ListaClientes()

	DeleteCliente( 2 )
	fmt.Printf( "\nClientes Delete\n" )
        ListaClientes()
	}

func AddCliente(nombre string, telefono string) {
	var cliente = Cliente{
		nombre:   nombre,
		telefono: telefono,
		next:     nil,
	}

	if primerCliente == nil {
		primerCliente = &cliente

		return
	}

	var nextCliente *Cliente = primerCliente
	for nextCliente.next != nil {
		nextCliente = nextCliente.next
	}

	nextCliente.next = &cliente
}

func ListaClientes() {
	if primerCliente == nil {
		return
	}

	var nextCliente *Cliente = primerCliente
	for nextCliente.next != nil {
		fmt.Printf("Nombre: %s Teléfono: %s \n", nextCliente.nombre, nextCliente.telefono)

		nextCliente = nextCliente.next
	}

	fmt.Printf("Nombre: %s Teléfono: %s \n", nextCliente.nombre, nextCliente.telefono)
}

func GetCliente(pos int) *Cliente {
	if primerCliente == nil {
		return nil
	}

	var nextCliente *Cliente = primerCliente
	i := 0
	for i < pos && nextCliente.next != nil {
		nextCliente = nextCliente.next
		i++
	}

	if i != pos {
		return nil
	}

	return nextCliente
}

func DeleteCliente(pos int) {
	if primerCliente == nil {
		return
	}

	if pos == 0 {
		if primerCliente.next == nil {
			primerCliente = nil

			return
		}

		primerCliente = primerCliente.next
		return
	}

	var cliente *Cliente = GetCliente(pos)
	var previo *Cliente = GetCliente(pos - 1)
	if cliente == nil || previo == nil {
		return
	}

	previo.next = cliente.next
}
```

En el ejemplo, hemos creado una estructura para almacenar clientes donde también tenemos un puntero al siguiente cliente. De esta manera podemos recorrer los clientes desde el primero hasta que encontremos el último(El que el puntero 'next' tiene valor None) y para eliminar uno de la lista simplemente hay que quitarlo de la cadena(En el caso de Go, a diferencia de C, no hace falta liberar la memoria porque lo hace solo con el recolector de basura).

Podemos optimizar el ejemplo para distintos casos de uso: Con dos punteros(Uno para next y otro para prev y así recorrer la lista en dos direcciones), con otro puntero que apunte al final de la lista, con otro puntero en la mitad de la lista, con punteros para ordenar los registros usando un índice, etc.

En fin, espero que hayáis disfrutado del contenido y os haya resultado útil. La mejor manera de colaborar es dándole a like, comentar y compartir, a ver si hacemos que esta red social se llene de contenido hispano hablante...