diff --git a/ch3/ch3-06.md b/ch3/ch3-06.md index 821def0..d68eb0d 100644 --- a/ch3/ch3-06.md +++ b/ch3/ch3-06.md @@ -1,8 +1,67 @@ ## 3.6. 常量 -TODO + +常量表达式的值在编译期计算, 而不是在运行期. 每种常量的潜在类型都是基础类型: boolean, string, 或数字. + +一个常量的声明语句定义了常量的名字, 和变量的声明语法类似, 常量的值不可修改, 这样可以防止在运行期被意外或恶意的修改. 例如, 常量比变量更适合用于表达像 π 之类的数学常数, 因为它们的值不会变化: + +```Go +const pi = 3.14159 // approximately; math.Pi is a better approximation +``` + +和变量声明一样, 可以批量声明多个常量; 这比较适合声明一组相关的常量: + +```Go +const ( + e = 2.71828182845904523536028747135266249775724709369995957496696763 + pi = 3.14159265358979323846264338327950288419716939937510582097494459 +) +``` + +许多常量的运算可以在编译期完成, 这样可以减少运行时的工作, 也方便其他编译优化. 当操作数是常量时, 一些运行时的错误可以在编译时发现, 例如整数除零, 字符串索引越界, 任何导致无效浮点数的操作等. + +常量间的所有算术运算, 逻辑运算和比较运算的结果也是常量, 对常量的类型转换操作或以下函数调用都是返回常量结果: len, cap, real, imag, complex, 和 unsafe.Sizeof(§13.1). + +因为它们的值是在编译期就确定的, 因此常量可以是构成类型的一部分, 例如用于指定数组类型的长度: + +```Go +const IPv4Len = 4 + +// parseIPv4 parses an IPv4 address (d.d.d.d). +func parseIPv4(s string) IP { + var p [IPv4Len]byte + // ... +} +``` + +一个常量的声明也可以包含一个类型和一个值, 但是如果没有显式指明类型, 那么将从右边的表达式推断类型. 在下面的代码中, time.Duration 是一个命名类型, 底层类型是 int64, time.Minute 是对应类型的常量. 下面声明的两个常量都是 time.Duration 类型, 可以通过 %T 参数打印类型信息: + +```Go +const noDelay time.Duration = 0 +const timeout = 5 * time.Minute +fmt.Printf("%T %[1]v\n", noDelay) // "time.Duration 0" +fmt.Printf("%T %[1]v\n", timeout) // "time.Duration 5m0s +fmt.Printf("%T %[1]v\n", time.Minute) // "time.Duration 1m0s" +``` + +如果是批量声明的常量, 除了第一个外其他常量的右边的表发生可以省略, 如果省略则表示使用前面的表达式, 对应的常量类型也一样. 例如: + +```Go +const ( + a = 1 + b + c = 2 + d +) + +fmt.Println(a, b, c, d) // "1 1 2 2" +``` + +如果只是简单地复制右边的常量表达式, 并没有太实用的价值. 但是它可以带来其他的特性, 那就是 iota 常量生成器. {% include "./ch3-06-1.md" %} {% include "./ch3-06-2.md" %} + +