Адрес временного в Go?
Какой самый чистый способ обрабатывать такой случай:
func a() string {
/* doesn't matter */
}
b *string = &a()
Это порождает ошибку:
не может принимать адрес a()
Я понимаю, что Go автоматически продвигает локальную переменную в кучу, если ее адрес занят. Здесь ясно, что нужно принять адрес возвращаемого значения. Какой идиоматический способ справиться с этим?
Ответы
Ответ 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