Clique sobre os tópicos listados abaixo para navegar até o conteúdo desejado.

  1. Passagem de argumento por referência
  2. Vetor/matriz de ponteiros
  3. Ponteiro para função
  4. Ponteiro para função como membro de struct
  5. Ponteiro e structs

Nesta seção vamos estudar sobre o tipo de dados ponteiro. Esse tipo de dado precisa de uma seção específica devido a sua complexidade em comparação aos outros tipos de dados.

Uma variável do tipo ponteiro também armazena valores. Porém, esses valores, diferentes dos tipos anteriores estudados, são endereços de memória.

O uso do tipo ponteiro nos permite trabalhar com manipulação de variáveis de forma indireta, sendo possível atribuir e recuperar informação.

Sintaxe
var nome_variavel *tipo_dado
Onde
tipo_dado: tipos de dados estudados anteriormente.
*: operador que indica que a variável é um ponteiro e irá armazenar um endereço de memória.
nome_variavel:nome da variável desejada para utilização.
Exemplo 1
var ponteiro *int
var ponteiro *float32
var ponteiro *nome_struct 
Saída
-

Para atribuir um valor a um ponteiro, utilizamos o operador (&). Esse deve ser acompanhado do nome de uma variável, vetor, matriz , função, procedimento e etc.

Sintaxe
nome_ponteiro = &nome_variavel
Exemplo 2
package main
import (
	"fmt"
)

func main() {

	var pnt_int *int
	var variavel int = 999
	pnt_int = &variavel
}
Saída
-

Quando um ponteiro é declarado sem ser iniciado, seu valor padrão é nil. Sempre que um ponteiro não for mais utilizado, por boa prática, devemos atribuir o valor nil para liberar qualquer recurso de memória utilizado, exceto recursos alocados.

nome_ponteiro = nil

Para acessar o conteúdo de uma variável a qual o endereço foi atribuído a um ponteiro, utilizamos o próprio operador (*), que nos diz que queremos o valor contido na variável apontada.

Sintaxe
*nome_variavel
Exemplo 3
package main
import (
	"fmt"
)

func main() {

	var pnt_int *int
	var variavel int = 999
	pnt_int = &variavel

	fmt.Println(*pnt_int)

}
Saída
999

Como mencionado anteriormente, podemos alterar valores de variáveis de forma indireta com a utilização de ponteiro. Essa alteração é feita com o auxílio do operador *.

Sintaxe
*nome_ponteiro = valor;
Exemplo 4
package main
import (
	"fmt"
)

func main() {

	var pint *int
	var vint int = 999
	pint = &vint
	*pint = 1000
	*pint = *pint + 1
	fmt.Println(*pint)
}
Saída
1001

Anteriormente, utilizamos a função printf para exibirmos o conteúdo de uma variável. Com uma variável do tipo ponteiro isso também é possível. Caso seja necessário sabermos o endereço para o local de memória que um ponteiro esteja apontando, utilizamos o especificador de formatação: %p.

Exemplo 5
package main
import (
	"fmt"
)

func main() {
	var pint *int
	var vint int = 999
	pint = &vint
	fmt.Printf("%p", pint)
}
Saída
0xc000012028

A comparação entre ponteiros na linguagem Go é bem simples, utilizamos os operadores relacionais vistos em Operadores para nos ajudar. Com essa comparação, utilizando os operadores mencionados, podemos verificar se dois ponteiros apontam ou referenciam a mesma variável ou recurso de memória.

Exemplo 5
package main
import "fmt"

func main() {

	var valor1, valor2 = 1, 2
	fmt.Println(valor1, valor2)

	ponteiro1, ponteiro2 := &valor1, &valor1
	ponteiro3 := &valor2

	if ponteiro1 == ponteiro2 {
		fmt.Println("ponteiro1 == ponteiro2")
	}

	if ponteiro1 != ponteiro3 {
		fmt.Println("ponteiro1 != ponteiro3")
	}
}
Saída
ponteiro1 == ponteiro2
ponteiro1 != ponteiro3

É possível criarmos matrizes e vetores utilizando o tipo de dado ponteiro. A sintaxe é semelhante a que abordada sobre declaração de matrizes/vetores em Go:Vetor e Go: Matriz.

