Qual è il punto di avere puntatori in Go?


100

So che i puntatori in Go consentono la mutazione degli argomenti di una funzione, ma non sarebbe stato più semplice se adottassero solo riferimenti (con qualificatori cost o mutabili appropriati). Ora abbiamo i puntatori e per alcuni tipi incorporati come mappe e canali passano implicitamente per riferimento.

Mi manca qualcosa o le indicazioni in Go sono solo una complicazione inutile?


1
Ecco una domanda che può aiutare a chiarire: stackoverflow.com/questions/795160/… C'è una differenza tra passare i riferimenti per valore e passare veramente per riferimento.
R. Martinho Fernandes

1
Nota: la domanda riguarda Java, ma vale anche qui.
R. Martinho Fernandes

1
"e per alcuni tipi incorporati come mappe e canali passano implicitamente per riferimento." No, tutto è pass-by-value in Go. Alcuni tipi sono (informalmente descritti come) tipi di riferimento, poiché hanno uno stato mutabile interno.
newacct

Il problema con questa domanda è che i "riferimenti" non sono una cosa singola con proprietà ben definite. Il termine "riferimenti" è molto vago. Possiamo vedere nelle risposte quante persone leggono cose diverse nella parola "riferimenti". Quindi questa domanda dovrebbe elaborare esattamente quali differenze ci sono tra i puntatori Go ei riferimenti che la domanda ha in mente.
mtraceur

Risposte:


36

Mi piace molto l'esempio tratto da http://www.golang-book.com/8

func zero(x int) {
    x = 0
}
func main() {
    x := 5
    zero(x)
    fmt.Println(x) // x is still 5
}

in contrasto con

func zero(xPtr *int) {
    *xPtr = 0
}
func main() {
    x := 5
    zero(&x)
    fmt.Println(x) // x is 0
}

42
La domanda era "perché abbiamo puntatori invece di riferimenti " e non capisco perché questo esempio non funzionerebbe con i riferimenti.
AndreKR

@AndreKR Perché possiamo scegliere se passare per riferimento o per valore. Ci sono alcuni casi in cui entrambi possono essere desiderabili.
JDSweetBeat

9
@DJMethaneMan È "puntatori contro riferimenti", non "puntatori contro valore di passaggio"!
AndreKR

Come commento a parte, il passaggio per riferimento è stato aggiunto in C # 2.0 tramite la parola chiave "ref". Ovviamente i puntatori sono ancora più convenienti in alcuni casi, perché possiamo avere un puntatore a puntatore a puntatore ...
robbie fan

Non capisco come si suppone che Go sia uno dei linguaggi popolari più facili eppure contengono una tale "caratteristica" ... È fonte di confusione e sembra inutile, almeno per le persone che lo fanno notare qui.
Akito

33

I puntatori sono utili per diversi motivi. I puntatori consentono il controllo sul layout della memoria (influisce sull'efficienza della cache della CPU). In Go possiamo definire una struttura in cui tutti i membri sono in memoria contigua:

type Point struct {
  x, y int
}

type LineSegment struct {
  source, destination Point
}

In questo caso le Pointstrutture sono incorporate all'interno della LineSegmentstruttura. Ma non puoi sempre incorporare i dati direttamente. Se vuoi supportare strutture come alberi binari o liste collegate, allora devi supportare un qualche tipo di puntatore.

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode
}

Java, Python ecc. Non hanno questo problema perché non consentono di incorporare tipi compositi, quindi non è necessario distinguere sintatticamente tra incorporamento e puntamento.

Problemi con le strutture Swift / C # risolti con i puntatori Go

Una possibile alternativa per ottenere lo stesso risultato è distinguere tra structe classcome fa C # e Swift. Ma questo ha dei limiti. Sebbene di solito sia possibile specificare che una funzione accetta una struttura come inoutparametro per evitare di copiare la struttura, non consente di memorizzare riferimenti (puntatori) a strutture. Ciò significa che non puoi mai trattare una struttura come un tipo di riferimento quando lo trovi utile, ad esempio per creare un allocatore di pool (vedi sotto).

