Punteros en Go: & y * sin aritmética de punteros

Go tiene punteros, pero los simplifica drásticamente respecto a C: no hay aritmética de punteros, no hay punteros a punteros en el uso cotidiano y el recolector de basura gestiona la memoria. Lo que sí necesitas entender es cuándo pasar un valor por copia y cuándo por referencia, porque esa decisión afecta tanto al comportamiento de tu programa como a los métodos que un tipo puede satisfacer.

Los operadores & y *

& obtiene la dirección de memoria de una variable. * desreferencia un puntero para acceder al valor que apunta:

package main

import "fmt"

func main() {
    x := 42
    p := &x          // p es un *int que apunta a x
    fmt.Println(p)   // 0xc0000b4010 (dirección, varía)
    fmt.Println(*p)  // 42

    *p = 100         // modifica x a través del puntero
    fmt.Println(x)   // 100
}

Pasar por valor vs pasar por puntero

Por defecto, Go pasa argumentos por valor: la función recibe una copia. Si quieres que la función modifique el original, pasa un puntero:

func duplicarValor(n int) {
    n *= 2 // solo modifica la copia local
}

func duplicarPuntero(n *int) {
    *n *= 2 // modifica el original
}

func main() {
    a := 5
    duplicarValor(a)
    fmt.Println(a) // 5 — sin cambios

    duplicarPuntero(&a)
    fmt.Println(a) // 10 — modificado
}

La función new()

new(T) asigna memoria para un valor de tipo T, lo inicializa al zero value y devuelve un puntero *T:

p := new(int)
fmt.Println(*p) // 0

*p = 99
fmt.Println(*p) // 99

En la práctica se usa poco: es más idiomático declarar una variable y tomar su dirección con &, especialmente con structs.

Punteros a structs y desreferenciación automática

Cuando tienes un puntero a un struct, no necesitas escribir (*p).campo. Go desreferencia automáticamente con el operador punto:

type Punto struct {
    X, Y float64
}

func mover(p *Punto, dx, dy float64) {
    p.X += dx // equivale a (*p).X += dx
    p.Y += dy
}

func main() {
    pt := Punto{1.0, 2.0}
    mover(&pt, 3.5, -1.0)
    fmt.Println(pt) // {4.5 1}
}

Puntero nil y el pánico que produce

El zero value de cualquier tipo puntero es nil. Desreferenciar un puntero nil produce un pánico en tiempo de ejecución:

var p *int
fmt.Println(p)  // <nil>
fmt.Println(*p) // panic: runtime error: invalid memory address or nil pointer dereference

El mensaje completo incluye la traza de la pila, lo que facilita encontrar el origen. Para evitarlo, comprueba siempre que el puntero no es nil antes de desreferenciar:

func imprimir(p *int) {
    if p == nil {
        fmt.Println("puntero nulo")
        return
    }
    fmt.Println(*p)
}

Cuándo usar punteros

Usa punteros cuando necesites que una función modifique el argumento original, cuando el struct sea grande y copiar tenga coste real, o cuando el tipo en cuestión tenga métodos con pointer receiver. En el resto de casos, Go copia por valor y eso es seguro y claro. No copies el estilo de C usando punteros para todo: el GC gestiona la memoria y no hay que liberarla manualmente.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP