2012-06-18 11 views
5

Nella mia applicazione passerò frequentemente i riferimenti a una stringa statica. Vorrei evitare di dover allocare memoria per ogni chiamata, ma non sono riuscito a ottenere l'indirizzo nella mia stringa letterale.Riferimento a stringhe letterali in Go

Perché non è possibile prendere l'indirizzo di una stringa letterale (vedere test1() nell'esempio seguente)? Ho frainteso la sintassi, o è una limitazione dovuta al funzionamento interno di Go?

Se non fosse possibile, quale sarebbe la soluzione migliore?

test2() funziona, ma allocherà la memoria per lo var hej ogni volta?
test3() non assegnerà alcuna nuova memoria, ma desidero evitare il disordine all'esterno della funzione.

package main 

import "fmt" 

var konnichiwa = `こんにちは世界` 

// Gives the compile error `cannot take the address of "Hello world"` 
func test1() (*string) { return &`Hello world` } 

// Works fine 
func test2() (*string) { 
    hej := `Hej världen` 
    return &hej 
} 

func test3() (*string) { return &konnichiwa } 

func main(){ 
    fmt.Println(*test1()) 
    fmt.Println(*test2()) 
    fmt.Println(*test3()) 
} 

Grazie per l'aiuto!

risposta

5

Prendere l'indirizzo di un letterale (stringa, numero, ecc.) È illegale perché ha semantica ambigua.

Stai prendendo l'indirizzo della costante attuale? Quale consentirebbe la modifica del valore (e potrebbe portare a un errore di runtime) o si desidera allocare un nuovo oggetto, copiare la costante e ottenere l'indirizzo nella nuova versione?

Questa ambiguità non esiste nel caso di test2 poiché si ha a che fare con una variabile esistente di cui la semantica è chiaramente definita. Lo stesso non funzionerebbe se la stringa fosse definita come const.

Le specifiche del linguaggio evitano questa ambiguità escludendo esplicitamente ciò che si sta chiedendo. La soluzione è test2. Mentre è leggermente più verboso, mantiene le regole semplici e pulite.

Naturalmente, ogni regola ha le sue eccezioni, e in questo Go riguarda letterali composit: Il seguente è legale e definiti come tali nelle specifiche:

func f() interface{} { 
    return &struct { 
     A int 
     B int 
    }{1, 2} 
} 
+0

Posso capire il punto di ambiguità. Grazie per aver spiegato! – ANisus

6

Una stringa in entrata è immutabile ed è solo un puntatore e una lunghezza (lunghezza totale: 2 parole).

Quindi non è necessario utilizzare un puntatore per gestirlo in modo efficiente.

Basta passare la stringa.

+0

Anche se non è una risposta diretta alla domanda, è una buona soluzione al problema. +1 – ANisus

2
  1. passando intorno una stringa non lo fa di solito alloca la memoria in Go: è un tipo di valore (ptr per i byte e un valore int).

  2. assunzione di un indirizzo di una letterale è supportato solo per letterali compositi come

    v := &T{1, "foo"}

    ma non per valori semplici come

    w := &1

    x := &"foo"

5

Per la questione della soluzione migliore per la vostra situazione di passare intorno stringhe "statici",

  1. Pass Tipo la stringa invece di * stringa.
  2. Non fare supposizioni su ciò che accade dietro le quinte.

Si sarebbe tentati di dare il consiglio "non si preoccupi per allocare le stringhe", perché questo è effettivamente vero nel caso che stai descrivendo in cui la stessa stringa viene passata in giro, forse molte volte. In generale, però, è davvero bello pensare all'uso della memoria. È davvero pessimo da indovinare, e ancora peggio da indovinare in base all'esperienza con un'altra lingua.

Ecco una versione modificata del programma. Dove indovini che la memoria è allocata?

package main 

import "fmt" 

var konnichiwa = `こんにちは世界` 

func test1() *string { 
    s := `Hello world` 
    return &s 
} 

func test2() string { 
    return `Hej världen` 
} 

func test3() string { 
    return konnichiwa 
} 

func main() { 
    fmt.Println(*test1()) 
    fmt.Println(test2()) 
    fmt.Println(test3()) 
} 

Ora chiedere al compilatore:

> go tool 6g -S t.go 

(ho chiamato il programma t.go.) Cerca l'uscita per le chiamate a runtime.new. Ce n'è solo uno! Lo spoglio per te, è in test1.

Quindi senza passare troppo su una tangente, la piccola occhiata all'output del compilatore indica che evitiamo l'allocazione lavorando con il tipo di stringa anziché con * stringa.

+2

Non solo ho capito meglio quando Go decide di allocare nuova memoria, ma anche meglio (o peggio!) Mi hai anche insegnato come usare 'go tool' per ottenere l'assemblaggio da solo. Maledette, ora sarò seduto ad analizzare il codice assembly di Go per ore di divertimento. Grazie! (E sì, rimarrò con le tue soluzioni) – ANisus

+2

perché questa non è la risposta accettata? grazie per averci insegnato a pescare :) –

Problemi correlati