2012-06-18 18 views
5

W mojej aplikacji będę często przekazywał odniesienia do statycznego ciągu znaków. Chciałbym uniknąć przydzielania pamięci do każdego wywołania, ale nie udało mi się uzyskać adresu na literał ciągu.Odsyłacz do literałów ciągów w Go

Dlaczego nie można pobrać adresu literału literowego (zob. test1() w poniższym przykładzie)? Czy źle zrozumiałem składnię, czy też jest to ograniczenie ze względu na wewnętrzne działanie Go?

Jeśli nie jest możliwe, jakie byłoby najlepsze rozwiązanie?

test2() działa, ale czy za każdym razem przydzieli pamięć dla?
Nie można przydzielić nowej pamięci, ale chcę uniknąć bałaganu poza funkcją.

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()) 
} 

Dzięki za pomoc!

Odpowiedz

5

Podanie adresu literału (ciąg, liczba itp.) Jest nielegalne, ponieważ ma niejednoznaczną semantykę.

Czy bierzesz adres rzeczywistej stałej? Które pozwoliłoby modyfikować wartość (i może prowadzić do błędu runtime) lub chcesz przydzielić nowy obiekt, skopiuj stałą i uzyskać adres do nowej wersji?

Ta niejednoznaczność nie istnieje w przypadku test2, ponieważ mamy do czynienia z istniejącą zmienną, której semantyka jest wyraźnie zdefiniowana. To samo nie działa, jeśli łańcuch został zdefiniowany jako const.

Specyfikacja językowa unika tej niejednoznaczności, wyraźnie nie zezwalając na to, o co prosisz. Rozwiązaniem jest test2. Chociaż jest nieco bardziej szczegółowy, zachowuje zasady proste i czyste.

Oczywiście, każda reguła ma swoje wyjątki, a idź dotyczy to Composit literałów: Poniżej jest legalne i określone jako takie w spec:

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

Potrafię zrozumieć punkt niejednoznaczności. Dzięki za wyjaśnienie! – ANisus

6

Ciąg w biegu jest niezmienny i jest tylko wskaźnikiem i długością (całkowita długość: 2 słowa).

Dzięki temu nie trzeba używać wskaźnika do efektywnego obchodzenia się z nim.

Wystarczy podać ciąg.

+0

Chociaż nie jest to bezpośrednia odpowiedź na to pytanie, jest to dobre rozwiązanie problemu. +1 – ANisus

2
  1. przechodząc wokół łańcuch nie zazwyczaj przydziela pamięć w Go - jest to typ wartości (ptr do bajtów i int len).

  2. Biorąc adres dosłownym jest wyłącznie dla kompozytowych literałach jak

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

    ale nie dla prostych wartości jak

    w := &1

    x := &"foo"

5

Aby uzyskać najlepsze rozwiązanie dla sytuacji, w której przechodzą "statyczne" ciągi znaków,

  1. Przekaż typ ciągu zamiast * ciąg.
  2. Nie należy przyjmować żadnych założeń na temat tego, co dzieje się za kulisami.

Kuszące jest udzielanie porad "nie przejmuj się przydzielaniem ciągów", ponieważ tak jest w przypadku, gdy opisujesz, gdzie ten sam ciąg jest przekazywany, może wiele razy. Ogólnie rzecz biorąc, dobrze jest myśleć o używaniu pamięci. Jest naprawdę źle odgadnąć, a jeszcze gorzej do zgadywania na podstawie doświadczenia z innym językiem.

Oto zmodyfikowana wersja twojego programu. Gdzie przypuszczasz, że pamięć jest przydzielona?

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()) 
} 

Teraz poprosić kompilatora:

> go tool 6g -S t.go 

(nazwałem t.go. programu) Szukaj wyjście dla połączeń do runtime.new. Jest tylko jeden! Zepsuje to dla ciebie, jest w teście1.

Tak więc bez wychodzenia za dużo stycznej, małe spojrzenie na wyjście kompilatora wskazuje, że unikamy alokacji, pracując z typem łańcucha zamiast * stringiem.

+2

Nie tylko lepiej zrozumiałem, kiedy Go faktycznie decyduje się na przydzielenie nowej pamięci, ale jeszcze lepiej (lub gorzej!) Nauczyłeś mnie również, jak używać 'go tool' do samodzielnego montażu. Przekleństwa, teraz będę siedział analizując kod zespołu Go na wiele godzin zabawy. Dzięki! (I tak, będę trzymać się twoich rozwiązań) – ANisus

+2

dlaczego nie jest to zaakceptowana odpowiedź? dzięki za uczenie nas, jak łowić ryby :) –

Powiązane problemy