我们已经写了一个程序,通过它我们试图找到一个常数的地址。有可能这样做吗?
package mainfunc main() { const k = 5 address := &k}
package main
func main() {
const k = 5
address := &k
}
字符串它给出了一个错误,有人能告诉我们如何找到一个常数的地址吗?
w6mmgewl1#
简而言之:你不能。错误消息显示:不能取k的地址地址运算符&的操作数有限制。规格:地址运算符:对于类型为T的操作数x,地址操作&x生成指向x的类型为*T的指针。操作数必须是 addressable 的,即,要么是变量、指针间接或切片索引操作;要么是可寻址结构操作数的字段选择器;作为可寻址性要求的例外,x也可以是可寻址数组的数组索引操作。如果x的求值会导致运行时出错,那么&x的求值也会导致运行时出错。常量不列为 addressable,并且在规范中没有列为 addressable 的东西(上面引用的)不能成为地址操作符&的操作数(你不能获取它们的地址)。不允许使用常量的地址。这有两个原因:1.常量可能根本没有地址。1.即使一个常量值在运行时存储在内存中,这也是为了帮助运行时保持以下常量:constant。如果你可以接受一个常量的地址,你可以把这个地址(指针)赋给一个变量,你可以改变它。(点的值,常数的值).罗伯特Griesemer(Go语言的作者之一)写了为什么不允许使用字符串字面量的地址:* “如果你能得到一个字符串常量的地址,你可以调用一个函数[赋值给指向的值,导致]可能奇怪的效果-你肯定不希望文字字符串常量改变。如果你需要一个指针指向一个等于该常数的值,把它赋给一个变量,这个变量是可寻址的,这样你就可以得到它的地址,例如。
&
T
x
&x
*T
func main() { const k = 5 v := k address := &v // This is allowed}
v := k
address := &v // This is allowed
字符串但是要知道在Go语言中,数字常量代表任意精度的值,并且不会溢出。(例如,常量可能大于您分配给它的变量类型的最大值-导致编译时错误),或者它可能不相同(例如,在浮点常量的情况下,它可能会失去精度)。
q3aa05252#
在单元测试中创建大型嵌套JSON对象时,我经常遇到这个问题。我可能有一个结构,其中所有字段都指向字符串/整数:
type Obj struct { Prop1 *string Prop2 *int Status *string}
type Obj struct {
Prop1 *string
Prop2 *int
Status *string
字符串我想写这样的东西:
obj := Obj{ Prop1: &"a string property", Prop2: &5, Status: &statuses.Awesome,}
obj := Obj{
Prop1: &"a string property",
Prop2: &5,
Status: &statuses.Awesome,
型当我初始化它时,但语言不允许直接这样做。绕过这个问题的一个快速方法是定义一个函数,它接受一个常量并返回其地址:
s := func(s string) *string { return &s } i := func(i int) *int { return &i } obj := Obj{ Prop1: s("a string property"), Prop2: i(5), Status: s(statuses.Awesome)}
s := func(s string) *string { return &s }
i := func(i int) *int { return &i }
Prop1: s("a string property"),
Prop2: i(5),
Status: s(statuses.Awesome)
型这是因为当常量作为参数传递给函数时,会生成常量的副本,这意味着在函数中创建的指针并不指向常量的地址,而是指向其副本的地址,与将常量值分配给var时的方式相同。使用函数来做到这一点,使其更具可读性/不那么麻烦IMO比必须向前声明大块的变量。AWS SDK使用这种技术。我现在发现自己经常向我的项目添加一个包,看起来像这样:
var
package refimport "time"func Bool(i bool) *bool { return &i}func Int(i int) *int { return &i}func Int64(i int64) *int64 { return &i}func String(i string) *string { return &i}func Duration(i time.Duration) *time.Duration { return &i}func Strings(ss []string) []*string { r := make([]*string, len(ss)) for i := range ss { r[i] = &ss[i] } return r}
package ref
import "time"
func Bool(i bool) *bool {
return &i
func Int(i int) *int {
func Int64(i int64) *int64 {
func String(i string) *string {
func Duration(i time.Duration) *time.Duration {
func Strings(ss []string) []*string {
r := make([]*string, len(ss))
for i := range ss {
r[i] = &ss[i]
return r
型我称之为:
func (t: Target) assignString(to string, value string) { if to == tags.AuthorityId { t.authorityId = ref.String(value) } // ...}
func (t: Target) assignString(to string, value string) {
if to == tags.AuthorityId {
t.authorityId = ref.String(value)
// ...
型你也可以添加一个deref包,尽管我通常发现这不太有用:
deref
package dereffunc String(s *string, d string) string { if s != nil { return *s } return d}// more derefs here.
package deref
func String(s *string, d string) string {
if s != nil { return *s }
return d
// more derefs here.
型编辑2022年4月:随着go 1.18的发布,现在可以定义一个方法来处理所有从常量到指针的转换:
package reffunc Of[E any](e E) *E { return &e}
func Of[E any](e E) *E {
return &e
型
esyap4oy3#
这三个选项可能会有所帮助:1.对泛型使用辅助函数。(适用于原始类型和自定义类型)
package mainimport "fmt"type Role stringconst ( 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}
import "fmt"
type Role string
const (
Engineer Role = "ENGINEER"
Architect Role = "ARCHITECT"
)
EngineerStr string = "ENGINEER"
ArchitectStr string = "ARCHITECT"
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
字符串Try it on playground1.使用pointy。(仅适用于基元类型)1.使用ToPointer()方法。(仅适用于自定义类型)
package mainimport "fmt"type Role stringconst ( Engineer Role = "ENGINEER" Architect Role = "ARCHITECT")func (r Role) ToPointer() *Role { return &r}func main() { fmt.Println(Engineer.ToPointer())}
func (r Role) ToPointer() *Role {
return &r
fmt.Println(Engineer.ToPointer())
型Try it on playground的
xkftehaa4#
我发现了另一种处理此问题的方法,即使用AWS API:
import "github.com/aws/aws-sdk-go/aws"type Obj struct { *int}x := aws.Int(16) // return addressobj := Obj{x} // work fine
import "github.com/aws/aws-sdk-go/aws"
*int
x := aws.Int(16) // return address
obj := Obj{x} // work fine
字符串这个方法和上面的答案是一样的,但是你不需要自己写整个函数。参见:https://docs.aws.amazon.com/sdk-for-go/api/aws/
tjrkku2a5#
你不能,但你可以使用以下命令将const转换为指针:
func ptr[T any](value T) *T { return &value}
func ptr[T any](value T) *T {
return &value
字符串
zhte4eai6#
常量部分没有说得很清楚:常量不像变量,不存在于编译的代码或运行的程序中。它们是无类型的,只有当它们被赋值给变量时才会在内存中。因此,它们的精度是无限的,如果你看this example,你可以看到我可以把常量赋给一个变量,而不需要强制转换,变量将尽可能多地保持常量的精度。1正如规范还指出的那样,整数至少有256位,浮点数至少有256位尾数和32位指数,如果编译器的内部结构不能准确存储常量,编译器将抛出错误。
6条答案
按热度按时间w6mmgewl1#
简而言之:你不能。
错误消息显示:
不能取k的地址
地址运算符
&
的操作数有限制。规格:地址运算符:对于类型为
T
的操作数x
,地址操作&x
生成指向x
的类型为*T
的指针。操作数必须是 addressable 的,即,要么是变量、指针间接或切片索引操作;要么是可寻址结构操作数的字段选择器;作为可寻址性要求的例外,x
也可以是可寻址数组的数组索引操作。如果x
的求值会导致运行时出错,那么&x
的求值也会导致运行时出错。常量不列为 addressable,并且在规范中没有列为 addressable 的东西(上面引用的)不能成为地址操作符
&
的操作数(你不能获取它们的地址)。不允许使用常量的地址。这有两个原因:
1.常量可能根本没有地址。
1.即使一个常量值在运行时存储在内存中,这也是为了帮助运行时保持以下常量:constant。如果你可以接受一个常量的地址,你可以把这个地址(指针)赋给一个变量,你可以改变它。(点的值,常数的值).罗伯特Griesemer(Go语言的作者之一)写了为什么不允许使用字符串字面量的地址:* “如果你能得到一个字符串常量的地址,你可以调用一个函数[赋值给指向的值,导致]可能奇怪的效果-你肯定不希望文字字符串常量改变。
如果你需要一个指针指向一个等于该常数的值,把它赋给一个变量,这个变量是可寻址的,这样你就可以得到它的地址,例如。
字符串
但是要知道在Go语言中,数字常量代表任意精度的值,并且不会溢出。(例如,常量可能大于您分配给它的变量类型的最大值-导致编译时错误),或者它可能不相同(例如,在浮点常量的情况下,它可能会失去精度)。
q3aa05252#
在单元测试中创建大型嵌套JSON对象时,我经常遇到这个问题。我可能有一个结构,其中所有字段都指向字符串/整数:
字符串
我想写这样的东西:
型
当我初始化它时,但语言不允许直接这样做。绕过这个问题的一个快速方法是定义一个函数,它接受一个常量并返回其地址:
型
这是因为当常量作为参数传递给函数时,会生成常量的副本,这意味着在函数中创建的指针并不指向常量的地址,而是指向其副本的地址,与将常量值分配给
var
时的方式相同。使用函数来做到这一点,使其更具可读性/不那么麻烦IMO比必须向前声明大块的变量。AWS SDK使用这种技术。我现在发现自己经常向我的项目添加一个包,看起来像这样:
型
我称之为:
型
你也可以添加一个
deref
包,尽管我通常发现这不太有用:型
编辑2022年4月:
随着go 1.18的发布,现在可以定义一个方法来处理所有从常量到指针的转换:
型
esyap4oy3#
这三个选项可能会有所帮助:
1.对泛型使用辅助函数。(适用于原始类型和自定义类型)
字符串
Try it on playground
1.使用pointy。(仅适用于基元类型)
1.使用ToPointer()方法。(仅适用于自定义类型)
型
Try it on playground的
xkftehaa4#
我发现了另一种处理此问题的方法,即使用AWS API:
字符串
这个方法和上面的答案是一样的,但是你不需要自己写整个函数。
参见:https://docs.aws.amazon.com/sdk-for-go/api/aws/
tjrkku2a5#
你不能,但你可以使用以下命令将const转换为指针:
字符串
zhte4eai6#
常量部分没有说得很清楚:常量不像变量,不存在于编译的代码或运行的程序中。它们是无类型的,只有当它们被赋值给变量时才会在内存中。
因此,它们的精度是无限的,如果你看this example,你可以看到我可以把常量赋给一个变量,而不需要强制转换,变量将尽可能多地保持常量的精度。
1正如规范还指出的那样,整数至少有256位,浮点数至少有256位尾数和32位指数,如果编译器的内部结构不能准确存储常量,编译器将抛出错误。