web-dev-qa-db-ja.com

Goの一時的な住所

このようなケースを処理する最もきれいな方法は何ですか?

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

b *string = &a()

これはエラーを生成します:

a()のアドレスを取得できません

私の理解では、Goはアドレスが取得されると、ローカル変数をヒープに自動的に昇格させます。ここで、戻り値のアドレスが取得されることは明らかです。これを処理する慣用的な方法は何ですか?

39
Matt Joiner

アドレス演算子は、「ホーム」を持つものへのポインタを返します。変数。コード内の式の値は「ホームレス」です。 * stringが本当に必要な場合は、2つのステップで実行する必要があります。

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

* stringには完全に有効な使用例がありますが、多くの場合、それらを使用するのは誤りです。 Goではstringは値型ですが、渡すのに安価なもの(ポインターとint)です。文字列のは不変であり、*stringは、文字列値ではなく、「ホーム」が指す場所を変更するため、ほとんどの場合*stringはまったく必要ありません。

40
zzzz

Go言語仕様 の関連セクションを参照してください。 _&_は以下でのみ使用できます:

  1. アドレス指定可能なもの:変数、ポインター間接指定、スライスインデックス操作、アドレス指定可能な構造体のフィールドセレクター、アドレス指定可能な配列の配列インデックス操作;または
  2. 複合リテラル

あなたが持っているものはそれらのどちらでもないので、それは機能しません。

できたらどういう意味かわかりません。関数呼び出しの結果のアドレスを取得していますか?通常、誰かが指すものに割り当てて、元の変数の変更を確認できるようにするために、誰かに何かのポインタを渡します。ただし、関数呼び出しの結果は一時的なものです。最初に何かに割り当てない限り、他の誰もそれを「見る」ことはありません。

ポインターを作成する目的が、new()に似た動的な存続時間を持つものを作成すること、または複合リテラルのアドレスを取得することである場合、関数呼び出しの結果を変数に割り当てて、そのアドレス。

13
newacct

最後に、Goを使用して任意の式のアドレスを取得できるようにすることを提案します。次に例を示します。

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

現在のGoコンパイラはエラーを出力します:cannot take the address of i + j

私の意見では、プログラマが任意の式のアドレスを取得できるようにします。

  • あまり役に立たないようです(つまり、実際のGoプログラムで発生する可能性は非常に低いようです)。
  • コンパイラと言語仕様が複雑になります。

コンパイラとスペックを複雑にして少しの利益を得るのは逆効果のようです。

5
user811773

私は最近、似たようなことについて結び目で縛られていました。

あなたの例で最初に文字列について話すのは注意散漫です。代わりに構造体を使用して、次のように書き直してください。

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

var b *MyStruct = &a()

A()のアドレスを取得できないため、これはコンパイルされません。これを行います:

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

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

これはコンパイルされますが、スタックにMyStructを返し、ヒープにMyStructを格納するのに十分なスペースを割り当て、スタックからヒープにコンテンツをコピーしました。これを避けたい場合は、次のように記述します。

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

var a *MyStruct = a2()

コピーは通常安価ですが、それらの構造体は大きいかもしれません。さらに悪いことに、構造体を変更して「スティック」にしたい場合、コピーしてからコピーを変更することはできません。

とにかく、インターフェイスの戻り値の型を使用している場合は、さらに楽しくなります{}。 interface {}は、構造体または構造体へのポインタにすることができます。同じコピーの問題が発生します。

1
hutch

新しい変数に割り当てるときに結果の参照を直接取得することはできませんが、一時変数を使用せずに(これは役に立たない)単純に "b"ポインターを事前宣言することでこれを行う慣用的な方法があります-これはあなたが逃した本当のステップ:

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は、ポインターを逆参照し、データを(もちろんヒープ上に)保持するメモリー領域に直接アクセスするために使用されます。

コードで遊ぶ: https://play.golang.org/p/VDhycPwRjK9

0
Ludovic De Luna

a()はスタック上にあるため、変数を指しません。スタックをポイントすることはできません(なぜそうするのでしょうか?)。

あなたが望むならあなたはそれを行うことができます

va := a()
b := &va

しかし、あなたが本当に達成したいことはやや不明確です。

0
Denys Séguret

より効果的なCpp ;-)の助けが必要だと思います

Temp objおよびrvalue

真の一時オブジェクト C++は表示されません-ソースコードには表示されません。ヒープ以外のオブジェクトが作成されたが名前が付けられていない場合に発生します。このような名前のないオブジェクトは通常、暗黙的な型変換が適用されて関数呼び出しが成功するときと、関数がオブジェクトを返すときの2つの状況のいずれかで発生します。

そしてPrimer Plusから

lvalueは、ユーザー(名前付きオブジェクト)を介してアドレスから参照できるデータオブジェクトです。非lvalueには、リテラル定数(アドレスで表される引用符付きの文字列を除く)、(a + b)などの複数の用語を含む式が含まれます。

Go langでは、文字列リテラルはStrucTypeオブジェクトに変換されます。これは、アドレス指定できない一時構造体オブジェクトになります。この場合、文字列リテラルはGoのアドレスで参照できません。

さて、最後ですが、少なくとも1つの例外として、複合リテラルのアドレスを使用できます。 OMG、なんてめちゃくちゃ。

0
Izana