Pular para o conteúdo principal

victorstein.dev

11/100 Dias de Golang - Ponteiros

Table of Contents

# Ponteiros

Ponteiros são variáveis que armazenam endereços de memória de outras variáveis. Ponteiros são um conceito fundamental na programação, permitindo manipulação eficiente de memória e referências a valores. Golang possui gerenciamento automático de memória, porém a linguagem ainda oferece suporte explícito a ponteiros para maior controle. Veja esse exemplo em Go:

var count int = 10
var ptrCount *int = &count

fmt.Println("Valor de count:", count)
fmt.Println("Endereço de count:", &count)
fmt.Println("Valor de ptrCount:", ptrCount)
fmt.Println("Valor apontado por ptrCount:", *ptrCount)
Valor de count: 10
Endereço de count: 0xc000104040
Valor de ptrCount: 0xc000104040
Valor apontado por ptrCount: 1

Veja a explicação desse código, linha por linha. A primeira linha temos uma variável count do tipo int que armazena o valor 10.

var count int = 10

Na segunda linha, declaramos uma variável ptrCount do tipo *int que armazena o endereço de memória da variável count. Usamos o * para indicar que ptrCount é um ponteiro e o & para obter o endereço de memória da variável count. O & é o operador de referência ou endereço e o * é o operador de desreferência ou indireção.

var ptrCount *int = &count

Nas duas próximas linhas, imprimimos o valor da variável count e o endereço de memória dela.

fmt.Println("Valor de count:", count)       // 10
fmt.Println("Endereço de count:", &count)   // 0xc000104040

Nas duas linhas finais, imprimimos o valor da variável ptrCount e o valor apontado por ela. Perceba que para obter o valor apontado por ptrCount, usamos o operador de desreferência *.

fmt.Println("Valor de ptrCount:", ptrCount)                 // 0xc000104040
fmt.Println("Valor apontado por ptrCount:", *ptrCount)      // 10

Veja que o valor de ptrCount é o endereço de memória da variável count e o valor apontado por ptrCount é o valor da variável count.

# Ponteiros e Funções

Ponteiros são muito úteis em funções, permitindo passar argumentos por referência. Veja esse exemplo em Go, temos duas funções dobrar e dobrarPtr. A função dobrar recebe um argumento n do tipo int e dobra o valor de n. A função dobrarPtr recebe um argumento n do tipo *int e dobra o valor apontado por n. Dentro de dobrar e dobrarPtr, imprimimos o endereço de memória de n.

func dobrar(n int) {
	fmt.Println("Endereço de count dentro de dobrar:", &n)
	n *= 2
}

func dobrarPtr(n *int) {
	fmt.Println("Endereço de count dentro de dobrarPtr:", n)
	*n *= 2
}
var count int = 10

fmt.Println("Endereço de count:", &count)
dobrar(count)
fmt.Println("Valor de count após dobrar:", count)

dobrarPtr(&count)
fmt.Println("Valor de count após dobrarPtr:", count)
Endereço de count: 0xc0000100e0
Endereço de count dentro de dobrar: 0xc0000100e8
Valor de count após dobrar: 10
Endereço de count dentro de dobrarPtr: 0xc0000100e0
Valor de count após dobrarPtr: 20

Perceba que o endereço de memória da variável count é diferente dentro de dobrar e dobrarPtr. Isso ocorre porque a função dobrar recebe uma cópia do valor de count, enquanto a função dobrarPtr recebe o endereço de memória de count. Portanto, a função dobrar não altera o valor de count, enquanto a função dobrarPtr altera o valor de count diretamente, tanto que o valor de count após dobrarPtr é 20.

# Ponteiros e Structs

Em Go, os ponteiros são frequentemente usados com structs para evitar cópias desnecessárias. O uso de *Pessoa permite modificar a idade diretamente, sem copiar a struct inteira.

type Pessoa struct {
    Nome string
    Idade int
}

func aniversario(p *Pessoa) {
    p.Idade++
}

func main() {
    p := Pessoa{"Victor", 28}
    aniversario(&p)
    fmt.Println(p) // Saída: {Victor 29}
}

# Exemplo função troca

Peguei esse exemplo do site do Professor Paulo Feofiloff. A função troca recebe dois argumentos a e b do tipo *int e troca os valores apontados por a e b. Só por curiosidade, veja como fica o código dessa função em C:

void troca (int *p, int *q)
{
   int temp;
   temp = *p; 
   *p = *q; 
   *q = temp;
}

Vamos implementar essa função em Go, sem usar ponteiros:

func troca(a, b int) (int, int) {
    return b, a
}

Agora, vamos implementar essa função em Go, usando ponteiros:

func trocaPtr(a, b *int) {
    *a, *b = *b, *a
}

Agora, vamos testar essas duas funções:

var x, y int = 10, 20

fmt.Println("Antes da troca:")
fmt.Println("x =", x)
fmt.Println("y =", y)

x, y = troca(x, y)

fmt.Println("Depois da troca:")
fmt.Println("x =", x)
fmt.Println("y =", y)

x, y = 10, 20
trocaPtr(&x, &y)
fmt.Println("Depois da troca usando ponteiros:")
fmt.Println("x =", x)
fmt.Println("y =", y)

O resultado de ambos os testes é o mesmo, mas a função trocaPtr é mais “eficiente”, pois não precisa copiar os valores de x e y. Obviamente, para esse exemplo, a diferença de desempenho é insignificante, mas em programas maiores, a diferença pode ser significativa.

# Referências

  1. Endereços e ponteiros - Professor Paulo Feofiloff
  2. Capítulo 7 - C Como Programar - Deitel & Deitel