2012-06-18 27 views
5

En mi aplicación, con frecuencia paso referencias a una cadena estática. Deseo evitar que Go asigne memoria para cada llamada, pero no pude obtener la dirección de mi cadena literal.Referencia a literales de cadenas en Go

¿Por qué no es posible tomar la dirección de un literal de cadena (ver test1() en el ejemplo a continuación)? ¿He entendido mal la sintaxis, o es una limitación debido al funcionamiento interno de Go?

Si no es posible, ¿cuál sería la mejor solución?

test2() funciona, pero ¿asignará la memoria para el var hej cada vez?
test3() no asignará ninguna memoria nueva, pero deseo evitar el desorden fuera de la función.

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

¡Gracias por la ayuda!

Respuesta

5

Tomar la dirección de un literal (cadena, número, etc.) es ilegal porque tiene una semántica ambigua.

¿Está tomando la dirección de la constante real? ¿Qué permitiría modificar el valor (y podría dar lugar a un error de tiempo de ejecución) o desea asignar un nuevo objeto, copiar la constante y obtener la dirección a la nueva versión?

Esta ambigüedad no existe en el caso de test2 ya que se trata de una variable existente cuya semántica está claramente definida. Lo mismo no funcionaría si la cadena se definió como const.

La especificación de idioma evita esta ambigüedad al no permitir explícitamente lo que está solicitando. La solución es test2. Si bien es un poco más detallado, mantiene las reglas simples y limpias.

Por supuesto, toda regla tiene sus excepciones, y en esta se refiere Ir literales de composite: El siguiente es legal y se define como tal en la especificación:

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

Puedo entender el punto de ambigüedad. ¡Gracias por la explicación! – ANisus

6

Una cuerda en el camino es inmutable y es solo un puntero y una longitud (longitud total: 2 palabras).

Para que no tenga que usar un puntero para manejarlo eficientemente.

Solo pase la cuerda.

+0

Aunque no es una respuesta directa a la pregunta, es una buena solución al problema. +1 – ANisus

2
  1. que pasa alrededor de una cadena no lo hace generalmente asigna memoria en Go - es un tipo de valor (ptr a los bytes y una int len).

  2. Tomando una dirección de un literal sólo se admite para literales compuestos como

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

    pero no para valores simples como

    w := &1

    x := &"foo"

5

Para la cuestión de la mejor solución para su situación de pasar alrededor de cuerdas "estáticos",

  1. pasar la cadena de tipo en lugar de * cadena.
  2. No hagas suposiciones sobre lo que está sucediendo detrás de escena.

Es tentador dar el consejo "no se preocupe por la asignación de cadenas" porque eso es cierto en el caso de que describa dónde se pasa la misma cadena, quizás muchas veces. En general, es realmente bueno pensar en el uso de la memoria. Es realmente malo de adivinar, y aún peor de adivinar basado en la experiencia con otro idioma.

Aquí hay una versión modificada de su programa. ¿Dónde crees que se asigna la memoria?

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

Ahora pida el compilador:

> go tool 6g -S t.go 

(I Este Programa de t.go.) Buscar la salida para las llamadas a runtime.new. ¡Sólo hay uno! Lo estropearé para ti, está en test1.

Así que sin desviarnos demasiado de una tangente, el pequeño vistazo a la salida del compilador indica que evitamos la asignación al trabajar con el tipo de cadena en lugar de * cadena.

+2

No solo obtuve una mejor comprensión cuando Go realmente decide asignar nueva memoria, sino que incluso mejor (¡o peor!) También me enseñó cómo usar 'go tool' para obtener el ensamblaje. Maldiciones, ahora estaré sentado analizando el código de montaje de Go para pasar horas de diversión. ¡Gracias! (Y sí, me quedaré con sus soluciones) – ANisus

+2

¿por qué esta no es la respuesta aceptada? gracias por enseñarnos a pescar :) –

Cuestiones relacionadas