Ponteiros em Go

Usando Ponteiros em Go

Desde os primórdios da era da computação, sempre existiu e ainda existe, um assunto bastante interessante sobre a forma como nossos dados são criados e passados a diante durante a execução do nosso sistema.

Assuntos como esses eram de vitais importância quando o professor entrava no assunto sobre gerenciamento de memória.

Hoje, pretendo explicar para você o que vem a ser um ponteiro e como utilizá-los em Go. Vamos nessa? 

O que são ponteiros?

"Ponteiro é aquele que aponta para alguma coisa, que mostra um caminho a ser seguido." - William Lima, 2025.

Partindo desse princípio, creio que fica mais fácil o entendimento sobre o que vem a ser um ponteiro no mundo da TI.

Eles nada mais são do que variáveis especiais em linguagens de programação que armazenam o endereço de memória de outra variável.

Em vez de guardar diretamente um valor, um ponteiro aponta para a localização onde esse valor está armazenado na memória do computador.

E isso também diz a respeito que toda variável que nós criamos, é salva dentro de um endereço de memória do nosso computador.

Neste caso, um ponteiro nada mais é do que uma espécie de aplicativo de atalho do windows.

Se você nunca usou o sistema operacional Windows, saiba que o que mais se vê dentro dele, são ícones de aplicativos, que representam atalhos para um aplicativo principal, que esta armazenado de forma interna no sistema.

Tanto é que, se se você clicar e arrastar esse atalho para um pen drive, ou até mesmo para a nuvem, o que é copiado é apenas um atalho, e não a aplicação de fato.

Sendo assim, podemos dizer que um ponteiro é como se fosse um aplicativo de atalho do windows, ou seja, ele não contém o dado em si, somente um apontamento para onde o dado está de fato armazenado.

🧠 Conceito básico de ponteiros

Imagine a memória do computador como uma série de "caixinhas" numeradas, onde cada número representa um endereço. Um ponteiro é como um bilhete com um desses números, indicando onde encontrar um valor.

O uso de ponteiros trás consigo uma série de benefícios, tais como:

Eficiência: Permite manipular grandes estruturas de dados (como arrays ou objetos) sem copiá-los.

Referência direta: Permite modificar valores diretamente em memória.

Compartilhamento de dados: Facilita o compartilhamento de informações entre diferentes partes do programa.

Estruturas complexas: Usado para implementar listas encadeadas, árvores e outras estruturas de dados dinâmicas.

Dito isso, creio que você já tem o entendimento suficiente para seguirmos com a criação de ponteiros em Go, certo? 😉

Criando seu projeto de testes

Dentro da pasta JornadaGoLang, nós iremos criar uma nova pasta chamada de 13-ponteiros-em-go, onde dentro dela, vamos criar o nosso arquivo main.go:

package main

func main(){

}

Agora sim, vamos aprender a criar ponteiros! 😉

Criando ponteiros em Go

A criação de ponteiros no linguagem GoLang, é uma das coisas mais simples de tudo que já vimos até agora, observe:

package main

import "fmt"

func main() {
    x := 10
    p := &x // 'p' é um ponteiro para 'x'
}

Observe que criamos uma variável chamada x que armazena o valor 10. Em seguida criamos uma outra variável chamada p que armazena o valor de x (que é igual a 10).

A única diferença é que adicionamos o & antes do x. O que significa que a variável p não armazena o valor 10, mas sim, aponta para a variável x.

Resumindo, a variável p é um ponteiro que aponta para x.

Inicialmente, não tem nada demais com isso, certo?

Agora experimente só mudar o valor de p achando que o x não irá mudar 😂

É obvio que ele vai mudar, olhe só:

package main

import "fmt"

func main() {
    x := 10
    p := &x // 'p' é um ponteiro para 'x'

    fmt.Println("Valor de x:", x)   // Saída: 10
    fmt.Println("Endereço de x:", p) // Saída: endereço de memória
    fmt.Println("Valor via ponteiro:", *p) // Saída: 10

    *p = 20 // Modifica o valor de 'x' através do ponteiro
    fmt.Println("Novo valor de x:", x) // Saída: 20
}

