Matteo Torromacco
Matteo Torromacco

Follow

Matteo Torromacco

Follow
Puntatori in Go

Photo by Chinmay Bhattar on Unsplash

Puntatori in Go

Impariamo come funzionano e come utilizzare i puntatori in Go step-by-step.

Matteo Torromacco's photo
Matteo Torromacco
·Aug 12, 2022·

3 min read

Table of contents

  • Introduzione
  • Puntatori in Go
  • Conclusione

Introduzione

Il primo concetto pratico che si apprende quando ci si approccia alla programmazione è sicuramente quello relativo alle variabili, delle scatole che possono contenere qualsiasi tipo di valore come interi, stringhe o booleani.
Quando una variabile contiene un indirizzo di memoria allora si parla di puntatore.
In programmazione si utilizzano i puntatori per memorizzare l'area di memoria di un'altra variabile.

Nel caso in cui si abbia a che fare con linguaggi di alto livello (come ad esempio Python o C#) questo meccanismo viene nascosto agli occhi del programmatore.
Diversa è la storia se si lavora con linguaggi di basso livello come C, C++ o Rust (potete trovare un approfondimento su come Rust gestisce la memoria QUI). Questi linguaggi costringono il programmatore a preoccuparsi della gestione della memoria e l'utilizzo di puntatori è fondamentale!

Puntatori in Go

Go, fa esplicito utilizzo dei puntatori per un motivo ben preciso: ogni passaggio di valore avviene by-value, questo significa che ogni volta che passiamo un valore ad una funzione questo verrà copiato impedendoci di andare a modificare il valore originario.

func main() {
    i := 5
    updateValue(i)
    fmt.Println(i) // 5
}

func updateValue(i int) {
    i = 10
}

Qui ci vengono in aiuto i puntatori che ci consentono di passare direttamente l'indirizzo di memoria di una variabile in modo da poter accedere al valore originario.
Dichiariamo un puntatore utilizzando un asterisco come prefisso del tipo.
*T rappresenta un puntatore ad un valore di tipo T.

var i *int // Puntatore per intero
var s *string // Puntatore per stringa
var b *bool // Puntatore per booleano

Utilizziamo l'operatore & per ottenere l'indirizzo di memoria di una variabile.

i := 5
p := &i // Puntatore di tipo *int che punta all'allocazione di memoria di 'i'

Ora che abbiamo il puntatore, possiamo accedere all'area di memoria della variabile originaria e modificare il suo valore.
Per farlo dobbiamo utilizzare l'operatore di dereferenziazione che ci consente, a partire da un puntatore, di accedere effettivamente al valore contenuto nell'area di memoria.
L'operatore di dereferenziazione è rappresentato anch'esso dall'asterisco.

i := 5
var p *int // Dichiaro il puntatore
p = &i  // Inizializzo il puntatore
*p = 10 // Accedo all'area di memoria di 'i' e la modifico
fmt.Println(i) // 10

Questo è probabilmente il punto più critico:

  • *TIPO → Rappresenta il tipo del puntatore

    var p *int

  • *PUNTATORE → Rappresenta l'accesso all'area di memoria (dereference operator)

    *p = 10

La logica di aggiornamento scritta in precedenza funzionerebbe correttamente se utilizzassimo i puntatori.

func main() {
    i := 5
    updateValue(&i)
    fmt.Println(i) // 10
}

func updateValue(i *int) {
    *i = 10
}

Conclusione

In Go i puntatori non sono così complessi come sembrano anche grazie al fatto che il loro utilizzo non implica la gestione della memoria grazie al garbage collector interno.
Differente è la situazione per gli altri linguaggi di basso livello, come C o C++ in cui una gestione di puntatori errata può compromettere il corretto funzionamento dell'applicazione.
Ovviamente la comprensione dei puntatori va ben oltre a quello visto in questo articolo e per ulteriori approfondimenti vi rimando alla documentazione ufficiale di Go.

Happy coding!

 
Share this