常量表示运行时恒定不可改变的值,通常是一些字面量。使用常量就可用一个易于阅读理解的标识符号来代替“魔法数字”,也使得在调整常量值时,无须修改所有引用代码。

常量值必须是编译期可确定的字符、字符串、数字或布尔值。可指定常量类型,或由编译器通过初始化值推断,不支持 C/C++ 数字类型后缀。

const x, y int = 123, 0x22
const s = "hello,world!" 
const c = ''              // rune(unicode code point) 
  
const ( 
   i, f = 1, 0.123         // int,float64(默认) 
   b   = false
)

可在函数代码块中定义常量,不曾使用的常量不会引发编译错误。

func main() { 
   const y = 1.23          // 未使用,不会引发编译错误 
 
    { 
       const x = "abc"            // 在不同作用域定义同名常量 
       println(x) 
    } 
}

如果显式指定类型,必须确保常量左右值类型一致,需要时可做显式转换。右值不能超出常量类型取值范围,否则会引发溢出错误。

const ( 
   x, y int   = 99, -999
   b  byte    = byte(x)       // x被指定为int类型,须显式转换为byte类型 
   n          = uint8(y)       // 错误: constant-999 overflows uint8
)

常量值也可以是某些编译器能计算出结果的表达式,如 unsafe. Sizeof、len、cap 等。

import "unsafe" 
  
const ( 
   ptrSize = unsafe.Sizeof(uintptr(0)) 
   strSize = len("hello,world!") 
)

在常量组中如不指定类型和初始化值,则与上一行非空常量右值(表达式文本)相同。

const ( 
   x uint16 = 120
   y               // 与上一行x类型、右值相同 
   s  = "abc"                
   z               // 与s类型、右值相同 
)

变量有“零值”,常量没有

// const declaration cannot have type without expression
// missing value in const declaration
 
const (
	a int
)
 
const b bool

枚举

Go 并没有明确意义上的 enum 定义,不过可借助 iota 标识符实现一组自增常量值来实现枚举类型。

const ( 
   x  = iota  // 0
   y     // 1
   z     // 2
) 
 
const( 
    _   = iota      // 0
   KB = 1<< (10*iota) // 1 << (10*1) 
   MB        // 1 << (10*2) 
   GB        // 1 << (10*3) 
)

自增作用范围为常量组。可在多常量定义中使用多个 iota,它们各自单独计数,只须确保组中每行常量的列数量相同即可。

const( 
    _, _ = iota, iota*10   // 0, 0*10
   a, b           // 1, 1*10
   c, d           // 2, 2*10
)

如中断 iota 自增,则必须显式恢复。且后续自增值按行序递增,而非 C enum 那般按上一取值递增。

const( 
   a  = iota          // 0
   b             // 1
   c  = 100           // 100
   d             // 100(与上一行常量右值表达式相同) 
   e  = iota          // 4(恢复iota自增,计数包括c、d) 
   f             // 5
)

自增默认数据类型为 int,可显式指定类型。

const ( 
   a          = iota      // int
   b float32  = iota    // float32
   c          = iota      // float32(如不显式指定iota,则与b数据类型相同) 
)

在实际编码中,建议用自定义类型实现用途明确的枚举类型。但这并不能将取值范围限定在预定义的枚举值内。

type color byte   // 自定义类型 
  
const ( 
   black color = iota       // 指定常量类型 
   red
   blue
) 
  
func test(c color) { 
   println(c) 
} 
  
func main() { 
   test(red) 
   test(100)           // 100并未超出color/byte类型取值范围 
  
   x := 2     
   test(x)         // 错误: cannot use x(type int)as type color in argument to test
}

展开

常量除“只读”外,和变量究竟有什么不同?

var x = 0x100
const y = 0x200
  
func main() { 
   println(&x, x) 
   println(&y, y)       // 错误: cannot take the address of y
}

不同于变量在运行期分配存储内存(非优化状态),常量通常会被编译器在预处理阶段直接展开,作为指令数据使用。

数字常量不会分配存储空间,无须像变量那样通过内存寻址来取值,因此无法获取地址。

提到常量展开,我们还须回头看看常量的两种状态对编译器的影响。

const x = 100         // 无类型声明的常量 
const y byte = x       // 直接展开x,相当于const y byte = 100
  
const a int = 100      // 显式指定常量类型,编译器会做强类型检查 
const b byte = a       // 错误: cannot use a(type int)as type byte in const initializer