無論你是學習 C/C++ 還是 Java,肯定遇到過常量這種概念。Go 語言當然也有常量,在前面的文章裡,你肯定遇見過很多次了。下面就是在 Go 裡定義的一個常量。
const pi = 3.1415926
不過很奇怪的是,為什麼定義常量的時候沒有指定類型?它也是自動推導的嗎?這個問題待會我們再說清楚。
1. 一些特性
- Go 語言的常量是在編譯期進行計算的
- 常量之間所有的運算,結果仍然是常量。這個特性可以提升程式的運作效率,将運作期的計算轉移到編譯期完成,是一種常見的程式設計技法。
舉個例子:
const a = 2
const b = 2 * a // b 在編譯期完成計算
2. const 關鍵字
正如上述所見,Go 中使用 const 關鍵字聲明常量。
- 例 1
package main
import "fmt"
const (
pi = 3.141592653589793238462643383279
e = 2.718281828459045235360287471352
i = 100
ii = 2 * i
c = 4 + 0i
r = 'a'
rr = '中'
s = "中國"
b = true
)
func main() {
fmt.Printf("%T: %[1]v\n", pi) // float64: 3.141592653589793
fmt.Printf("%T: %[1]v\n", e) // float64: 2.718281828459045
fmt.Printf("%T: %[1]v\n", i) // int: 100
fmt.Printf("%T: %[1]v\n", ii) // int: w00
fmt.Printf("%T: %[1]v\n", c) // complex128: (4+0i)
fmt.Printf("%T: %[1]v\n", r) // int32: 97
fmt.Printf("%T: %[1]v\n", rr) // int32: 20013
fmt.Printf("%T: %[1]v\n", s) // string: 中國
fmt.Printf("%T: %[1]v\n", b) // bool: true
var x float64 = i
fmt.Printf("%T: %[1]v\n", x) // float64: 100
fmt.Printf("%T: %[1]v\n", i/6)// int: 16
- 例 2
package main
import "fmt"
func main() {
const (
a = 8
b = 2.2 * a
c float64 = 5
)
fmt.Printf("%T %[1]v\n", a) // int 8
fmt.Printf("%T %[1]v\n", b) // float64 17.6
fmt.Printf("%T %[1]v\n", c) // float64 5
上面的兩個例子裡,大多數 const 聲明的常量都是沒有指定類型的,但是例 2 中,你也可以顯式指定類型。沒有指定類型的常量,在 Go 裡稱之為無類型常量(untyped constant),但并不是真的就是沒有類型。接下來,我們來說說這種 untyped constant 是啥。
2.1 untyped constant
實際上,沒有顯式指定 type 的常量,在 Go 裡稱為 uncommitted constant (未送出、未明确的常量),意思就是說這種常量的類型暫時還不能确認,以後在用到它的時候再确認也不遲。
換言之,那些顯式指定了類型的 constant 稱為 committed constant.
舉例:
const a = 10
fmt.Printf("%T\n", a)
var b float64 = 4 * a // 在需要的時候,a 轉變成了 float64
fmt.Printf("%T\n", b)
在 Go 裡,uncommitted constant 擁有比普通的
int/float64
更高的運算精度(gopl 說可以認為其精度達到 256bit)。
前面我們也說了,untyped constant 并不是說它沒有類型,實際上在第 1 節的兩個例子中,我們看到了 untyped constant 可以通過
Printf
函數列印出其類型,這是因為在 constant 在此處隐式轉換成了對應基礎類型的常量。
這些隐式轉換的規則是什麼?
Go 裡,有 6 種 uncommitted constant,分别是:
- untyped boolean
- untyped integer (隐式轉換成 int)
- untyped rune (隐式轉換成 int32)
- untyped floaing-point (隐式轉換成 float64)
- untyped complex (隐匿轉換成 complex128)
- untyped string
uncommitted constant 另一個特點是在做類型轉換時,不需要顯式的進行強制轉換,前面也說了,它會在需要的時候進行類型轉換。
3. iota

在 Go 裡,iota 被稱之為 constant generator (常量生成器)。你可以把 iota 了解成一個函數(并不真的是函數,它是由編譯器自動處理的),這個函數每使用一次,傳回的值就自動 + 1.
- 例 1
package main
import "fmt"
const (
a = iota
b = iota + 9
c = iota - 4
d = iota
)
func main() {
fmt.Printf("%T: %[1]v\n", a) // int: 0
fmt.Printf("%T: %[1]v\n", b) // int: 10
fmt.Printf("%T: %[1]v\n", c) // int: -2
fmt.Printf("%T: %[1]v\n", d) // int: 3
- 例 2
package main
import "fmt"
const (
Sunday = iota
Monday // = iota
Tuesday
Wednesday
Thursday
Friday
Saturday
)
func main() {
fmt.Printf("%T: %[1]v\n", Sunday) // int: 0
fmt.Printf("%T: %[1]v\n", Monday) // int: 1
fmt.Printf("%T: %[1]v\n", Tuesday) // int: 2
fmt.Printf("%T: %[1]v\n", Wednesday) // int: 3
fmt.Printf("%T: %[1]v\n", Thursday) // int: 4
fmt.Printf("%T: %[1]v\n", Friday) // int: 5
fmt.Printf("%T: %[1]v\n", Saturday) // int: 6
- 例3
package main
import "fmt"
const (
a = 1 << iota
b // = 1 << iota,後面都不用寫了,都一樣
c
d
)
func main() {
fmt.Printf("%T: %[1]v\n", a) // int: 1
fmt.Printf("%T: %[1]v\n", b) // int: 2
fmt.Printf("%T: %[1]v\n", c) // int: 4
fmt.Printf("%T: %[1]v\n", d) // int: 8
- 例 4
package main
import "fmt"
const (
_ = 1 << (10 * iota)
KiB
MiB
GiB
TiB
PiB
EiB
ZiB
YiB
)
func main() {
fmt.Printf("%T: %[1]v\n", KiB) // int: 1024
fmt.Printf("%T: %[1]v\n", MiB) // int: 1048576
fmt.Printf("%T: %[1]v\n", GiB) // int: 1073741824
fmt.Printf("%T: %[1]v\n", TiB) // int: 1099511627776
fmt.Printf("%T: %[1]v\n", PiB) // int: 1125899906842624
fmt.Printf("%T: %[1]v\n", EiB) // int: 1152921504606846976
// 還記得嗎?常量之間的運算結果仍然是常量。YiB/ZiB 是編譯期計算出來的。
fmt.Printf("%T: %[1]v\n", YiB/ZiB) // int: 1024
// 下面這兩行不注釋掉,編譯會報錯。
// fmt.Printf("%T: %[1]v\n", ZiB)
// fmt.Printf("%T: %[1]v\n", YiB)
4. 總結
- 掌握 const 關鍵字
- 掌握 iota