gopl-zh.github.com/ch3/ch3-06-2.md

5.2 KiB
Raw Blame History

3.6.2. 無類型常量

Go語言的常量有個不同尋常之處。雖然一個常量可以有任意有一個確定的基礎類型例如int或float64或者是類似time.Duration這樣命名的基礎類型但是許多常量併沒有一個明確的基礎類型。編譯器爲這些沒有明確的基礎類型的數字常量提供比基礎類型更高精度的算術運算你可以認爲至少有256bit的運算精度。這里有六種未明確類型的常量類型分别是無類型的布爾型、無類型的整數、無類型的字符、無類型的浮點數、無類型的複數、無類型的字符串。

通過延遲明確常量的具體類型無類型的常量不僅可以提供更高的運算精度而且可以直接用於更多的表達式而不需要顯式的類型轉換。例如例子中的ZiB和YiB的值已經超出任何Go語言中整數類型能表達的范圍但是它們依然是合法的常量而且可以像下面常量表達式依然有效譯註YiB/ZiB是在編譯期計算出來的併且結果常量是1024是Go語言int變量能有效表示的

fmt.Println(YiB/ZiB) // "1024"

另一個例子math.Pi無類型的浮點數常量可以直接用於任意需要浮點數或複數的地方

var x float32 = math.Pi
var y float64 = math.Pi
var z complex128 = math.Pi

如果math.Pi被確定爲特定類型比如float64那麽結果精度可能會不一樣同時對於需要float32或complex128類型值的地方則會強製需要一個明確的類型轉換

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也是無類型的布爾類型字符串面值常量是無類型的字符串類型。

前面説過除法運算符/會根據操作數的類型生成對應類型的結果。因此,不同寫法的常量除法表達式可能對應不同的結果:

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

隻有常量可以是無類型的。當一個無類型的常量被賦值給一個變量的時候,就像上面的第一行語句,或者是像其餘三個語句中右邊表達式中含有明確類型的值,無類型的常量將會被隱式轉換爲對應的類型,如果轉換合法的話。

var f float64 = 3 + 0i // untyped complex -> float64
f = 2                  // untyped integer -> float64
f = 1e123              // untyped floating-point -> float64
f = 'a'                // untyped rune -> float64

上面的語句相當於:

var f float64 = float64(3 + 0i)
f = float64(2)
f = float64(1e123)
f = float64('a')

無論是隱式或顯式轉換,將一種類型轉換爲另一種類型都要求目標可以表示原始值。對於浮點數和複數,可能會有舍入處理:

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
)

對於一個沒有顯式類型的變量聲明語法(包括短變量聲明語法),無類型的常量會被隱式轉爲默認的變量類型,就像下面的例子:

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語言本身併沒有不確定內存大小的浮點數和複數類型而且如果不知道浮點數類型的話將很難寫出正確的數值算法。

如果要給變量一個不同的類型,我們必鬚顯式地將無類型的常量轉化爲所需的類型,或給聲明的變量指定明確的類型,像下面例子這樣:

var i = int8(0)
var i int8 = 0

當嚐試將這些無類型的常量轉爲一個接口值時見第7章這些默認類型將顯得尤爲重要因爲要靠它們明確接口對應的動態類型。

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語言中全部的基礎數據類型。下一步將演示如何用基礎數據類型組合成數組或結構體等複雜數據類型然後構建用於解決實際編程問題的數據結構這將是第四章的討論主題。