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. Eles são um conceito fundamental na programação, permitindo a manipulação eficiente da memória e referências a valores. O Golang possui gerenciamento automático de memória, mas ainda oferece suporte explícito a ponteiros para maior controle. Veja este 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: 10
Veja a explicação deste código, linha por linha. Na 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, pois permitem passar argumentos por referência. Veja este exemplo em Go, com 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 n dentro de dobrar:", &n)
n *= 2
}
func dobrarPtr(n *int) {
fmt.Println("Endereço de n 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: 0xc00011a1f0
Endereço de n dentro de dobrar: 0xc00011a1f8
Valor de count após dobrar: 10
Endereço de n dentro de dobrarPtr: 0xc00011a1f0
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 este 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 este exemplo, a diferença de desempenho é insignificante, mas em programas maiores, a diferença pode ser significativa.