Подтвердить что ты не робот

Адрес временного в Go?

Какой самый чистый способ обрабатывать такой случай:

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

b *string = &a()

Это порождает ошибку:

не может принимать адрес a()

Я понимаю, что Go автоматически продвигает локальную переменную в кучу, если ее адрес занят. Здесь ясно, что нужно принять адрес возвращаемого значения. Какой идиоматический способ справиться с этим?

4b9b3361

Ответ 1

Оператор адресов возвращает указатель на то, что имеет "домашний", например. Переменная. Значение выражения в вашем коде является "бездомным". если вам действительно нужна строка *, вам нужно сделать это за 2 шага:

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

Обратите внимание, что, хотя для строки * есть полностью допустимые варианты использования, много раз ошибочно использовать их. В Go string - тип значения, но дешевый, который нужно пройти (указатель и int). Строковое значение неизменно, изменение a *string меняется, когда указывает "домашний", а не строковое значение, поэтому в большинстве случаев *string вообще не требуется.

Ответ 2

См. соответствующий раздел Спецификация языка go. & может использоваться только:

  • Что-то адресуемое: переменная, указатель указателя, операция индексации среза, селектор полей адресной структуры, операция индексирования массива адресного массива; ИЛИ
  • Составной литерал

У вас нет ни одного из них, поэтому он не работает.

Я даже не знаю, что бы это значило, даже если бы вы могли это сделать. Принимая адрес результата вызова функции? Обычно вы передаете кому-то указатель на что-то, потому что хотите, чтобы они могли присваивать указанную вещь и видеть изменения исходной переменной. Но результат вызова функции является временным; никто не видит "это", если вы сначала не назначили его.

Если целью создания указателя является создание чего-то с динамическим временем жизни, похожее на new() или взятие адреса составного литерала, то вы можете назначить результат вызова функции переменной и принять адрес из этого.

Ответ 3

В конце вы предлагаете, чтобы Go позволял вам принимать адрес любого выражения, например:

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

Текущий компилятор Go печатает ошибку: cannot take the address of i + j

По-моему, позволяя программисту принимать адрес любого выражения:

  • Кажется, что он не очень полезен (то есть: он, кажется, имеет очень небольшую вероятность появления в реальных программах Go).
  • Это усложнит компилятор и спецификацию языка.

Кажется непродуктивным усложнять компилятор и спецификацию для небольшого выигрыша.

Ответ 4

Недавно я был связан в узлах о чем-то подобном.

Сначала речь о строках в вашем примере - это отвлечение, вместо этого используйте структуру, переписывая ее на что-то вроде:

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

Копирование, как правило, недорогое, но эти структуры могут быть большими. Еще хуже, если вы хотите изменить структуру и использовать ее, вы не можете копировать, а затем изменять копии.

В любом случае, становится все веселее, когда вы используете возвращаемый тип интерфейса {}. Интерфейс {} может быть структурой или указателем на структуру. Вызывается тот же самый вопрос о копировании.

Ответ 5

a() не указывает на переменную, как в стеке. Вы не можете указать на стек (зачем вам?).

Вы можете сделать это, если хотите

va := a()
b := &va

Но то, что вы действительно хотите достичь, несколько неясное.

Ответ 6

думаю, вам нужна помощь от более эффективного Cpp ;-)

Temp obj и rvalue

" Истинные временные объекты в C++ невидимы - они не появляются в вашем исходном коде. Они возникают всякий раз, когда создается объект без кучи, но не именуется. Такие безымянные объекты обычно возникают в одной из двух ситуаций: когда неявные преобразования типов применяются для успешного вызова функций и когда функции возвращают объекты. "

И от Primer Plus

lvalue - это объект данных, на который можно ссылаться по адресу через пользователя (именованный объект). К ненулевым значениям относятся буквальные константы (кроме строк в кавычках, которые представлены их адресами), выражения с несколькими терминами, например (a + b).

В Go lang строковый литерал будет преобразован в объект StrucType, который будет не адресуемым временным структурным объектом. В этом случае на строковый литерал нельзя ссылаться по адресу в Go.

Ну, последнее, но не менее важное, одно исключение в go, вы можете взять адрес составного литерала. О, Боже, какой беспорядок.

Ответ 7

Вы не можете получить ссылку на результат напрямую при назначении новой переменной, но у вас есть идиоматический способ сделать это без использования временной переменной (она бесполезна), просто предварительно объявив указатель "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