web-dev-qa-db-fra.com

Adresse d'un temporaire à Go?

Quelle est la façon la plus propre de gérer un cas comme celui-ci:

func a() string {
    /* doesn't matter */
}

b *string = &a()

Cela génère l'erreur:

ne peut pas prendre l'adresse d'un ()

Je crois comprendre que Go promeut automatiquement une variable locale dans le tas si son adresse est prise. Ici, il est clair que l'adresse de la valeur de retour doit être prise. Quelle est une manière idiomatique de gérer cela?

39
Matt Joiner

L'opérateur d'adresse renvoie un pointeur vers quelque chose ayant une "maison", par ex. une variable. La valeur de l'expression dans votre code est "sans-abri". si vous avez vraiment besoin d'une chaîne *, vous devrez le faire en 2 étapes:

tmp := a(); b := &tmp

Notez que s'il existe des cas d'utilisation totalement valides pour * string, c'est souvent une erreur de les utiliser. Dans Go string est un type valeur, mais pas cher à faire circuler (un pointeur et un int). La chaîne valeur est immuable, la modification d'un *string change l'endroit où pointe le "home", pas la valeur de la chaîne, donc dans la plupart des cas *string n'est pas du tout nécessaire.

40
zzzz

Voir la section correspondante de Go language spec . & Ne peut être utilisé que sur:

  1. Quelque chose qui est adressable: variable, indirection de pointeur, opération d'indexation de tranche, sélecteur de champ d'une structure adressable, opération d'indexation de tableau d'un tableau adressable; OU
  2. Un littéral composite

Ce que vous avez n'est ni l'un ni l'autre, donc ça ne marche pas.

Je ne sais même pas ce que cela signifierait même si vous pouviez le faire. Prendre l'adresse du résultat d'un appel de fonction? Habituellement, vous passez un pointeur de quelque chose à quelqu'un parce que vous voulez qu'il soit en mesure d'assigner à l'objet pointé et de voir les modifications dans la variable d'origine. Mais le résultat d'un appel de fonction est temporaire; personne d'autre ne le "voit" à moins que vous ne l'attribuiez d'abord à quelque chose.

Si le but de la création du pointeur est de créer quelque chose avec une durée de vie dynamique, similaire à new() ou en prenant l'adresse d'un littéral composite, alors vous pouvez affecter le résultat de l'appel de fonction à une variable et prendre le adresse de cela.

13
newacct

Au final, vous proposez que Go vous permette de prendre l'adresse de n'importe quelle expression, par exemple:

i,j := 1,2
var p *int = &(i+j)
println(*p)

Le compilateur Go actuel imprime l'erreur: cannot take the address of i + j

À mon avis, permettre au programmeur de prendre l'adresse de n'importe quelle expression:

  • Cela ne semble pas très utile (c'est-à-dire: il semble avoir une très faible probabilité d'occurrence dans les programmes Go réels).
  • Cela compliquerait le compilateur et la spécification du langage.

Il semble contre-productif de compliquer le compilateur et la spécification pour peu de gain.

5
user811773

J'ai récemment été ligoté à propos de quelque chose de similaire.

Parler d'abord de chaînes dans votre exemple est une distraction, utilisez plutôt une structure, réécrivez-la en quelque chose comme:

func a() MyStruct {
    /* doesn't matter */
}

var b *MyStruct = &a()

Cela ne compilera pas car vous ne pouvez pas prendre l'adresse d'un (). Alors fais ceci:

func a() MyStruct {
    /* doesn't matter */
}

tmpA := a()
var b *MyStruct = &tmpA

Cela compilera, mais vous avez renvoyé un MyStruct sur la pile, alloué suffisamment d'espace sur le tas pour stocker un MyStruct, puis copié le contenu de la pile vers le tas. Si vous voulez éviter cela, écrivez-le comme ceci:

func a2() *MyStruct {
  /* doesn't matter as long as MyStruct is created on the heap (e.g. use 'new') */
}

var a *MyStruct = a2()

La copie est normalement peu coûteuse, mais ces structures peuvent être volumineuses. Pire encore, lorsque vous voulez modifier la structure et la faire coller, vous ne pouvez pas copier puis modifier les copies.

Quoi qu'il en soit, cela devient d'autant plus amusant lorsque vous utilisez un type d'interface de retour {}. L'interface {} peut être la structure ou un pointeur vers une structure. Le même problème de copie se pose.

1
hutch

Vous ne pouvez pas obtenir la référence du résultat directement lors de l'affectation à une nouvelle variable, mais vous avez un moyen idiomatique de le faire sans l'utilisation d'une variable temporaire (c'est inutile) en pré-déclarant simplement votre pointeur "b" - c'est la vraie étape que vous avez manquée:

func a() string {
    return "doesn't matter"
}

b := new(string) // b is a pointer to a blank string (the "zeroed" value)
*b = a()         // b is now a pointer to the result of `a()`

*b permet de déréférencer le pointeur et d'accéder directement à la zone mémoire qui contient vos données (sur le tas, bien sûr).

Jouez avec le code: https://play.golang.org/p/VDhycPwRjK9

0
Ludovic De Luna

a() ne pointe pas vers une variable telle qu'elle se trouve sur la pile. Vous ne pouvez pas pointer vers la pile (pourquoi le feriez-vous?).

Tu peux faire ça si tu veux

va := a()
b := &va

Mais ce que vous voulez vraiment réaliser n'est pas clair.

0
Denys Séguret

suppose que vous avez besoin d'aide de Cpp plus efficace ;-)

Temp obj et rvalue

"Les vrais objets temporaires en C++ sont invisibles - ils n'apparaissent pas dans votre code source. Ils surviennent chaque fois qu'un objet non-tas est créé mais pas nommé. Ces objets sans nom surviennent généralement dans l'une des deux situations suivantes: lorsque des conversions de type implicites sont appliquées pour que les appels de fonction réussissent et lorsque les fonctions renvoient des objets. "

Et de Primer Plus

lvalue est un objet de données qui peut être référencé par adresse via l'utilisateur (objet nommé). Les valeurs non-l incluent des constantes littérales (à part les chaînes entre guillemets, qui sont représentées par leurs adresses), des expressions avec plusieurs termes, tels que (a + b).

Dans Go lang, la chaîne littérale sera convertie en objet StrucType, qui sera un objet struct temporaire non adressable. Dans ce cas, la chaîne littérale ne peut pas être référencée par adresse dans Go.

Eh bien, le dernier mais non le moindre, une exception en go, vous pouvez prendre l'adresse du littéral composite. OMG, quel bordel.

0
Izana