# Golang 中的常量 ## 1. 常量定义及分类 ### 1.1. 定义 常量是指在程序运行时,不会被修改的量。 常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。 常量的定义格式: ```go const identifier [type] = value ``` 可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。 * 显式类型定义 ```go const b string = "abc" ``` * 隐式类型定义 ```go const b = "abc" ``` 多个相同类型的声明可以简写为: ```go const name1, name2 = value1, value2 ``` ### 1.2. 分类 ```mermaid flowchart LR const(常量) int(整形常量) plural(复数常量) char(字符常量) float(浮点常量) string(字符串常量) int_ins0(21) int_ins1(0xAbcdef) plural_ins0(0.25i) plural_ins1(1.e+4i) char_ins0('a') char_ins1('\t') char_ins2('中') float_ins0(0.2) float_ins1(1.e+0) float_ins2(1E4) string_ins0(''hello world'') string_ins1(''程序员'') const---int int---int_ins0 int---int_ins1 const---plural plural---plural_ins0 plural---plural_ins1 const---char char---char_ins0 char---char_ins1 char---char_ins2 const---float float---float_ins0 float---float_ins1 float---float_ins2 const---string string---string_ins0 string---string_ins1 ``` * 字符常量:Go 的源码采用的是 UTF-8 的编码方式,UTF-8 的字符占用的字节数可以有 1~4 个字节,Rune 字符常量也有多种表现形式,但是使用''(单引号)将其括住。 * 字符串常量:字符串常量基本表现形式就是使用""(双引号)将字符序列包括在内,双引号里面可以是 UTF-8 的字符字面量,也可以是其编码值。 ## 2. 常量使用 ```go package main import "fmt" func main() { const LENGTH int = 10 const WIDTH int = 5 var area int const a, b, c = 1, false, "str" //多重赋值 area = LENGTH * WIDTH fmt.Printf("面积为 : %d", area) println() println(a, b, c) } // 输出结果为: // 面积为 : 50 // 1 false str ``` ## 3. 常量作为枚举 ```go const ( Unknown = 0 Female = 1 Male = 2 ) ``` 数字 0、1 和 2 分别代表未知性别、女性和男性。 常量可以用 len(), cap(), unsafe.Sizeof() 函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过: 在包一级声明语句声明的名字可在整个包对应的每个源文件中访问,而不是仅仅在其声明语句所在的源文件中访问。如下代码中的 a 、b 、c 。 ```go package main import "unsafe" const ( a = "abc" b = len(a) c = unsafe.Sizeof(a) ) func main(){ println(a, b, c) } // 运行结果为: // abc 3 16 ``` **注意:字符串类型在 go 里是个结构, 包含指向底层数组的指针和长度,这两部分每部分都是 8 个字节,所以字符串类型大小为 16 个字节。** ## 4. iota 常量 iota 特殊常量,可以认为是一个可以被编译器修改的常量。 iota 在 const 关键字出现时将被重置为 0 (const 内部的第一行之前),const 中每新增一行常量声明将使iota 计数一次 ( iota 可理解为 const 语句块中的行索引)。 iota 可以被用作枚举值: ```go const ( a = iota b = iota c = iota ) ``` 第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1 ;所以 a=0, b=1, c=2 可以简写为如下形式: ```go const ( a = iota b c ) ``` 使用示例: ```go package main import "fmt" func main() { const ( a = iota //0 b //1 c //2 d = "ha" //独立值,iota += 1 e //"ha" iota += 1 f = 100 //iota +=1 g //100 iota +=1 h = iota //7,恢复计数 i //8 ) fmt.Println(a,b,c,d,e,f,g,h,i) } // 输出结果为: // 0 1 2 ha ha 100 100 7 8 ``` ### 4.1. const 作用域只在当前的括号范围内 iota 的含义是初始化一个计数器,这个计数器的影响范围只能是 const 后括号作用域范围内的常量。 ```go package main import ( "fmt" ) func main() { const ( a = iota b c d e ) const ( A = iota B C D ) fmt.Println(a, b, c, d, e) fmt.Println(A, B, C) } // 输出结果如下: // 0 1 2 3 4 // 0 1 2 ``` iota 把初始值 0 赋给 a 后,自增 1,此时 iota 等于1,随后将 1 赋值给常量 b,赋值完成后,iota 又自增 1,此时 iota 等于2,这个自增过程,只会出现在 const 后括号范围内,超过这个括号作为域。再次使用 iota 时,其初始值为 0。从输出结果可知,常量 A 从 0 开始,可以证实这个观点。 ### 4.2. const 作用域内,iota 自增 1 ```go package main import ( "fmt" ) func main() { const ( NUM1 = iota NUM2 NUM3 = iota NUM4 NUM5 ) const ( A = iota B C D ) fmt.Println(NUM1, NUM2, NUM3, NUM4, NUM5) fmt.Println(A, B, C) } // 输出信息是: // 0 1 2 3 4 // 0 1 2 ``` ### 4.3. iota 不会自动初始化括号作用域内 iota 前边的常量 ```go // 错误示例代码 func main() { const ( a b c = iota d e ) fmt.Println(a, b, c, d, e) } ``` 编译时产生的错误信息: ```bash ./hello.go:9:9: missing value in const declaration ./hello.go:10:9: missing value in const declaration ./hello.go:15:17: undefined: a ./hello.go:15:20: undefined: b ``` 从上边的示例代码中可以得知,iota 并不会给括号作用域范围内使用 iota 赋值的那个常量之前的常量赋值,只会给括号作用域内使用 iota 初始化的那个常量后边的所有常量自增 1 后赋值。 ### 4.4. 设定从某个特定初始值开始 iota 默认初始值为 0,我们可以定义的常量初始值从 10 开始,代码如下: ```go func main() { const ( a = 10 + iota b c d e ) fmt.Println(a, b, c, d, e) } // 输出结果: // 10 11 12 13 14 ``` 当使用带 iota 的表达式初始化常量时,括号作用域内,后边的常量在初始化时,也会使用这个表达式进行初始化。相当于初始化表达式是:10+1,b 初始化的值是:10+2,以此类推。整个初始化过程中,依然是 iota 在自增 1。 ### 4.5. const 常量带有多个 iota 表达式 ```go func main() { const ( a = iota + 10 b = iota c = iota + 5 d e ) fmt.Println(a, b, c, d, e) } // 输出信息是: // 10 1 7 8 9 ``` 从上边的输出信息可以得知,在使用表达式初始化常量时,会使用离被初始化常量前边最近的那个表达式。如初始化 c 时,使用的是 2+5,初始化 b 时,使用的是 iota,此时的 iota 值为 1。 ## 5. 定义常量组不设置初始值 在定义常量组时,如果不提供初始值,则表示将使用上行的表达式值。 ```go package main import "fmt" const ( a = 1 b c d ) func main() { fmt.Println(a) // b、c、d没有初始化,使用上一行(即a)的值 fmt.Println(b) // 输出1 fmt.Println(c) // 输出1 fmt.Println(d) // 输出1 } ```