Puntatori Go
Go mantiene i puntatori, garantendo in una certa misura le prestazioni, e allo stesso tempo limita l'uso dei puntatori per un GC migliore e per considerazioni di sicurezza.
Creazione
Per i puntatori ci sono due operatori comunemente utilizzati: uno è l'operatore di indirizzo &, l'altro è l'operatore di dereferenziazione *. Prendendo l'indirizzo di una variabile, viene restituito un puntatore del tipo corrispondente. Ad esempio:
func main() {
num := 2
p := &num
fmt.Println(p)
}Il puntatore memorizza l'indirizzo della variabile num
0xc00001c088L'operatore di dereferenziazione ha due scopi: il primo è accedere all'elemento puntato dal puntatore, ovvero dereferenziare. Ad esempio
func main() {
num := 2
p := &num
rawNum := *p
fmt.Println(rawNum)
}p è un puntatore, dereferenziando il tipo puntatore è possibile accedere all'elemento puntato dal puntatore. L'altro scopo è dichiarare un puntatore. Ad esempio:
func main() {
var numPtr *int
fmt.Println(numPtr)
}<nil>*int significa che il tipo della variabile è un puntatore di tipo int. Tuttavia, i puntatori non possono essere solo dichiarati, devono anche essere inizializzati. È necessario allocare memoria per essi, altrimenti sono puntatori nulli e non possono essere utilizzati normalmente. O si utilizza l'operatore di indirizzo per assegnare l'indirizzo di un'altra variabile al puntatore, o si utilizza la funzione incorporata new per allocare manualmente. Ad esempio:
func main() {
var numPtr *int
numPtr = new(int)
fmt.Println(numPtr)
}Si utilizza più spesso la variabile breve
func main() {
numPtr := new(int)
fmt.Println(numPtr)
}La funzione new ha un solo parametro, ovvero il tipo, e restituisce un puntatore del tipo corrispondente. La funzione allocherà memoria per il puntatore e il puntatore punterà al valore zero del tipo corrispondente. Ad esempio:
func main() {
fmt.Println(*new(string))
fmt.Println(*new(int))
fmt.Println(*new([5]int))
fmt.Println(*new([]float64))
}
0
[0 0 0 0 0]
[]Operazioni con puntatori non supportate
In Go non sono supportate le operazioni con puntatori, ovvero i puntatori non possono essere spostati. Prima guarda un codice C++:
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = &arr[0];
cout << &arr << endl
<< p << endl
<< p + 1 << endl
<< &arr[1] << endl;
}0x31d99ff880
0x31d99ff880
0x31d99ff884
0x31d99ff884Si può vedere che l'indirizzo dell'array è coerente con l'indirizzo del primo elemento dell'array e, dopo aver aggiunto uno al puntatore, punta al secondo elemento dell'array. Anche gli array in Go sono così, ma la differenza è che i puntatori non possono essere spostati. Ad esempio
func main() {
arr := [5]int{0, 1, 2, 3, 4}
p := &arr
println(&arr[0])
println(p)
// Tenta di eseguire operazioni con puntatori
p++
fmt.Println(p)
}Questo tipo di programma non supererà la compilazione, l'errore è il seguente
main.go:10:2: invalid operation: p++ (non-numeric type *[5]int)TIP
La libreria standard unsafe fornisce molte operazioni per la programmazione di basso livello, incluse le operazioni con puntatori. Visita Libreria standard-unsafe per i dettagli.
new e make
Nelle sezioni precedenti sono state menzionate molte volte le funzioni incorporate new e make. Le due sono simili, ma ci sono anche differenze. Di seguito una revisione.
func new(Type) *Type- Il valore di ritorno è un puntatore al tipo
- Il parametro ricevuto è un tipo
- Specializzato per allocare memoria per puntatori
func make(t Type, size ...IntegerType) Type- Il valore di ritorno è un valore, non un puntatore
- Il primo parametro ricevuto è un tipo, i parametri di lunghezza variabile dipendono dal tipo passato
- Specializzato per allocare memoria per slice, mappe e canali
Di seguito alcuni esempi:
new(int) // puntatore int
new(string) // puntatore string
new([]int) // puntatore a slice di interi
make([]int, 10, 100) // slice di interi con lunghezza 10, capacità 100
make(map[string]int, 10) // mappa con capacità 10
make(chan int, 10) // canale con buffer di dimensione 10