這篇文章將介紹常量和變量相關的知識。 類型不確定值、類型推斷和值的顯式類型轉換等概念也將被介紹。
上一章中提到的基本類型的字面量表示 (除了false
和true
)都屬于無名常量(unnamed constant),或者叫字面常量(literal constant)。 false
和true
是預聲明的兩個具名常量。本文將介紹如何聲明自定義的具名常量。
在Go中,有些值的類型是不確定的。換句話說,有些值的類型有很多可能性。 這些值稱為類型不確定值。對于大多數(shù)類型不確定值來說,它們各自都有一個默認類型, 除了預聲明的nil
。nil
是沒有默認類型的。 我們在后續(xù)的文章中將了解到很多關于nil
的知識。
與類型不確定值相對應的概念稱為類型確定值。
上一章提到的字面常量(無名常量)都屬于類型不確定值。 事實上,Go中大多數(shù)的類型不確定值都屬于字面常量和本文即將介紹的具名常量。 少數(shù)類型不確定值包括剛提到的nil
和以后會逐步接觸到的某些操作的布爾返回值。
一個字面(常)量的默認類型取決于它為何種字面量形式:
string
類型。bool
類型。int
類型。rune
(亦即int32
)類型。float64
類型。complex128
類型。和很多語言一樣,Go也支持類型轉換。 一個顯式類型轉換的形式為T(v)
,其表示將一個值v
轉換為類型T
。 編譯器將T(v)
的轉換結果視為一個類型為T
的類型確定值。 當然,對于一個特定的類型T
,T(v)
并非對任意的值v
都合法。
下面介紹的規(guī)則同時適用于上一章介紹的字面常量和即將介紹的類型不確定具名常量。
對于一個類型不確定常量值v
,有兩種情形顯式轉換T(v)
是合法的:
v
可以表示為T
類型的一個值。
轉換結果為一個類型為T
的類型確定常量值。
v
的默認類型是一個整數(shù)類型(int
或者rune
)
并且T
是一個字符串類型。
轉換T(v)
將v
看作是一個Unicode碼點。
轉換結果為一個類型為T
的字符串常量。
此字符串常量只包含一個Unicode碼點,并且可以看作是此Unicode碼點的UTF-8表示形式。
對于不在合法的Unicode碼點取值范圍內的整數(shù)v
,
轉換結果等同于字符串字面量"\uFFFD"
(亦即"\xef\xbf\xbd"
)。
0xFFFD
是Unicode標準中的(非法碼點的)替換字符值。
(但是請注意,今后的Go版本可能只允許rune或者byte整數(shù)被轉換為字符串。
從Go官方工具鏈1.15版本開始,go vet
命令會對從非rune和非byte整數(shù)到字符串的轉換做出警告。)
事實上,第二種情形并不要求v
必須是一個常量。 如果v
是一個常量,則轉換結果也是一個常量。 如果v
不是一個常量,則轉換結果也不是一個常量。
一些合法的轉換例子:
// 結果為complex128類型的1.0+0.0i。虛部被舍入了。
complex128(1 + -1e-1000i)
// 結果為float32類型的0.5。這里也舍入了。
float32(0.49999999)
// 只要目標類型不是整數(shù)類型,舍入都是允許的。
float32(17000000000000000)
float32(123)
uint(1.0)
int8(-123)
int16(6+0i)
complex128(789)
string(65) // "A"
string('A') // "A"
string('\u68ee') // "森"
string(-1) // "\uFFFD"
string(0xFFFD) // "\uFFFD"
string(0x2FFFFFFFF) // "\uFFFD"
下面是一些非法的轉換:
int(1.23) // 1.23不能被表示為int類型值。
uint8(-1) // -1不能被表示為uint8類型值。
float64(1+2i) // 1+2i不能被表示為float64類型值。
// -1e+1000不能被表示為float64類型值。不允許溢出。
float64(-1e1000)
// 0x10000000000000000做為int值將溢出。
int(0x10000000000000000)
// 字面量65.0的默認類型是float64(不是一個整數(shù)類型)。
string(65.0)
// 66+0i的默認類型是complex128(不是一個整數(shù)類型)。
string(66+0i)
從上面的例子可以看出,一個類型不確定數(shù)字值所表示的值可能溢出它的默認類型的表示范圍。 比如上例中的-1e1000
和0x10000000000000000
。 一個溢出了它的默認類型的表示范圍的類型不確定數(shù)字值是不能被轉換到它的默認類型的(將編譯報錯)。
注意,有時一個顯式轉換形式必須被寫成(T)(v)
以免發(fā)生歧義。 這種情況多發(fā)生在T
不為一個標識符的時候。
我們以后將在其它章節(jié)學到更多的顯式類型轉換規(guī)則。
Go支持類型推斷(type deduction or type inference)。 類型推斷是指在某些場合下,程序員可以在代碼中使用一些類型不確定值, 編譯器會自動推斷出這些類型不確定值在特定情景下應被視為某些特定類型的值。
在Go代碼中,如果某處需要一個特定類型的值并且一個類型不確定值可以表示為此特定類型的值, 則此類型不確定值可以使用在此處。Go編譯器將此類型不確定值視為此特定類型的類型確定值。 這種情形常常出現(xiàn)在運算符運算、函數(shù)調用和賦值語句中。
有些場景對某些類型不確定值并沒有特定的類型要求。在這種情況下,Go編譯器將這些類型不確定值視為它們各自的默認類型的類型確定值。
上述兩條類型推斷規(guī)則可以被視為隱式轉換規(guī)則。
本文下面的章節(jié)將展示一些類型推斷的例子。 后續(xù)其它文章將會展示更多類型推斷的例子和規(guī)則。
和無名字面常量一樣,具名常量也必須都是布爾、數(shù)字或者字符串值。 在Go中,關鍵字const
用來聲明具名常量。 下面是一些常量聲明的例子。
package main
// 聲明了兩個單獨的具名常量。(是的,
// 非ASCII字符可以用做標識符。)
const π = 3.1416
const Pi = π // 等價于:const Pi = 3.1416
// 聲明了一組具名常量。
const (
No = !Yes
Yes = true
MaxDegrees = 360
Unit = "弧度"
)
func main() {
// 聲明了三個局部具名常量。
const DoublePi, HalfPi, Unit2 = π * 2, π * 0.5, "度"
}
Go白皮書把上面每行含有一個等號=
的語句稱為一個常量描述(constant specification)。 每個const
關鍵字對應一個常量聲明。一個常量聲明中可以有若干個常量描述。 上面的例子中含有4個常量聲明。除了第3個,其它的常量聲明中都各自只有一個常量描述。 第3個常量聲明中有4個常量描述。
在上面的例子中,符號*
是一個乘法運算符, 符號!
是一個布爾取否運算符。 運算符將在下一篇文章中詳述。
常量聲明中的等號=
表示“綁定”而非“賦值”。 每個常量描述將一個或多個字面量綁定到各自對應的具名常量上。 或者說,每個具名常量其實代表著一個字面常量。
在上面的例子中,具名常量π
和Pi
都綁定到(或者說代表著)字面常量3.1416
。 這兩個具名常量可以在程序代碼中被多次使用,從而有效避免了字面常量3.1416
在代碼中出現(xiàn)在多處。 如果字面常量3.1416
在代碼中出現(xiàn)在多處, 當我們以后欲將3.1416
改為3.14
的時候,所有出現(xiàn)在代碼中的3.1416
都得逐個修改。 有了具名常量的幫助,我們只需修改對應常量描述中的3.1416
即可。 這是常量聲明的主要作用。當然常量聲明也可常常增加代碼的可讀性(代碼即注釋)。
以后,我們使用非常量這一術語表示不是常量的值。 下一節(jié)將要介紹的變量就屬于非常量。
注意,常量可以直接聲明在包中,也可以聲明在函數(shù)體中。 聲明在函數(shù)體中的常量稱為局部常量(local constant),直接聲明在包中的常量稱為包級常量(package-level constant)。 包級常量也常常被稱為全局常量。
包級常量聲明中的常量描述的順序并不重要。比如在上面的例子中, 常量描述No
和Yes
的順序可以掉換一下。
上面例子中聲明的所有常量都是類型不確定的。 它們各自的默認類型和它們各自代表的字面量的默認類型是一樣的。
我們可以在聲明一些常量的時候指定這些常量的確切類型。 這樣聲明的常量稱為類型確定具名常量。 在下面這個例子中,所有這4個聲明的常量都是類型確定的。 X
和Y
的類型都是float32
, A
和B
的類型都是int64
。
const X float32 = 3.14
const (
A, B int64 = -3, 5
Y float32 = 2.718
)
如果一個常量描述中包含多個類型確定常量,則這些常量的類型必然是一樣的, 比如上例中的A
和B
。
我們也可以使用顯式類型轉換來聲明類型確定常量。 下面的例子和上面的例子是完全等價的。
const X = float32(3.14)
const (
A, B = int64(-3), int64(5)
Y = float32(2.718)
)
欲將一個字面常量綁定到一個類型確定具名常量上,此字面常量必須能夠表示為此常量的確定類型的值。 否則,編譯將報錯。
const a uint8 = 256 // error: 256溢出uint8
const b = uint8(255) + uint8(1) // error: 256溢出uint8
const c = int8(-128) / int8(-1) // error: 128溢出int8
const MaxUint_a = uint(^0) // error: -1溢出uint
const MaxUint_b uint = ^0 // error: -1溢出uint
在上面的例子中,符號^
為位反運算符,符號+
為加法運算符,符號/
為除法運算符。
下面這個類型確定常量聲明在64位的操作系統(tǒng)上是合法的,但在32位的操作系統(tǒng)上是非法的。 因為一個uint
值在32位操作系統(tǒng)上的尺寸是32位, (1 << 64) - 1
將溢出uint
。(這里,符號<<
為左移位運算符。)
const MaxUint uint = (1 << 64) - 1
那么如何聲明一個代表著最大uint
值的常量呢? 我們可以用下面這個常量聲明來替換上面這個。下面這個聲明在64位和32位的操作系統(tǒng)上都是合法的。
const MaxUint = ^uint(0)
類似地,我們可以使用下面這個常量聲明來聲明一個具名常量來表示最大的int
值。(這里,符號>>
為右移位運算符。)
const MaxInt = int(^uint(0) >> 1)
使用類似的方法,我們可以聲明一個常量來表示當前操作系統(tǒng)的位數(shù),或者檢查當前操作系統(tǒng)是32位的還是64位的。
const NativeWordBits = 32 << (^uint(0) >> 63) // 64 or 32
const Is64bitOS = ^uint(0) >> 63 != 0
const Is32bitOS = ^uint(0) >> 32 == 0
這里,符號!=
和==
分別為不等于和等于比較運算符。
在一個包含多個常量描述的常量聲明中,除了第一個常量描述,其它后續(xù)的常量描述都可以只包含標識符列表部分。 Go編譯器將通過照抄前面最緊挨的一個完整的常量描述來自動補全不完整的常量描述。 比如,在編譯階段,編譯器會將下面的代碼
const (
X float32 = 3.14
Y // 這里必須只有一個標識符
Z // 這里必須只有一個標識符
A, B = "Go", "language"
C, _
// 上一行中的空標識符是必需的(如果
// 上一行是一個不完整的常量描述的話)。
)
自動補全為
const (
X float32 = 3.14
Y float32 = 3.14
Z float32 = 3.14
A, B = "Go", "language"
C, _ = "Go", "language"
)
iota
是Go中預聲明(內置)的一個特殊的具名常量。 iota
被預聲明為0
,但是它的值在編譯階段并非恒定。 當此預聲明的iota
出現(xiàn)在一個常量聲明中的時候,它的值在第n個常量描述中的值為n
(從0開始)。 所以iota
只對含有多個常量描述的常量聲明有意義。
iota
和常量描述自動補全相結合有的時候能夠給Go編程帶來很大便利。 比如,下面是一個使用了這兩個特性的例子。 請閱讀代碼注釋以了解清楚各個常量被綁定的值。
package main
func main() {
const (
k = 3 // 在此處,iota == 0
m float32 = iota + .5 // m float32 = 1 + .5
n // n float32 = 2 + .5
p = 9 // 在此處,iota == 3
q = iota * 2 // q = 4 * 2
_ // _ = 5 * 2
r // r = 6 * 2
s, t = iota, iota // s, t = 7, 7
u, v // u, v = 8, 8
_, w // _, w = 9, 9
)
const x = iota // x = 0 (iota == 0)
const (
y = iota // y = 0 (iota == 0)
z // z = 1
)
println(m) // +1.500000e+000
println(n) // +2.500000e+000
println(q, r) // 8 12
println(s, t, u, v, w) // 7 7 8 8 9
println(x, y, z) // 0 0 1
}
上面的例子只是展示了一下如何使用iota
。 在實際編程中,我們應該用有意義的方式使用之。比如:
const (
Failed = iota - 1 // == -1
Unknown // == 0
Succeeded // == 1
)
const (
Readable = 1 << iota // == 1
Writable // == 2
Executable // == 4
)
在上面這段代碼中,-
是一個減法運算符。
變量可以被看作是在運行時刻存儲在內存中并且可以被更改的具名的值。
所有的變量值都是類型確定值。當聲明一個變量的時候,我們必須在代碼中給編譯器提供足夠的信息來讓編譯器推斷出此變量的確切類型。
在一個函數(shù)體內聲明的變量稱為局部變量。 在任何函數(shù)體外聲明的變量稱為包級或者全局變量。
Go語言有兩種變量聲明形式。一種稱為標準形式,另一種稱為短聲明形式。 短聲明形式只能用來聲明局部變量。
每條標準變量聲明形式語句起始于一個var
關鍵字。 每個var
關鍵字跟隨著一個變量名。 每個變量名必須為一個標識符。
下面是幾條完整形式的標準變量聲明語句。 這些聲明確地指定了被聲明的變量的類型和初始值。
var lang, website string = "Go", "https://golang.org"
var compiled, dynamic bool = true, false
var announceYear int = 2009
我們可以看到,和常量聲明一樣,多個同類型的變量可以在一條語句中被聲明。
完整形式的標準變量聲明使用起來有些羅嗦,因此很少在日常Go編程中使用。 在日常Go編程中,另外兩種變種形式用得更廣泛一些。 一種變種形式省略了變量類型(但仍指定了變量的初始值),這時編譯器將根據(jù)初始值的字面量形式來推斷出變量的類型。 另一種變種形式省略了初始值(但仍指定了變量類型),這時編譯器將使用變量類型的零值做為變量的初始值。
下面是一些第一種變種形式的用例。在這些用例中,如果一個初始值是一個類型確定值,則對應聲明的變量的類型將被推斷為此初始值的類型; 如果一個初始值是一個類型不確定值,則對應聲明的變量的類型將被推斷為此初始值的默認類型。 注意在這種變種中,同時聲明的多個變量的類型可以不一樣。
// 變量lang和dynamic的類型將被推斷為內置類型string和bool。
var lang, dynamic = "Go", false
// 變量compiled和announceYear的類型將被推斷
// 為內置類型bool和int。
var compiled, announceYear = true, 2009
// 變量website的類型將被推斷為內置類型string。
var website = "https://golang.org"
上例中的類型推斷可以被視為隱式類型轉換。
下例展示了幾個省略了初始值的標準變量聲明。每個聲明的變量的初始值為它們各自的類型的零值。
var lang, website string // 兩者都被初始化為空字符串。
var interpreted, dynamic bool // 兩者都被初始化為false。
var n int // 被初始化為0。
和常量聲明一樣,多個變量可以用一對小括號組團在一起被聲明。
var (
lang, bornYear, compiled = "Go", 2007, true
announceAt, releaseAt int = 2009, 2012
createdBy, website string
)
上面這個變量聲明語句已經(jīng)被go fmt
命令格式化過了。 這個變量聲明語句包含三個變量描述(variable specification)。
一般來說,將多個相關的變量聲明在一起將增強代碼的可讀性。
在上面展示的變量聲明的例子中,等號=
表示賦值。 一旦一個變量被聲明之后,它的值可以被通過純賦值語句來修改。 多個變量可以同時在一條賦值語句中被修改。
一個賦值語句等號左邊的表達式必須是一個可尋址的值、一個映射元素或者一個空標識符。 內存地址(以及指針)和映射將在以后的文章中介紹。
常量是不可改變的(不可尋址的),所以常量不能做為目標值出現(xiàn)在純賦值語句的左邊,而只能出現(xiàn)在右邊用做源值。 變量既可以出現(xiàn)在純賦值語句的左邊用做目標值,也可以出現(xiàn)在右邊用做源值。
空標識符也可以出現(xiàn)在純賦值語句的左邊,表示不關心對應的目標值。 空標識符不可被用做源值。
一個包含了很多(合法或者不合法的)純賦值語句的例子:
const N = 123
var x int
var y, z float32
N = 789 // error: N是一個不可變量
y = N // ok: N被隱式轉換為類型float32
x = y // error: 類型不匹配
x = N // ok: N被隱式轉換為類型int
y = x // error: 類型不匹配
z = y // ok
_ = y // ok
z, y = y, z // ok
_, y = y, z // ok
z, _ = y, z // ok
_, _ = y, z // ok
x, y = 69, 1.23 // ok
x, y = y, x // error: 類型不匹配
x, y = int(y), float32(x) // ok
上例中的最后一行使用了顯式類型轉換,否則此賦值(見倒數(shù)第二行)將不合法。 數(shù)字非常量值的類型轉換規(guī)則將在后邊的章節(jié)介紹。
Go不支持某些其它語言中的連等語法。下面的賦值語句在Go中是不合法的。
var a, b int
a = b = 123 // 語法錯誤
我們也可以用短變量聲明形式來聲明一些局部變量。比如下例:
package main
func main() {
// 變量lang和year都為新聲明的變量。
lang, year := "Go language", 2007
// 這里,只有變量createdBy是新聲明的變量。
// 變量year已經(jīng)在上面聲明過了,所以這里僅僅
// 改變了它的值,或者說它被重新聲明了。
year, createdBy := 2009, "Google Research"
// 這是一個純賦值語句。
lang, year = "Go", 2012
print(lang, "由", createdBy, "發(fā)明")
print("并發(fā)布于", year, "年。")
println()
}
每個短聲明語句中必須至少有一個新聲明的變量。
從上面的例子中,我們可以看到短變量聲明形式和標準變量聲明形式有幾個顯著的區(qū)別:
var
?關鍵字,并且不能指定變量的類型。:=
?。注意,相對于純賦值語句,目前短聲明語句有一個限制:出現(xiàn)在一個短聲明左側的項必須都為純標識符。 以后我們將學習到在純賦值語句的左邊可以出現(xiàn)結構體值的字段,指針的解引用和容器類型值的元素索引項等。 但是這些項不能出現(xiàn)在一個變量短聲明語句的左邊。
以后,當“賦值”這個術語被提到的時候,它可以指一個純賦值、一個短變量聲明或者一個初始值未省略的標準變量聲明。 事實上,一個更通用的定義包括后續(xù)文章將要介紹的函數(shù)傳參。
當y = x
是一條合法的賦值語句時,我們可以說x
可以被賦給y
。 假設y
的類型為Ty
,有時為了敘述方便,我們也可以說x
可以被賦給類型Ty
。
一般來說,如果x
可以被賦給y
,則y
應該是可修改的,并且x
和y
的類型相同或者x
可以被隱式轉換到y
的類型。 當然,y
也可以是空標識符_
。
注意,當使用目前的主流Go編譯器編譯Go代碼時,一個局部變量被聲明之后至少要被有效使用一次,否則編譯器將報錯。 包級變量無此限制。 如果一個變量總是被當作賦值語句中的目標值,那么我們認為這個變量沒有被有效使用過。
下面這個例子編譯不通過。
package main
var x, y, z = 123, true, "foo" // 包級變量
func main() {
var q, r = 789, false
r, s := true, "bar"
r = y // r沒有被有效使用。
x = q // q被有效使用了。
}
當編譯上面這個程序的時候,編譯器將報錯(這個程序代碼存在一個名為example-unused.go
的文件中):
./example-unused.go:6:6: r declared and not used
./example-unused.go:7:16: s declared and not used
避免編譯器報錯的方法很簡單,要么刪除相關的變量聲明,要么像下面這樣,將未曾有效使用過的變量(這里是r
和s
)賦給空標識符。
package main
var x, y, z = 123, true, "foo"
func main() {
var q, r = 789, false
r, s := true, "bar"
r = y
x = q
_, _ = r, s // 將r和s做為源值使用一次。
}
下面這個例子中的聲明的變量的初始化順序為y = 5
、c = y
、b = c+1
、a = b+1
、x = a+1
。
var x, y = a+1, 5 // 8 5
var a, b, c = b+1, c+1, y // 7 6 5
包級變量在初始化的時候不能相互依賴。比如,下面這個變量聲明語句編譯不通過。
var x, y = y, x
在Go中,有些值是可以被尋址的。上面已經(jīng)提到所有變量都是可以尋址的,所有常量都是不可被尋址。 我們可以從后面的指針一文了解更多關于內存地址和指針的知識。
在Go中,兩個類型不一樣的基本類型值是不能相互賦值的。 我們必須使用顯式類型轉換將一個值轉換為另一個值的類型之后才能進行賦值。
前面某節(jié)已經(jīng)提到了整數(shù)(不論常量還是非常量)都可以被顯式轉換為字符串類型。 這里再介紹兩個不同類型數(shù)字值之間的轉換規(guī)則。
上面已經(jīng)提到,常量數(shù)字值的類型轉換不能溢出。此規(guī)則不適用于非常量數(shù)字值的類型轉換。 非常量數(shù)字值的類型轉換中,溢出是允許的。 另外當將一個浮點數(shù)非常量值(比如一個變量)轉換為一個整數(shù)類型的時候,舍入(或者精度丟失)也是允許的。 具體規(guī)則如下:
在下面的例子中,第7行和第15行的隱式轉換是不允許的,第5行和第14行的顯式轉換也是不允許的。
const a = -1.23
// 變量b的類型被推斷為內置類型float64。
var b = a
// error: 常量1.23不能被截斷舍入到一個整數(shù)。
var x = int32(a)
// error: float64類型值不能被隱式轉換到int32。
var y int32 = b
// ok: z == -1,變量z的類型被推斷為int32。
// z的小數(shù)部分將被舍棄。
var z = int32(b)
const k int16 = 255
var n = k // 變量n的類型將被推斷為int16。
var f = uint8(k + 1) // error: 常量256溢出了uint8。
var g uint8 = n + 1 // error: int16值不能隱式轉換為uint8。
var h = uint8(n + 1) // ok: h == 0,變量h的類型為uint8。
// (n+1)溢出uint8,所以只有低8位
// bits(都為0)被保留。
第3行的隱式轉換中,a
被轉換為它的默認類型(float64
);因此b
的類型被推斷為float64
。
在Go中,我們可以使用一對大括號來顯式形成一個(局部)代碼塊。一個代碼塊可以內嵌另一個代碼塊。 最外層的代碼塊稱為包級代碼塊。 一個聲明在一個內層代碼塊中的常量或者變量將遮擋另一個外層代碼塊中聲明的同名變量或者常量。 比如,下面的代碼中聲明了3個名為x
的變量。 內層的x
將遮擋外層的x
, 從而外層的x
在內層的x
聲明之后在內層中將不可見。
package main
const y = 70
var x int = 123 // 包級變量
func main() {
// 此x變量遮擋了包級變量x。
var x = true
// 一個內嵌代碼塊。
{
x, y := x, y-10 // 這里,左邊的x和y均為新聲明
// 的變量。右邊的x為外層聲明的
// bool變量。右邊的y為包級變量。
// 在此內層代碼塊中,從此開始,
// 剛聲明的x和y將遮擋外層聲明x和y。
x, z := !x, y/10 // z是一個新聲明的變量。
// x和y是上一句中聲明的變量。
println(x, y, z) // false 60 6
}
println(x) // true
println(y) // 70 (包級變量y從未修改)
/*
println(z) // error: z未定義。
// z的作用域僅限于上面的最內層代碼塊。
*/
}
剛提到的作用域是指一個標識符的可見范圍。 一個包級變量或者常量的作用域為其所處于的整個代碼包。 一個局部變量或者常量的作用域開始于此變量或者常量的聲明的下一行,結束于最內層包含此變量或者常量的聲明語句的代碼塊的結尾。 這解釋了為什么上例中的println(z)
將編譯不通過。
后面的代碼塊和作用域一文將詳述代碼塊和標識符的作用域。
比如,下例中的三個類型不確定常量均溢出了它們各自的默認類型,但是此程序編譯和運行都沒問題。
package main
// 三個類型不確定常量。
const n = 1 << 64 // 默認類型為int
const r = 'a' + 0x7FFFFFFF // 默認類型為rune
const x = 2e+308 // 默認類型為float64
func main() {
_ = n >> 2
_ = r - 0x7FFFFFFF
_ = x / 2
}
但是下面這個程序編譯不通過,因為三個聲明的常量為類型確定常量。
package main
// 三個類型確定常量。
const n int = 1 << 64 // error: 溢出int
const r rune = 'a' + 0x7FFFFFFF // error: 溢出rune
const x float64 = 2e+308 // error: 溢出float64
func main() {}
常量聲明可以看作是增強型的C語言中的#define
宏。 在編譯階段,所有的標識符將被它們各自綁定的字面量所替代。
如果一個運算中的所有運算數(shù)都為常量,則此運算的結果也為常量?;蛘哒f,此運算將在編譯階段就被估值。 下一篇文章將介紹Go中的常用運算符。
一個例子:
package main
const X = 3
const Y = X + X
var a = X
func main() {
b := Y
println(a, b, X, Y)
}
上面這段程序代碼將在編譯階段被重寫為下面這樣:
package main
var a = 3
func main() {
b := 6
println(a, b, 3, 6)
}
更多建議: