Go语言中的常量使用关键字 const 定义,用于存储不会改变的数据,常量是在编译时被创建的,即使定义在函数内部也是如此,并且只能是布尔型、数字型(整数型、浮点型和复数)和字符串型。由于编译时的限制,定义常量的表达式必须为能被编译器求值的常量表达式。
枚举的实现
在go中,不像c,有原生的enum关键字支持枚举,那枚举怎么实现呢?先看结论,如下代码
package main
import (
"fmt"
)
const GlobalSingleConst int = 10
const (
GlobalListConst1 int = 1
GlobalListConst2 int = 2
)
// 默认是使用第一个变量的值,也可以使用表达式,如后文
const (
GlobalEnumType1 = 10 // 10
GlobalEnumType2 // 10
GlobalEnumType3 // 10
GlobalEnumType4 // 10
)
// 可以看到这里的变量表达式是隐式重复的,也可以显示的填写(如后文)
const (
_ = iota // 0
GlobalEnumTypeC1 = 1 << iota // 1 // 1
GlobalEnumTypeC2 // 2
GlobalEnumTypeC4 // 4
GlobalEnumTypeC5 // 8
)
// 定义一个枚举列表,其中iota是一个内置函数,代表的这个内置函数所在的位次
const (
GlobalEnumTypeA1 = 1 << iota // 1
GlobalEnumTypeA2 = 1 << iota // 2
GlobalEnumTypeA3 = 1 << iota // 4
GlobalEnumTypeA4 = 1 << iota // 8
)
// 我们可以通过 _ 来表示跳过这一行,为后续可能的状态预留位置
const (
GlobalEnumTypeB1 = 1 << iota // 1
GlobalEnumTypeB2 = 1 << iota // 2
_
GlobalEnumTypeB4 = 1 << iota // 8
GlobalEnumTypeB5 = 1 << iota // 16
// 这里我们的空行对 iota的计算没有任何影响
GlobalEnumTypeB6 = 1 << iota * iota // 32*32
)
// 枚举的格式化
type StatusCode uint32
const (
_ = iota
StatusCode1 StatusCode = 1 << iota
StatusCode2
StatusCode3
StatusCodeError
)
func (s StatusCode) String() string {
switch s {
case StatusCode1:
return fmt.Sprintf("Code: %s", "StatusCode1")
case StatusCode2:
return fmt.Sprintf("Code: %s", "StatusCode1")
case StatusCode3:
return fmt.Sprintf("Code: %s", "StatusCode1")
default:
return fmt.Sprintf("Code: %s", "StatusCodeError")
}
}
func main() {
var c StatusCode = 10
fmt.Println(StatusCode1)
fmt.Println(StatusCode2)
fmt.Println(StatusCode3)
fmt.Println(c)
}
输出:
Code: StatusCode1
Code: StatusCode1
Code: StatusCode1
Code: StatusCodeError
上文列举了const+iota配合的一些例子,用以实现枚举。那么问题就来了,为何go语言要搞的这个麻烦?感觉违反了其号称的"简洁性"。我们可以这样理解他的设计:
首先,"枚举"这个概念体现的是一个程序设计的期望,期望能有一组具有明确、特定的业务含义的状态列表,这些列表不需要修改,更多的情况是作为常量使用,来进行状态判定等操作。
那么,这里就引出来枚举的两个特征:
- 第一:一组变量列表
- 第二:常量属性
这两个特性在go语言中,通过批量const + iota完全可以搞定,没必要再额外的定义出来一个关键字来完成,因为即使有一个关键字,其承载形式、写法、规则可能是大多相似,参考c的写法:
enum DAY
{
MON=1,
TUE,
WED,
THU,
FRI,
SAT,
SUN
};
如果是用go的const+iota,如下:
type DAY uint8 const ( _ DAY = 1 << iota MON TUE WED THU FRI SAT SUN )
这两者几乎没有区别,并且因为 iota 的加入让go的定义变得更为可靠、易用。
这里引申出另一个问题,iota是个什么?
iota是一个内置函数,其在编译期间生效,用以获取在const列表中变量的index。
2. 实现原理const是怎么实现不可修改这个特性的?
因为const修饰的变量是在编译期间,直接进行的展开,是一个常量,存储在程序的全局区-常量区,而这个区的数据不可寻址、不可修改。因而,被const修饰的变量不可修改。
3. 一些问题对const变量取地址会发生什么?
package main
const cl = 100
var bl = 123
func main() {
println(&bl,bl)
println(&cl,cl)
}
常量不同于变量的在运行期分配内存,常量通常会被编译器在预处理阶段直接展开,作为指令数据使用,存储在全局区-常量区,是没有地址的。因此这个会在编译期间报错。
cannot take the address of cl
#golang工程师#