類型(type)可以被看作是值(value)的模板,值可以被看作是類型的實例。 這篇文章將介紹內(nèi)置(或稱為預(yù)聲明的)基本類型和它們字面量的表示形式。 本篇文章不介紹組合類型。
Go支持如下內(nèi)置基本類型:
bool
。int8
、uint8
、int16
、uint16
、int32
、uint32
、int64
、uint64
、int
、uint
和uintptr
。float32
和float64
。complex64
和complex128
。string
。內(nèi)置類型也稱為預(yù)聲明類型。
這17種內(nèi)置基本類型(type)各自屬于一種Go中的類型種類(kind)。 盡管所有的內(nèi)置基本類型的名稱都是非導(dǎo)出標(biāo)識符, 我們可以不用引入任何代碼包而直接使用這些類型。
除了bool
和string
類型,其它的15種內(nèi)置基本類型都稱為數(shù)值類型(整型、浮點數(shù)型和復(fù)數(shù)型)。
Go中有兩種內(nèi)置類型別名(type alias):
byte
是uint8
的內(nèi)置別名。 我們可以將byte
和uint8
看作是同一個類型。rune
是int32
的內(nèi)置別名。 我們可以將rune
和int32
看作是同一個類型。以u
開頭的整數(shù)類型稱為無符號整數(shù)類型。 無符號整數(shù)類型的值都是非負(fù)的。 一個數(shù)值類型名稱中的數(shù)字表示每個這個類型的值將在內(nèi)存中占有多少二進制位(以后簡稱位)。二進制位常稱為比特(bit)。 比如,一個uint8
的值將占有8位。 我們稱uint8
類型的值的尺寸是8位。 因此,最大的uint8
值是255
(28-1), 最大的int8
值是127
(27-1),
最小的int8
值是-128
(-27)。
任一個類型的所有值的尺寸都是相同的,所以一個值的尺寸也常稱為它的類型的尺寸。
更多的時候,我們使用字節(jié)(byte)做為值尺寸的度量單位。 一個字節(jié)相當(dāng)于8個比特。所以uint32
類型的尺寸為4,即每個uint32
值占用4個字節(jié)。
uintptr
、int
以及uint
類型的值的尺寸依賴于具體編譯器實現(xiàn)。 通常地,在64位的架構(gòu)上,int
和uint
類型的值是64位的;在32位的架構(gòu)上,它們是32位的。 編譯器必須保證uintptr
類型的值的尺寸能夠存下任意一個內(nèi)存地址。
一個complex64
復(fù)數(shù)值的實部和虛部都是float32
類型的值。 一個complex128
復(fù)數(shù)值的實部和虛部都是float64
類型的值。
在內(nèi)存中,所有的浮點數(shù)都使用IEEE-754格式存儲。
一個布爾值表示一個真假。在內(nèi)存中,一個布爾值只有兩種可能的狀態(tài)。 這兩種狀態(tài)使用兩個預(yù)聲明(或稱為內(nèi)置)的常量(false
和true
)來表示。 關(guān)于常量聲明,下一篇文章將做詳細(xì)解釋。
從邏輯上說,一個字符串值表示一段文本。 在內(nèi)存中,一個字符串存儲為一個字節(jié)(byte)序列。 此字節(jié)序列體現(xiàn)了此字符串所表示的文本的UTF-8編碼形式。 我們可以從Go中的字符串一文中獲取更多關(guān)于字符串的知識。
盡管布爾和字符串類型分類各自只有一種內(nèi)置類型, 我們可以聲明定義更多自定義布爾和字符串類型。 所以,Go代碼中可以出現(xiàn)很多布爾和字符串類型(數(shù)值類型也同樣)。 下面是一個類型聲明的例子。 在這些例子中,type
是一個關(guān)鍵字。
// 一些類型定義聲明
type status bool // status和bool是兩個不同的類型
type MyString string // MyString和string是兩個不同的類型
type Id uint64 // Id和uint64是兩個不同的類型
type real float32 // real和float32是兩個不同的類型
// 一些類型別名聲明
type boolean = bool // boolean和bool表示同一個類型
type Text = string // Text和string表示同一個類型
type U8 = uint8 // U8、uint8和 byte表示同一個類型
type char = rune // char、rune和int32表示同一個類型
我們將上面定義的real
類型和內(nèi)置類型float32
都稱為float32類型 (注意這里的第二個float32是一個泛指,而第一個高亮的float32是一個特指)。 同樣地,MyString
和string
都被稱為字符串(string)類型,status
和bool
都被稱為布爾(bool)類型。
我們將在Go類型系統(tǒng)概述一文中學(xué)習(xí)到更多關(guān)于自定義類型的知識。
每種類型都有一個零值。一個類型的零值可以看作是此類型的默認(rèn)值。
一個值的字面形式稱為一個字面量,它表示此值在代碼中文字體現(xiàn)形式(和內(nèi)存中的表現(xiàn)形式相對應(yīng))。一個值可能會有很多種字面量形式。
Go白皮書沒有定義布爾類型值字面量形式。 我們可以將false
和true
這兩個預(yù)聲明的具名常量當(dāng)作布爾類型的字面量形式。 但是,我們應(yīng)該知道,從嚴(yán)格意義上說,它們不屬于字面量。具名常量聲明將在下一篇文章中介紹和詳細(xì)解釋。
布爾類型的零值可以使用預(yù)聲明的false
來表示。
整數(shù)類型值有四種字面量形式:十進制形式(decimal)、八進制形式(octal)、十六進制形式(hex)和二進制形式(binary)。 比如,下面的三個字面量均表示十進制的15:
0xF // 十六進制表示(必須使用0x或者0X開頭)
0XF
017 // 八進制表示(必須使用0、0o或者0O開頭)
0o17
0O17
0b1111 // 二進制表示(必須使用0b或者0B開頭)
0B1111
15 // 十進制表示(必須不能用0開頭)
(注意:二進制形式和以0o
或0O
開頭的八進制形式從Go 1.13開始才支持。)
下面的程序打印出兩個true
。
package main
func main() {
println(15 == 017) // true
println(15 == 0xF) // true
}
注意這里的==
是一個等于比較操作符。 操作符將在后續(xù)的文章常用操作符一文中詳細(xì)解釋。
整數(shù)類型的零值的字面量一般使用0
表示。 當(dāng)然,00
和0x0
等也是合法的整數(shù)類型零值的字面量形式。
一個浮點數(shù)的完整十進制字面量形式可能包含一個十進制整數(shù)部分、一個小數(shù)點、一個十進制小數(shù)部分和一個以10為底數(shù)的整數(shù)指數(shù)部分。 整數(shù)指數(shù)部分由字母e
或者E
帶一個十進制的整數(shù)字面量組成(xEn
表示x
乘以10n
的意思,而xE-n
表示x
除以10n
的意思)。
常常地,某些部分可以根據(jù)情況省略掉。一些例子:
1.23
01.23 // == 1.23
.23
1.
// 一個e或者E隨后的數(shù)值是指數(shù)值(底數(shù)為10)。
// 指數(shù)值必須為一個可以帶符號的十進制整數(shù)字面量。
1.23e2 // == 123.0
123E2 // == 12300.0
123.E+2 // == 12300.0
1e-1 // == 0.1
.1e0 // == 0.1
0010e-2 // == 0.1
0e+5 // == 0.0
從Go 1.13開始,Go也支持另一種浮點數(shù)字面量形式:十六進制浮點數(shù)字面量。 在一個十六進制浮點數(shù)字面量中,
p
或者P
帶一個十進制的整數(shù)字面量組成(yPn
表示y
乘以2n
的意思,而yP-n
表示y
除以2n
的意思)。0x
或者0X
開頭。 和整數(shù)的十六進制字面量不同的是,一個十六進制浮點數(shù)字面量可以包括一個小數(shù)點和一個十六進制小數(shù)部分。一些合法的浮點數(shù)的十六進制字面量例子:
0x1p-2 // == 1.0/4 = 0.25
0x2.p10 // == 2.0 * 1024 == 2048.0
0x1.Fp+0 // == 1+15.0/16 == 1.9375
0X.8p1 // == 8.0/16 * 2 == 1.0
0X1FFFP-16 // == 0.1249847412109375
而下面這幾個均是不合法的浮點數(shù)的十六進制字面量。
0x.p1 // 整數(shù)部分表示必須包含至少一個數(shù)字
1p-2 // p指數(shù)形式只能出現(xiàn)在浮點數(shù)的十六進制字面量中
0x1.5e-2 // e和E不能出現(xiàn)在十六進制浮點數(shù)字面量的指數(shù)部分中
注意:下面這個表示是合法的,但是它不是浮點數(shù)的十六進制字面量。事實上,它是一個減法算術(shù)表達式。其中的e
為是十進制中的14
,0x15e
為一個整數(shù)十六進制字面量,-2
并不是此整數(shù)十六進制字面量的一部分。 (算術(shù)運算將在后續(xù)的文章常用操作符一文中詳細(xì)介紹。)
0x15e-2 // == 0x15e - 2 (整數(shù)相減表達式)
浮點類型的零值的標(biāo)準(zhǔn)字面量形式為0.0
。 當(dāng)然其它很多形式也是合法的,比如0.
、.0
、0e0
和0x0p0
等。
一個虛部值的字面量形式由一個浮點數(shù)字面量或者一個整數(shù)字面量和其后跟隨的一個小寫的字母i
組成。 在Go 1.13之前,如果虛部中i
前的部分為一個整數(shù)字面量,則其必須為并且總是被視為十進制形式。 一些例子:
1.23i
1.i
.23i
123i
0123i // == 123i(兼容性使然。見下)
1.23E2i // == 123i
1e-1i
011i // == 11i(兼容性使然。見下)
00011i // == 11i(兼容性使然。見下)
// 下面這幾行從Go 1.13開始才能編譯通過。
0o11i // == 9i
0x11i // == 17i
0b11i // == 3i
0X.8p-0i // == 0.5i
注意:在Go 1.13之前,虛部字面量中字母i
前的部分只能為浮點數(shù)字面量。 為了兼容老的Go版本,從Go 1.13開始,一些虛部字面量中表現(xiàn)為(不以0o
和0O
開頭的)八進制形式的整數(shù)字面量仍被視為浮點數(shù)字面量。 比如上例中的011i
、0123i
和00011i
。
虛部字面量用來表示復(fù)數(shù)的虛部。下面是一些復(fù)數(shù)值的字面量形式:
1 + 2i // == 1.0 + 2.0i
1. - .1i // == 1.0 + -0.1i
1.23i - 7.89 // == -7.89 + 1.23i
1.23i // == 0.0 + 1.23i
復(fù)數(shù)零值的標(biāo)準(zhǔn)字面表示為0.0+0.0i
。 當(dāng)然0i
、.0i
、0+0i
等表示也是合法的。
從Go 1.13開始,下劃線_
可以出現(xiàn)在整數(shù)、浮點數(shù)和虛部數(shù)字面量中,以用做分段符以增強可讀性。 但是要注意,在一個數(shù)值字面表示中,一個下劃線_
不能出現(xiàn)在此字面表示的首尾,并且其兩側(cè)的字符必須為(相應(yīng)進制的)數(shù)字字符或者進制表示頭。
一些合法和不合法使用下劃線的例子:
// 合法的使用下劃線的例子
6_9 // == 69
0_33_77_22 // == 0337722
0x_Bad_Face // == 0xBadFace
0X_1F_FFP-16 // == 0X1FFFP-16
0b1011_0111 + 0xA_B.Fp2i
// 非法的使用下劃線的例子
_69 // 下劃線不能出現(xiàn)在首尾
69_ // 下劃線不能出現(xiàn)在首尾
6__9 // 下劃線不能相連
0_xBadFace // x不是一個合法的八進制數(shù)字
1_.5 // .不是一個合法的十進制數(shù)字
1._5 // .不是一個合法的十進制數(shù)字
上面已經(jīng)提到,rune
類型是int32
類型的別名。 因此,rune類型(泛指)是特殊的整數(shù)類型。 一個rune值可以用上面已經(jīng)介紹的整數(shù)類型的字面量形式表示。 另一方面,很多各種整數(shù)類型的值也可以用本小節(jié)介紹的rune字面量形式來表示。
在Go中,一個rune值表示一個Unicode碼點。 一般說來,我們可以將一個Unicode碼點看作是一個Unicode字符。 但是,我們也應(yīng)該知道,有些Unicode字符由多個Unicode碼點組成。 每個英文或中文Unicode字符值含有一個Unicode碼點。
一個rune字面量由若干包在一對單引號中的字符組成。 包在單引號中的字符序列表示一個Unicode碼點值。 rune字面量形式有幾個變種,其中最常用的一種變種是將一個rune值對應(yīng)的Unicode字符直接包在一對單引號中。比如:
'a' // 一個英文字符
'π'
'眾' // 一個中文字符
下面這些rune字面量形式的變種和'a'
是等價的 (字符a
的Unicode值是97)。
'\141' // 141是97的八進制表示
'\x61' // 61是97的十六進制表示
'\u0061'
'\U00000061'
注意:\
之后必須跟隨三個八進制數(shù)字字符(0-7)表示一個byte值, \x
之后必須跟隨兩個十六進制數(shù)字字符(0-9,a-f和A-F)表示一個byte值, \u
之后必須跟隨四個十六進制數(shù)字字符表示一個rune值(此rune值的高四位都為0), \U
之后必須跟隨八個十六進制數(shù)字字符表示一個rune值。 這些八進制和十六進制的數(shù)字字符序列表示的整數(shù)必須是一個合法的Unicode碼點值,否則編譯將失敗。
下面這些println
函數(shù)調(diào)用都將打印出true
。
package main
func main() {
println('a' == 97)
println('a' == '\141')
println('a' == '\x61')
println('a' == '\u0061')
println('a' == '\U00000061')
println(0x61 == '\x61')
println('\u4f17' == '眾')
}
事實上,在日常編程中,這四種rune字面量形式的變種很少用來表示rune值。 它們多用做字符串的雙引號字面量形式中的轉(zhuǎn)義字符(詳見下一小節(jié))。
如果一個rune字面量中被單引號包起來的部分含有兩個字符, 并且第一個字符是\
,第二個字符不是x
、 u
和U
,那么這兩個字符將被轉(zhuǎn)義為一個特殊字符。 目前支持的轉(zhuǎn)義組合為:
\a (rune值:0x07) 鈴聲字符
\b (rune值:0x08) 退格字符(backspace)
\f (rune值:0x0C) 換頁符(form feed)
\n (rune值:0x0A) 換行符(line feed or newline)
\r (rune值:0x0D) 回車符(carriage return)
\t (rune值:0x09) 水平制表符(horizontal tab)
\v (rune值:0x0b) 豎直制表符(vertical tab)
\\ (rune值:0x5c) 一個反斜杠(backslash)
\' (rune值:0x27) 一個單引號(single quote)
其中,\n
在日常編程中用得最多。
一個例子:
println('\n') // 10
println('\r') // 13
println('\'') // 39
println('\n' == 10) // true
println('\n' == '\x0A') // true
rune類型的零值常用 '\000'
、'\x00'
或'\u0000'
等來表示。
在Go中,字符串值是UTF-8編碼的, 甚至所有的Go源代碼都必須是UTF-8編碼的。
Go字符串的字面量形式有兩種。 一種是解釋型字面表示(interpreted string literal,雙引號風(fēng)格)。 另一種是直白字面表示(raw string literal,反引號風(fēng)格)。 下面的兩個字符串表示形式是等價的:
// 解釋形式
"Hello\nworld!\n\"你好世界\""
// 直白形式
`Hello
world!
"你好世界"`
在上面的解釋形式(雙引號風(fēng)格)的字符串字面量中,每個\n
將被轉(zhuǎn)義為一個換行符,每個\"
將被轉(zhuǎn)義為一個雙引號字符。 雙引號風(fēng)格的字符串字面量中支持的轉(zhuǎn)義字符和rune字面量基本一致,除了一個例外:雙引號風(fēng)格的字符串字面量中支持\"
轉(zhuǎn)義,但不支持\'
轉(zhuǎn)義;而rune字面量則剛好相反。
以\
、\x
、\u
和\U
開頭的rune字面量(不包括兩個單引號)也可以出現(xiàn)在雙引號風(fēng)格的字符串字面量中。比如:
// 這幾個字符串字面量是等價的。
"\141\142\143"
"\x61\x62\x63"
"\x61b\x63"
"abc"
// 這幾個字符串字面量是等價的。
"\u4f17\xe4\xba\xba"
// “眾”的Unicode值為4f17,它的UTF-8
// 編碼為三個字節(jié):0xe4 0xbc 0x97。
"\xe4\xbc\x97\u4eba"
// “人”的Unicode值為4eba,它的UTF-8
// 編碼為三個字節(jié):0xe4 0xba 0xba。
"\xe4\xbc\x97\xe4\xba\xba"
"眾人"
在UTF-8編碼中,一個Unicode碼點(rune)可能由1到4個字節(jié)組成。 每個英文字母的UTF-8編碼只需要一個字節(jié);每個中文字符的UTF-8編碼需要三個字節(jié)。
直白反引號風(fēng)格的字面表示中是不支持轉(zhuǎn)義字符的。 除了首尾兩個反引號,直白反引號風(fēng)格的字面表示中不能包含反引號。 為了跨平臺兼容性,直白反引號風(fēng)格的字面表示中的回車符(Unicode碼點為0x0D
) 將被忽略掉。
字符串類型的零值在代碼里用 ""
或``
表示。
一個數(shù)值型的字面量只有在不需要舍入時,才能用來表示一個整數(shù)基本類型的值。 比如,1.0
可以表示任何基本整數(shù)類型的值,但1.01
卻不可以。 當(dāng)一個數(shù)值型的字面量用來表示一個非整數(shù)基本類型的值時,舍入(或者精度丟失)是允許的。
每種數(shù)值類型有一個能夠表示的數(shù)值范圍。 如果一個字面量超出了一個類型能夠表示的數(shù)值范圍(溢出),則在編譯時刻,此字面量不能用來表示此類型的值。
下表是一些例子:
字面表示 | 此字面表示可以表示哪些類型的值(在編譯時刻) |
---|---|
256
|
除了int8和uint8類型外的所有的基本數(shù)值類型。 |
255
|
除了int8類型外的所有的基本數(shù)值類型。 |
-123
|
除了無符號整數(shù)類型外的所有的基本數(shù)值類型。 |
123
|
所有的基本數(shù)值類型。 |
123.000
|
|
1.23e2
|
|
'a'
|
|
1.0+0i
|
|
1.23
|
所有浮點數(shù)和復(fù)數(shù)基本數(shù)值類型。 |
0x10000000000000000
(16 zeros)
|
|
3.5e38
|
除了float32和complex64類型外的所有浮點數(shù)和復(fù)數(shù)基本數(shù)值類型。 |
1+2i
|
所有復(fù)數(shù)基本數(shù)值類型。 |
2e+308
|
無。 |
注意幾個溢出的例子:
0x10000000000000000
需要65個比特才能表示,所以在運行時刻,任何基本整數(shù)類型都不能精確表示此字面量。
3.40282346638528859811704183484516925440e+38
,所以3.5e38
不能表示任何float32和complex64類型的值。
1.797693134862315708145274237317043567981e+308
,因此2e+308
不能表示任何基本數(shù)值類型的值。
0x10000000000000000
可以用來表示float32類型的值,但是它不能被任何float32類型的值所精確表示。上面已經(jīng)提到了,當(dāng)使用字面量來表示非整數(shù)基本數(shù)值類型的時候,精度丟失是允許的(但溢出是不允許的)。
更多建議: