Найти адрес постоянной в го
Мы написали одну программу, с помощью которой мы пытаемся найти адрес константы. Можно ли так сделать?
package main
func main() {
const k = 5
address := &k
}
Это дает ошибку, кто-нибудь может сказать, как мы можем найти адрес постоянной?
5 ответов
Короче говоря, вы не можете.
Сообщение об ошибке говорит:
не может взять адрес к
Существуют ограничения на операнд оператора адреса &
, Спец: Адреса операторов:
Для операнда
x
типаT
, адресная операция&x
генерирует указатель типа*T
вx
, Операнд должен быть адресуемым, т. Е. Либо переменной, либо косвенным указателем, либо операцией индексации среза; или селектор поля адресуемого структурного операнда; или операция индексации массива адресуемого массива. В качестве исключения из требования адресуемости,x
также может быть составным литералом (возможно, заключенным в скобки). Если оценкаx
вызовет панику во время выполнения, а затем оценку&x
тоже
Константы не указаны как адресуемые, а вещи, которые не указаны в спецификации как адресуемые (цитируется выше), не могут быть операндами оператора адресации. &
(вы не можете взять их адрес).
Не разрешается принимать адрес константы. Это по 2 причинам:
- Константа может вообще не иметь адреса.
- И даже если постоянное значение хранится в памяти во время выполнения, это помогает среде выполнения сохранять постоянные: постоянные. Если бы вы могли взять адрес постоянного значения, вы могли бы назначить адрес (указатель) переменной, и вы могли бы изменить это (указанное значение, значение константы).
Если вам нужен указатель на значение, равное этой константе, присвойте его переменной, которую можно адресовать, чтобы вы могли взять ее адрес, например
func main() {
const k = 5
v := k
address := &v // This is allowed
}
Но знайте, что в Go числовые константы представляют значения произвольной точности и не переполняются. Когда вы присваиваете значение константы переменной, это может оказаться невозможным (например, константа может быть больше максимального значения типа переменной, которому вы ее назначаете - что приводит к ошибке времени компиляции), или это может не должно быть одинаковым (например, в случае констант с плавающей точкой, оно может потерять точность).
Я часто сталкиваюсь с этой проблемой при создании больших вложенных объектов JSON во время модульных тестов. Я мог бы иметь структуру, где все поля являются указателями на строки / целые:
type Obj struct {
Prop1 *string
Prop2 *int
Status *string
}
и хочу написать что-то вроде:
obj := Obj{
Prop1: &"a string property",
Prop2: &5,
Status: &statuses.Awesome,
}
Когда я инициализирую это, но язык не позволяет это напрямую. Быстрый способ обойти это - определить функцию, которая принимает константу и возвращает ее адрес:
s := func(s string) *string { return &s }
i := func(s int) *int { return &i }
obj := Obj{
Prop1: s("a string property"),
Prop2: i(5),
Status: s(statuses.Awesome)
}
Это работает из-за того, что когда константа передается в качестве параметра функции, создается копия константы, что означает, что созданный в функции указатель указывает не на адрес константы, а на адрес ее копировать так же, как когда постоянное значение присваивается var
, Однако использование функции для этого делает ее более читаемой / менее громоздкой IMO, чем необходимость объявлять большие блоки переменных.
Эти 3 варианта могут быть полезны:
- Использование вспомогательной функции с дженериками. (Работает как для примитивных, так и для пользовательских типов)
package main
import "fmt"
type Role string
const (
Engineer Role = "ENGINEER"
Architect Role = "ARCHITECT"
)
const (
EngineerStr string = "ENGINEER"
ArchitectStr string = "ARCHITECT"
)
func main() {
fmt.Println(PointerTo(Engineer)) // works for custom types
fmt.Println(PointerTo(EngineerStr)) // works for primitive types
}
func PointerTo[T any](v T) *T {
return &v
}
Попробуйте на детской площадке
Использование остроконечного . (Работает только для примитивных типов)
Использование метода ToPointer(). (Работает только для пользовательских типов)
package main
import "fmt"
type Role string
const (
Engineer Role = "ENGINEER"
Architect Role = "ARCHITECT"
)
func (r Role) ToPointer() *Role {
return &r
}
func main() {
fmt.Println(Engineer.ToPointer())
}
Я нашел другой способ справиться с этим, используя API AWS:
import "github.com/aws/aws-sdk-go/aws"
type Obj struct {
*int
}
x := aws.Int(16) // return address
obj := Obj{x} // work fine
этот метод буквально совпадает с ответом выше, но вам не нужно писать все функции самостоятельно.
Что секция констант не проясняет: константы, в отличие от переменных, отсутствуют в скомпилированном коде или выполняющейся программе. Они нетипизированы и будут в памяти только после того, как они назначены переменной.
В результате они, кажется, имеют бесконечную точность. Если вы посмотрите на этот пример, то увидите, что я могу присвоить константу переменной без ее приведения, и переменная будет содержать столько точности констант, сколько сможет.
1 Как указывает спецификация, целые числа имеют по крайней мере 256 бит, плавающие по крайней мере 256 бит мантиссы и 32-битную экспоненту, и компилятор выдаст ошибку, если его внутренние конструкции не могут точно сохранить константу.