diff --git a/ch3/ch3-06-2.md b/ch3/ch3-06-2.md index fe336c2..88f5244 100644 --- a/ch3/ch3-06-2.md +++ b/ch3/ch3-06-2.md @@ -1,4 +1,104 @@ ### 3.6.2. 無類型常量 -TODO + +Go语言的常量有点不寻常. 虽然一个常量可以有任意有一个确定的基础类型, 例如 int 或 float64, 或者是类似 time.Duration 这样命名的基础类型, 但是许多常量并没有一个明确的基础类型. 编译期为这些没有明确的基础类型的数字常量提供比基础类型或机器更高精度的算术运算; 你可以认为至少有256bit的运算精度. 这里有六种未明确类型的常量类型, 分别是 无类型的布尔型, 无类型的整数, 无类型的字符, 无类型的浮点数, 无类型的复数, 无类型的字符串. + +通过延迟明确具体类型, 无类型的常量不仅可以提供更高的精度, 而且可以直接用于更多的表达式而不需要类型转换. 例如 例子中的 ZiB 和 YiB 的值已经超出任何Go中整数类型能表达的范围, 但是它们依然是合法的常量, 而且可以像下面表达式这样使用: + +```Go +fmt.Println(YiB/ZiB) // "1024" +``` + +另一个例子, math.Pi 无类型的浮点数常量, 可以直接用于任意需要浮点数或复数的地方: + +```Go +var x float32 = math.Pi +var y float64 = math.Pi +var z complex128 = math.Pi +``` + +如果 math.Pi 被确定为特定类型, 比如 float64, 那么结果精度可能会不一样, 同时对于需要float32或complex128类型值的地方会需要一个明确的类型转换: + +```Go +const Pi64 float64 = math.Pi + +var x float32 = float32(Pi64) +var y float64 = Pi64 +var z complex128 = complex128(Pi64) +``` + +对于常量面值, 不同的写法对应不同的类型. 例如 0, 0.0, 0i, 和 '\u0000' 虽然有着相同的常量值, 但是它们分别对应无类型的整数,无类型的浮点数,无类型的复数,和无类型的字符等不同的常量类型. 同样, true 和 false 也是无类型的布尔类型, 字符串面值常量是无类型的字符串. + +前面说过除法运算符 / 根据操作数的类型生成对应类型的结果. 因此, 不同写法的常量除法表达式可能对应不同的结果: + +```Go +var f float64 = 212 +fmt.Println((f - 32) * 5 / 9) // "100"; (f - 32) * 5 is a float64 +fmt.Println(5 / 9 * (f - 32)) // "0"; 5/9 is an untyped integer, 0 +fmt.Println(5.0 / 9.0 * (f - 32)) // "100"; 5.0/9.0 is an untyped float +``` + +只有常量可以是无类型的. 当一个无类型的常量被赋值给一个变量, 就像上面的第一行语句, 或者是像其余三个语句中右边表达式中含有明确类型的值, 无类型的常量将会被隐式转换为对应的类型, 如果可能的话. + +```Go +var f float64 = 3 + 0i // untyped complex -> float64 +f = 2 // untyped integer -> float64 +f = 1e123 // untyped floating-point -> float64 +f = 'a' // untyped rune -> float64 +``` + +上面的语句相当于: + +```Go +var f float64 = float64(3 + 0i) +f = float64(2) +f = float64(1e123) +f = float64('a') +``` + +无论是隐式或显式, 将一种类型转换为另一种类型要求目标可以表示原始值. 对于浮点数和复数, 可能会有舍入处理: + +```Go +const ( + deadbeef = 0xdeadbeef // untyped int with value 3735928559 + a = uint32(deadbeef) // uint32 with value 3735928559 + b = float32(deadbeef) // float32 with value 3735928576 (rounded up) + c = float64(deadbeef) // float64 with value 3735928559 (exact) + d = int32(deadbeef) // compile error: constant overflows int32 + e = float64(1e309) // compile error: constant overflows float64 + f = uint(-1) // compile error: constant underflows uint +) +``` + +对于一个没有显式类型的变量声明(包括短变量声明语法), 无类型的常量会被隐式转为默认的变量类型, 就像下面的例子: + +```Go +i := 0 // untyped integer; implicit int(0) +r := '\000' // untyped rune; implicit rune('\000') +f := 0.0 // untyped floating-point; implicit float64(0.0) +c := 0i // untyped complex; implicit complex128(0i) +``` + +注意默认类型是规则的: 无类型的整数常量默认转换为int, 对应不确定的尺寸, 但是浮点数好复数常量则默认转换为float64和complex128. Go语言本身并没有不确定的尺寸的浮点数和复数类型, 因为如何不知道浮点数类型的话很难写出正确的数值算法. + +如果要给变量一个不同的类型, 我们必须显式地将无类型的常量转化为所需的类型, 或给声明的变量指定类型, 像下面例子这样: + +```Go +var i = int8(0) +var i int8 = 0 +``` + +当尝试将这些无类型的常量转为一个接口值时(见第7章), 这些默认类型将显得尤为重要, 因为要靠它们明确接口对应的动态类型. + +```Go +fmt.Printf("%T\n", 0) // "int" +fmt.Printf("%T\n", 0.0) // "float64" +fmt.Printf("%T\n", 0i) // "complex128" +fmt.Printf("%T\n", '\000') // "int32" (rune) +``` + +现在我们已经讲述了Go语言中全部的基础数据类型. 下一步将演示如何用基础数据类型组合成数组或结构体等复杂数据类型, 然后构建用于解决实际编程问题的数据结构, 这将是第四章的讨论主题. + + +