Allocatore di memoria personalizzato

Usando i puntatori puoi anche creare il tuo allocatore di pool (questo è molto semplificato con molti controlli rimossi per mostrare solo il principio):

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode

  nextFreeNode *TreeNode; // For memory allocation
}

var pool [1024]TreeNode
var firstFreeNode *TreeNode = &pool[0] 

func poolAlloc() *TreeNode {
    node := firstFreeNode
    firstFreeNode  = firstFreeNode.nextFreeNode
    return node
}

func freeNode(node *TreeNode) {
    node.nextFreeNode = firstFreeNode
    firstFreeNode = node
}

Scambia due valori

I puntatori ti consentono anche di implementare swap. Ovvero scambiare i valori di due variabili:

func swap(a *int, b *int) {
   temp := *a
   *a = *b
   *b = temp
}

Conclusione

Java non è mai stato in grado di sostituire completamente il C ++ per la programmazione di sistemi in luoghi come Google, in parte perché le prestazioni non possono essere regolate nella stessa misura a causa della mancanza di capacità di controllare il layout e l'utilizzo della memoria (i mancati riscontri nella cache influiscono in modo significativo sulle prestazioni). Go ha mirato a sostituire il C ++ in molte aree e quindi deve supportare i puntatori.


7
C # consente di passare le strutture per riferimento. Vedi le parole chiave "ref" e "out".
olegz

1
Va bene, quindi è come Swift. Penserò a un modo per aggiornare il mio esempio.
Erik Engheim

29

I riferimenti non possono essere riassegnati, mentre i puntatori sì. Questo da solo rende i puntatori utili in molte situazioni in cui non è possibile utilizzare i riferimenti.


17
Se i riferimenti sono riassegnabili è un problema di implementazione specifico della lingua.
crantok

28

Go è progettato per essere un linguaggio conciso e minimalista. È quindi iniziato con solo valori e puntatori. Successivamente, per necessità, sono stati aggiunti alcuni tipi di riferimento (sezioni, mappe e canali).


Il linguaggio di programmazione Go: domande frequenti sulla progettazione del linguaggio: perché mappe, sezioni e riferimenti di canali mentre gli array sono valori?

"C'è molta storia su questo argomento. All'inizio, mappe e canali erano sintatticamente puntatori ed era impossibile dichiarare o utilizzare un'istanza non puntatore. Inoltre, abbiamo lottato con il modo in cui gli array dovrebbero funzionare. Alla fine abbiamo deciso che la netta separazione di puntatori e valori ha reso il linguaggio più difficile da usare. L'introduzione di tipi di riferimento, comprese le sezioni per gestire la forma di riferimento degli array, ha risolto questi problemi. I tipi di riferimento aggiungono una deplorevole complessità al linguaggio, ma hanno un grande effetto sull'usabilità: Go è diventato un linguaggio più produttivo e confortevole quando sono stati introdotti ".


La compilazione veloce è uno dei principali obiettivi di progettazione del linguaggio di programmazione Go; che ha i suoi costi. Una delle vittime sembra essere la capacità di contrassegnare le variabili (ad eccezione delle costanti del tempo di compilazione di base) e i parametri come immutabili. È stato richiesto, ma rifiutato.


golang-nuts: vai lingua. Alcuni feedback e dubbi.

"L'aggiunta di const al sistema di tipi lo costringe ad apparire ovunque e costringe a rimuoverlo ovunque se qualcosa cambia. Sebbene possa esserci qualche vantaggio nel contrassegnare gli oggetti come immutabili in qualche modo, non pensiamo che un qualificatore di tipo const sia in modo andare."


FWIW, "tipi di riferimento" in Go sono anche riassegnabili. Sono più come puntatori impliciti?
Matt Joiner

1
Sono solo una sintassi speciale per strutture che contengono un puntatore (e lunghezza, capacità, ...).
mk12
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.