常量是在程序运行过程中,其值不可以发生改变的数据
,常量无法被获取地址
常量中的数据类型能是布尔型、数字型(整型、浮点型和复数型)、字符串
常量的定义是通过const
关键字完成的,并且常量名称大写为公开,小写为私有,一般定义在函数外,定义在函数内属于局部常量
常量和变量一样,可以省略数据类型,由编译器推断完成
常量的定义
const Name string = "不知火舞"
const Age = 99
const Address = "峡谷76号"
// 当常量比较多的时候可以使用常量块来表示,比较简洁
const (
USERNAME = "干将莫邪"
AGE = 88
ADDRESS = "中路草丛"
)
// 也可以批量赋值
const name, age, address = "李白", 99, "峡谷野区草丛"
常量除不可以再次赋值外,和变量究竟有什么不同?
常量不会分配存储空间,无需像变量那样通过内存地址取值,因此无法获取内存地址。
常量值不能修改,变量可以
const NAME = "itzhuzhu"
func main() {
NAME = "haha" // 报错,不能修改
}
函数内外都有相同常量时采用就近原则
const (
USERNAME = "干将莫邪"
AGE = 88
ADDRESS = "中路草丛"
)
func main() {
const USERNAME, AGE, ADDRESS = "itzhuzhu", 24, "中国"
fmt.Println(USERNAME, AGE, ADDRESS) // 就近原则打印的是itzhuzhu
}
定义常量不使用不会报编译错误,不同作用域也可以定义相同常量
func main() {
const X = 123
fmt.Println("我是外面的:", &X)
const Y = 1.23 // 未使用不会引发编译错误
{
const X = 321 // 不同作用域定义同名常量
fmt.Println("我是里面的:", &X)
}
}
常量值也可以是某些编译器能直接计算结果的表达式,如unsafe.Sizeof、len、cap等
func main() {
const (
ptrSize = unsafe.Sizeof(uintptr(0)) // 函数返回操作数在内存中的字节大小
strSize = len("hello, world!")
)
fmt.Println(ptrSize)
fmt.Println(strSize)
}
在常量组中如不指定类型和初始化值,则与上一行非空常量的值相同
func main() {
const (
X uint16 = 120
Y // 与上一行x类型、右值相同
W = "abc"
Z // 与s类型、右值相同
)
fmt.Printf("%T, %v\n", Y, Y) // 输出类型和值
fmt.Printf("%T, %v\n", Z, Z)
}
打印结果
uint16, 120
string, abc
显示常量和隐式定义常量区别,这里没搞懂为什么隐式不会做强类型检查
func main() {
const x = 100 // 隐式无常量类型,值随便写
const y byte = x // 相当于const y byte = 100
const a int = 100 // 显式指定常量类型,编译器会做强类型検查
const b byte = a // 错误:无法将 'a' (类型 int) 用作类型 byt
const b byte = byte(a) // 只能通过强转
}
枚举就是将数据值一一列出来,枚举可以用来表示一些固定的值,枚举是常量组成的。在Go可以通过iota实现枚举的功能。
iota是Go语言的常量计数器,const出现时,会将iota初始化为0,const中每新增一行iota就会计数一次,递增默认数据类型为int
iota可以持续递增
const (
a = 1
b = 2
c = 3
)
上面代码使用iota可以简化为
const (
a = iota // 第一行 重置为0 a:0
b // 第二行 新增1行 b:1
c // 第三行 新增1行 c:2
)
自增默认类型为int,不过可显式指定常量类型
const (
a = iota // int iota = 0
b float32 = iota // float32 iota = 1
c // float32 iota = 2
)
fmt.Println(a, b, c)
fmt.Printf("%T\n%T\n%T\n", a, b, c)
_跳过当前常量值
const (
a = iota // 0
_ // 1
c // 2
d // 3
)
fmt.Println(a, c, d)
使用位移计算
const (
_ = iota // 0
KB = 1 << (10 * iota) // 1 << (10 * 1)
MB // 1 << (10 * 2)
GB // 1 << (10 * 3)
)
如果中断iota递增,必须再次遇到iota才会恢复按行递增,否则将沿用上一次取值
const (
a = 1 + iota // 第0行 重置为0 a = 1 + 0
b // 第1行 新增1行 b = 1 + 1
c // 第2行 新增1行 c = 1 + 2
d = 4 // 第3行 新增1行 d = 4
e // 第4行 新增1行 e = 4 与上一行常量右值表达式相同
f // 第5行 新增1行 f = 4 只要不遇到iota,就一直是上次赋值的值,就是4
g = iota // 第6行 重置为0 g = 6 这里遇到了iota,恢复iota自增,计数包括前面所有的常量
i // 第7行 新增1行 i = 7 接着g+1,如果再定义常量初始化,就还是和4一样循环
j // 第8行 新增1行 i = 8
)
func main() {
fmt.Println(a, b, c, d, e, f, g, i, j)
}
如果使用多个常量赋值的时候,后面的数据都会跟着第一行的格式走,直到下一个iota出现
const (
a, b = 1 + iota, 2 + iota // iota = 0 a = iota + 1 b = iota + 2 a = 1 ,b = 2
c, d // iota = 1 c = iota + 1 d = iota + 2 c = 2 ,d = 3
e, f // iota = 2 e = iota + 1 f = iota + 2 e = 3 ,f = 4
g, h = 3 + iota, 4 + iota // iota = 3 g = iota + 3 h = iota + 4 g = 6 ,h = 7
i, j // iota = 4 i = iota + 3 j = iota + 4 i = 7 ,j = 8
)
fmt.Println(a, b, c, d, e, f, g, h, i, j)
可以自定义类型来实现用途明确的枚举类型。但须注意,这并不能将取值限定在预定义的枚举常量值范围内。
func main() {
test(red)
test(10000000) // 错误:10000000超出color/byte类型取值范围。
x := 2
test(x) // 错误:无法将 'x' (类型 int) 用作类型 color
}
type color byte // 自定义类型
const (
black color = iota // 指定常量类型
red
blue
)
func test(c color) {
fmt.Println(c)
}