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.