Sintaxe
var nome_variavel *[dimensao]tipo_dado;
var nome_varivel *[linhas][colunas]tipo_dado;
Exemplo 6

Uso de vetor de ponteiros

package main

import "fmt"

func main() {

	vrint1, vrint2, vrint3 := 999, 888, 777
	var pint1, pint2, pint3 *int = &vrint1, &vrint2, &vrint3
	vint := [3]*int{pint1, pint2, pint3}

	for i := 0; i < 3; i++ {
		fmt.Println(*vint[i])
	}
}
Saída
999
888
777

O uso de matriz de ponteiros é feita de forma semelhante ao vetor. Declaramos uma matriz de acordo com as dimensões desejadas, atribuímos os ponteiros a cada posição.

Exemplo 7

Uso de vetor de ponteiros

package main
import "fmt"

func main() {

	vrint1, vrint2, vrint3, vrint4 := 999, 888, 777, 555
	var pint1, pint2, pint3, pint4 *int = &vrint1, &vrint2, &vrint3, &vrint4
	vint := [2][2]*int{{pint1, pint2}, {pint3, pint4}}

	for i := 0; i < 2; i++ {
		for j := 0; j < 2; j++ {
			fmt.Printf("%d ", *vint[i][j])
		}
		fmt.Println()
	}
}
Saída
999 888 
777 555

Para a utilização de ponteiro para funções em Go, podemos utilizando a inferência de dados com o operador (:=), por exemplo, para realizar a associação.

Sintaxe
nome_variavel := nome_funcao 
Exemplo 8
package main
import (
	"fmt"
)

func funcao2(param int) {
	fmt.Printf("funcao2: %d", param)
}

func main() {
	variavel := funcao2
	variavel(10)
}
Saída
funcao2: 10

Podemos também criar um novo tipo para que possamos utilizar o ponteiro de uma função em vetores, structs e outros

Sintaxe: Tipo
type tipo_funcao func(parametros) 
Sintaxe: Declaração
var nome_ponteiro_funcao tipo_funcao = nome_funcao_atribuida
Onde
tipo_função: Novo tipo de dado que representa o tipo da função.
parametros: parâmetros utilizados pela função.

A chamada ou invocação da função utilizando um ponteiro é feito da seguinte forma:

Sintaxe
nome_ponteiro(argumentos)
Exemplo 9
package main
import (
	"fmt"
)

type tipo_funcao func(param int)

func funcao2(param int) {
	fmt.Printf("funcao2: %d", param)
}

func main() {
	var pfuncao tipo_funcao = funcao2
	pfuncao(10)
}
Saída
funcao2: 10

Nessa abordagem acima, podemos utilizar uma função como argumentos para outras funções, conhecido também por callback:

Exemplo 9
type meu_tipo_funcao func(param int) 

func funcao1 (param int){
    fmt.Println("função passada como argumento")
    fmt.Println("parâmetro:",param)
}

func funcao2 (funcao_param meu_tipo_funcao, param int){
  //seu codigo aqui
  funcao_param(param);
}

func main (){
    funcao2(funcao1,999) //passando função como parâmetro
}
Saída
função passada como argumento
parâmetro: 999

Com o novo tipo de dados criado acima podemos criar, por exemplo, vetores de ponteiros para função como mostrado na sintaxe abaixo:

Sintaxe
var nome_variavel *[dimensao]tipo_dado;
var nome_varivel *[linhas][colunas]tipo_dado;
Exemplo 10
package main

import (
	"fmt"
)

type tipo_funcao func(param int)

func funcao1(param int) {
	fmt.Printf("funcao1: %d\n", param)
}

func funcao2(param int) {
	fmt.Printf("funcao2: %x\n", param)
}

func funcao3(param int) {
	fmt.Printf("funcao3: %o\n", param)
}

func main() {
	var vetpf = [3]tipo_funcao{funcao1, funcao2, funcao3}
	vetpf[0](100)
	vetpf[1](100)
	vetpf[2](100)
}
Saída
funcao1: 100
funcao2: 64
funcao3: 144
  1. 16/09/2024 - revisão 2 - Correção descrição/Ex. 8; ajustes gramaticais
  2. 02/10/2023 - revisão 1 - Correção em referências, erros gramaticais e objetivos
  3. 21/08/2023 - versão inicial