Note que ao mostrarmos o valor de p de forma pura, o retorno será um endereço de memória. Para contornar isso, basta usar o comando * antes da variável p, para que o compilador pegue o valor 10.

  • &x: Captura o endereço de memória de x.
  • p: Armazena o endereço de x.
  • *p: Desreferencia o ponteiro, ou seja, acessa o valor armazenado no endereço.

Por que usar ponteiros em Go?

Em Go, os ponteiros são úteis quando você deseja manipular dados de forma mais eficiente, evitar cópias desnecessárias e compartilhar informações entre diferentes partes do programa.

Vejamos abaixo, algumas situações em que os ponteiros podem ser úteis:

📌 Modificar valores diretamente

Quando você passa uma variável para uma função em Go, ela é passada por valor (uma cópia é criada). Se você precisar modificar o valor original, precisa usar um ponteiro.

✅ Exemplo sem ponteiro (não altera o valor original):

package main

import "fmt"

func dobrarValor(n int) {
    n = n * 2
}

func main() {
    num := 10
    dobrarValor(num)
    fmt.Println("Sem ponteiro:", num) // Saída: 10 (não mudou)
}

✅ Exemplo com ponteiro (altera o valor original):

package main

import "fmt"

func dobrarValor(n *int) {
    *n = *n * 2
}

func main() {
    num := 10
    dobrarValor(&num)
    fmt.Println("Com ponteiro:", num) // Saída: 20 (mudou!)
}

Observaçãonum pega o endereço de memória da variável num, e *n acessa/modifica o valor nesse endereço.

📌 Melhorar a eficiência com grandes estruturas

Se você tem uma estrutura grande (como um struct), passar um ponteiro evita criar uma cópia inteira, economizando memória e processamento.

package main

import "fmt"

type Produto struct {
    Nome  string
    Preco float64
}

func aplicarDesconto(p *Produto) {
    p.Preco -= 10
}

func main() {
    p := Produto{"Notebook", 3500}
    aplicarDesconto(&p)
    fmt.Println("Preço com desconto:", p.Preco) // Saída: 3490
}

Observação: Passar o ponteiro evita copiar todo o struct, tornando o código mais rápido.

📌 Trabalhar com nil para representar ausência de valor

Ponteiros podem ser nulos (nil), o que permite representar a ausência de um valor ou estado não inicializado.

package main

import "fmt"

func getNome() *string {
    return nil // Não há nome disponível
}

func main() {
    nome := getNome()

    if nome == nil {
        fmt.Println("Nome não encontrado!")
    }
}

📌 Compartilhar estado entre goroutines

Se várias goroutines precisarem acessar e modificar uma variável, usar um ponteiro permite que todas compartilhem o mesmo valor na memória.

Em lições futuras, quando aprendermos sobre goroutines, você irá usar muitos ponteiros para isso 😋

package main

import (
    "fmt"
    "sync"
)

func incrementar(contador *int, wg *sync.WaitGroup) {
    defer wg.Done()
    *contador++
}

func main() {
    var wg sync.WaitGroup
    contador := 0

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go incrementar(&contador, &wg)
    }

    wg.Wait()
    fmt.Println("Valor final:", contador) // Saída: 5
}

Repositório da lição

Todos os arquivos relacionados com esta lição, podem ser encontrados nos seguintes repositórios abaixo:

Conclusão

Nesta lição, vimos apenas como criar ponteiros em GoLang!

Uma das lições mais importantes antes de aprendermos sobre goroutines 🙃

Criadores de Conteúdo

Foto do William Lima
William Lima
Fundador da Micilini

Inventor nato, escreve conteudos de programação para o portal da micilini.

Torne-se um MIC 🤖

Mais de 100 mic's já estão conectados na plataforma.