当我第一次接触 Go 时,我认为常量很简单且有限——只是固定值,没什么花哨的。但随着我深入研究,我发现它们非常多才多艺。是的,它们是固定值,但 Go 以灵活且高效的方式处理它们。非常酷。让我们通过一些实际例子来看看这意味着什么。
在 Go 中,常量通常是无类型的,直到您实际使用它们为止。它们具有默认类型,但可以分配给不同类型的变量,只要值合适即可。这使得它们以一种对于静态类型语言来说不寻常的方式适应。
看起来是这样的:
const x = 10 var i int = x var f float64 = x var b byte = x
有点类似于薛定谔悖论,x 可以是一个 int、一个 float64,甚至是一个字节,直到你分配它。这种暂时的灵活性让 x 能够顺利地处理代码中的不同类型。无需强制转换,保持整洁。
你甚至可以在表达式中混合不同类型的常量,Go 会找出结果的最佳类型:
const a = 1.5 const b = 2 const result = a * b // result is float64
由于a是浮点数,Go将整个表达式提升为float64。所以你不必担心失去精度——Go 可以处理它。但要小心:如果您尝试将结果分配给 int,您将收到错误。 Go 不允许可能丢失数据的隐式转换。
限制
这种灵活性只会走得很远。将常量分配给变量后,该变量的类型就会被设置:
const y = 10 var z int = y // z is an int var k float64 = y // y can still be used as float64
但是如果你尝试这个:
const y = 10.5 var m int = y // Error: constant 10.5 truncated to integer
Go 会抛出错误,因为如果没有显式强制转换,它不会自动将浮点常量转换为整数。因此,虽然常量很灵活,但它们不会更改类型以适应不兼容的变量。
了解类型默认值
当您使用无类型常量而不指定类型时,它们会采用默认类型:
无类型整数常量 默认为 int。
无类型浮点常量 默认为 float64。
无类型 Rune 常量 默认为 rune(int32)。
非类型化复杂常量默认为complex128。
无类型字符串常量默认为字符串。
无类型布尔常量 默认为 bool。
这是一个快速表格:
Constant Kind | Can Adapt To |
---|---|
Untyped Integer | int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, complex64, complex128 |
Untyped Float | float32, float64, complex64, complex128 |
Untyped Complex | complex64, complex128 |
Untyped Rune | rune, int32, any integer type that can hold the value |
Untyped String | string |
Untyped Boolean | bool |
Go 不仅在编译时计算常量,它还优化常量表达式。这意味着你可以在计算中使用常量,Go 将在编译期间计算结果:
const x = 10 var i int = x var f float64 = x var b byte = x
因此 c 不会在运行时重新计算; Go 在编译时已经算出它是 520。这可以提高性能,尤其是在速度很重要的代码中。通过使用常量,Go 只处理一次计算,而不是每次程序运行时都进行计算。
Go 不像其他语言那样有预处理器,但您可以在 if 语句中使用常量在编译时包含或排除代码。
const a = 1.5 const b = 2 const result = a * b // result is float64
当 debug 为 false 时,编译器知道 if 条件永远不会为 true,并且可能会遗漏块内的代码。这可以使您的最终二进制文件更小。非常方便,对吧?
Go 常量的一个强大功能是它们支持非常大的数字。 Go 中的无类型数值常量具有“无限”精度,仅受内存和编译器的限制。
const y = 10 var z int = y // z is an int var k float64 = y // y can still be used as float64
尽管 bigNum 比 float64 或 int 等任何内置数字类型大得多,但 Go 允许您将其定义为常量。您可以在编译时使用这些大数字进行计算:
const y = 10.5 var m int = y // Error: constant 10.5 truncated to integer
如果您一直在使用 Go,您可能已经见过用于创建枚举常量的 iota。它很有用,因为它会自动分配增量值。
您还可以在常量声明中使用 iota 的表达式来创建相关常量。
const a = 100 const b = 5 const c = a * b + 20 // c is computed at compile time
此代码使用位移位定义千字节、兆字节、千兆字节和太字节的常量。它是在编译时计算的。这是生成一系列相关常量的巧妙方法。
我发现 iota 对于此类事情非常有帮助。由于 Go 没有内置枚举类型,因此您可以使用 iota 标识符和自定义类型有效地模拟枚举。
常量可以使用按位运算和移位,甚至产生比任何内置类型都大的值。
const debug = false func main() { if debug { fmt.Println("Debugging enabled") } // The above block might be removed by the compiler if debug is false }
这里,由于移位量很大,shiftedValue 变成一个非常大的数字。该值对于标准整数类型来说太大了,但在您尝试分配它之前作为常量有效:
const bigNum = 1e1000 // This is a valid constant
这表明常量可以表示无法存储在变量中的值,从而允许进行非常大的数字的编译时计算。
虽然 Go 的常量很灵活,但有些事情他们无法做到。
常量不能被指针引用
常量在运行时没有内存地址。所以你不能获取常量的地址或使用指向它的指针。
const x = 10 var i int = x var f float64 = x var b byte = x
带类型的常量 nil 指针
虽然 nil 可以分配给指针、切片、映射、通道和函数类型的变量,但您不能创建保存类型化 nil 指针的常量。
const a = 1.5 const b = 2 const result = a * b // result is float64
这增加了 Go 中常量的不变性和编译时性质。
常量声明中的函数调用
只有某些内置函数可以在常量表达式中使用,例如 len、cap、real、imag 和complex。
const y = 10 var z int = y // z is an int var k float64 = y // y can still be used as float64
这是因为,那些内置的函数都可以使用
复合类型和常量
常量不能直接表示复合类型,如切片、映射或结构。但你可以使用常量来初始化它们。
const y = 10.5 var m int = y // Error: constant 10.5 truncated to integer
上面的代码不起作用,因为你不能将切片声明为常量。但是,您可以在变量切片内使用常量:
const a = 100 const b = 5 const c = a * b + 20 // c is computed at compile time
请记住,像 slice 这样的类型本身不是常量 - 您不能将其声明为常量。不过,里面的元素可以是常量。
需要时显式转换
如果由于类型不匹配或可能丢失精度而导致无类型常量无法直接赋值,则需要使用显式类型转换。
const debug = false func main() { if debug { fmt.Println("Debugging enabled") } // The above block might be removed by the compiler if debug is false }
我希望这能让您对常量有更好的了解。它们不仅是简单的固定值,而且是固定值。而且还有一个灵活的功能,可以使您的代码更具表现力和效率。
我对分享 Go 经验还比较陌生,我渴望学习和提高。如果您发现这篇文章有价值或对我如何改进它有建议,请在评论中发表。
我第一篇关于“Go 的 UTF-8 支持:一个有趣的限制”的 Reddit 帖子 (不需要登录) 引起了相当大的关注。
期待您的反馈。
以上是Go 的常量:超越基础的详细内容。更多信息请关注PHP中文网其他相关文章!