Go語(yǔ)言 基本類型和它們的字面量表示

2023-02-16 17:36 更新

類型(type)可以被看作是值(value)的模板,值可以被看作是類型的實(shí)例。 這篇文章將介紹內(nèi)置(或稱為預(yù)聲明的)基本類型和它們字面量的表示形式。 本篇文章不介紹組合類型。

基本內(nèi)置類型

Go支持如下內(nèi)置基本類型:

  • 一種內(nèi)置布爾類型:bool。
  • 11種內(nèi)置整數(shù)類型:int8uint8、int16、uint16、int32uint32、int64uint64、int、uintuintptr
  • 兩種內(nèi)置浮點(diǎn)數(shù)類型:float32float64。
  • 兩種內(nèi)置復(fù)數(shù)類型:complex64complex128。
  • 一種內(nèi)置字符串類型:string。

內(nèi)置類型也稱為預(yù)聲明類型。

這17種內(nèi)置基本類型(type)各自屬于一種Go中的類型種類(kind)。 盡管所有的內(nèi)置基本類型的名稱都是非導(dǎo)出標(biāo)識(shí)符, 我們可以不用引入任何代碼包而直接使用這些類型。

除了boolstring類型,其它的15種內(nèi)置基本類型都稱為數(shù)值類型(整型、浮點(diǎn)數(shù)型和復(fù)數(shù)型)。

Go中有兩種內(nèi)置類型別名(type alias):

  • byteuint8的內(nèi)置別名。 我們可以將byteuint8看作是同一個(gè)類型。
  • runeint32的內(nèi)置別名。 我們可以將runeint32看作是同一個(gè)類型。

u開(kāi)頭的整數(shù)類型稱為無(wú)符號(hào)整數(shù)類型。 無(wú)符號(hào)整數(shù)類型的值都是非負(fù)的。 一個(gè)數(shù)值類型名稱中的數(shù)字表示每個(gè)這個(gè)類型的值將在內(nèi)存中占有多少二進(jìn)制位(以后簡(jiǎn)稱位)。二進(jìn)制位常稱為比特(bit)。 比如,一個(gè)uint8的值將占有8位。 我們稱uint8類型的值的尺寸是8位。 因此,最大的uint8值是255(28-1), 最大的int8值是127(27-1), 最小的int8值是-128(-27)。

任一個(gè)類型的所有值的尺寸都是相同的,所以一個(gè)值的尺寸也常稱為它的類型的尺寸。

更多的時(shí)候,我們使用字節(jié)(byte)做為值尺寸的度量單位。 一個(gè)字節(jié)相當(dāng)于8個(gè)比特。所以uint32類型的尺寸為4,即每個(gè)uint32值占用4個(gè)字節(jié)。

uintptr、int以及uint類型的值的尺寸依賴于具體編譯器實(shí)現(xiàn)。 通常地,在64位的架構(gòu)上,intuint類型的值是64位的;在32位的架構(gòu)上,它們是32位的。 編譯器必須保證uintptr類型的值的尺寸能夠存下任意一個(gè)內(nèi)存地址。

一個(gè)complex64復(fù)數(shù)值的實(shí)部和虛部都是float32類型的值。 一個(gè)complex128復(fù)數(shù)值的實(shí)部和虛部都是float64類型的值。

在內(nèi)存中,所有的浮點(diǎn)數(shù)都使用IEEE-754格式存儲(chǔ)。

一個(gè)布爾值表示一個(gè)真假。在內(nèi)存中,一個(gè)布爾值只有兩種可能的狀態(tài)。 這兩種狀態(tài)使用兩個(gè)預(yù)聲明(或稱為內(nèi)置)的常量(falsetrue)來(lái)表示。 關(guān)于常量聲明,下一篇文章將做詳細(xì)解釋。

從邏輯上說(shuō),一個(gè)字符串值表示一段文本。 在內(nèi)存中,一個(gè)字符串存儲(chǔ)為一個(gè)字節(jié)(byte)序列。 此字節(jié)序列體現(xiàn)了此字符串所表示的文本的UTF-8編碼形式。 我們可以從Go中的字符串一文中獲取更多關(guān)于字符串的知識(shí)。

盡管布爾和字符串類型分類各自只有一種內(nèi)置類型, 我們可以聲明定義更多自定義布爾和字符串類型。 所以,Go代碼中可以出現(xiàn)很多布爾和字符串類型(數(shù)值類型也同樣)。 下面是一個(gè)類型聲明的例子。 在這些例子中,type是一個(gè)關(guān)鍵字。

// 一些類型定義聲明
type status bool     // status和bool是兩個(gè)不同的類型
type MyString string // MyString和string是兩個(gè)不同的類型
type Id uint64       // Id和uint64是兩個(gè)不同的類型
type real float32    // real和float32是兩個(gè)不同的類型

// 一些類型別名聲明
type boolean = bool // boolean和bool表示同一個(gè)類型
type Text = string  // Text和string表示同一個(gè)類型
type U8 = uint8     // U8、uint8和 byte表示同一個(gè)類型
type char = rune    // char、rune和int32表示同一個(gè)類型

我們將上面定義的real類型和內(nèi)置類型float32都稱為float32類型 (注意這里的第二個(gè)float32是一個(gè)泛指,而第一個(gè)高亮的float32是一個(gè)特指)。 同樣地,MyStringstring都被稱為字符串(string)類型,statusbool都被稱為布爾(bool)類型。

我們將在Go類型系統(tǒng)概述一文中學(xué)習(xí)到更多關(guān)于自定義類型的知識(shí)。

零值

每種類型都有一個(gè)零值。一個(gè)類型的零值可以看作是此類型的默認(rèn)值。

  • 一個(gè)布爾類型的零值表示真假中的假。
  • 數(shù)值類型的零值都是零(但是不同類型的零在內(nèi)存中占用的空間可能不同)。
  • 一個(gè)字符串類型的零值是一個(gè)空字符串。

基本類型的字面量表示形式

一個(gè)值的字面形式稱為一個(gè)字面量,它表示此值在代碼中文字體現(xiàn)形式(和內(nèi)存中的表現(xiàn)形式相對(duì)應(yīng))。一個(gè)值可能會(huì)有很多種字面量形式。

布爾值的字面量形式

Go白皮書沒(méi)有定義布爾類型值字面量形式。 我們可以將falsetrue這兩個(gè)預(yù)聲明的具名常量當(dāng)作布爾類型的字面量形式。 但是,我們應(yīng)該知道,從嚴(yán)格意義上說(shuō),它們不屬于字面量。具名常量聲明將在下一篇文章中介紹和詳細(xì)解釋。

布爾類型的零值可以使用預(yù)聲明的false來(lái)表示。

整數(shù)類型值的字面量形式

整數(shù)類型值有四種字面量形式:十進(jìn)制形式(decimal)、八進(jìn)制形式(octal)、十六進(jìn)制形式(hex)和二進(jìn)制形式(binary)。 比如,下面的三個(gè)字面量均表示十進(jìn)制的15:

0xF // 十六進(jìn)制表示(必須使用0x或者0X開(kāi)頭)
0XF

017 // 八進(jìn)制表示(必須使用0、0o或者0O開(kāi)頭)
0o17
0O17

0b1111 // 二進(jìn)制表示(必須使用0b或者0B開(kāi)頭)
0B1111

15  // 十進(jìn)制表示(必須不能用0開(kāi)頭)

(注意:二進(jìn)制形式和以0o0O開(kāi)頭的八進(jìn)制形式從Go 1.13開(kāi)始才支持。)

下面的程序打印出兩個(gè)true。

package main

func main() {
	println(15 == 017) // true
	println(15 == 0xF) // true
}

注意這里的==是一個(gè)等于比較操作符。 操作符將在后續(xù)的文章常用操作符一文中詳細(xì)解釋。

整數(shù)類型的零值的字面量一般使用0表示。 當(dāng)然,000x0等也是合法的整數(shù)類型零值的字面量形式。

浮點(diǎn)數(shù)類型值的字面量形式

一個(gè)浮點(diǎn)數(shù)的完整十進(jìn)制字面量形式可能包含一個(gè)十進(jìn)制整數(shù)部分、一個(gè)小數(shù)點(diǎn)、一個(gè)十進(jìn)制小數(shù)部分和一個(gè)以10為底數(shù)的整數(shù)指數(shù)部分。 整數(shù)指數(shù)部分由字母e或者E帶一個(gè)十進(jìn)制的整數(shù)字面量組成(xEn表示x乘以10n的意思,而xE-n表示x除以10n的意思)。 常常地,某些部分可以根據(jù)情況省略掉。一些例子:

1.23
01.23 // == 1.23
.23
1.
// 一個(gè)e或者E隨后的數(shù)值是指數(shù)值(底數(shù)為10)。
// 指數(shù)值必須為一個(gè)可以帶符號(hào)的十進(jìn)制整數(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開(kāi)始,Go也支持另一種浮點(diǎn)數(shù)字面量形式:十六進(jìn)制浮點(diǎn)數(shù)字面量。 在一個(gè)十六進(jìn)制浮點(diǎn)數(shù)字面量中,

  • 一個(gè)十六進(jìn)制浮點(diǎn)數(shù)字面量必須以一個(gè)以2為底數(shù)的整數(shù)指數(shù)部分。 這樣的一個(gè)整數(shù)指數(shù)部分由字母p或者P帶一個(gè)十進(jìn)制的整數(shù)字面量組成(yPn表示y乘以2n的意思,而yP-n表示y除以2n的意思)。
  • 和整數(shù)的十六進(jìn)制字面量一樣,一個(gè)十六進(jìn)制浮點(diǎn)數(shù)字面量也必須使用0x或者0X開(kāi)頭。 和整數(shù)的十六進(jìn)制字面量不同的是,一個(gè)十六進(jìn)制浮點(diǎn)數(shù)字面量可以包括一個(gè)小數(shù)點(diǎn)和一個(gè)十六進(jìn)制小數(shù)部分。

一些合法的浮點(diǎn)數(shù)的十六進(jìn)制字面量例子:

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

而下面這幾個(gè)均是不合法的浮點(diǎn)數(shù)的十六進(jìn)制字面量。

0x.p1    // 整數(shù)部分表示必須包含至少一個(gè)數(shù)字
1p-2     // p指數(shù)形式只能出現(xiàn)在浮點(diǎn)數(shù)的十六進(jìn)制字面量中
0x1.5e-2 // e和E不能出現(xiàn)在十六進(jìn)制浮點(diǎn)數(shù)字面量的指數(shù)部分中

注意:下面這個(gè)表示是合法的,但是它不是浮點(diǎn)數(shù)的十六進(jìn)制字面量。事實(shí)上,它是一個(gè)減法算術(shù)表達(dá)式。其中的e為是十進(jìn)制中的14,0x15e為一個(gè)整數(shù)十六進(jìn)制字面量,-2并不是此整數(shù)十六進(jìn)制字面量的一部分。 (算術(shù)運(yùn)算將在后續(xù)的文章常用操作符一文中詳細(xì)介紹。)

0x15e-2 // == 0x15e - 2 (整數(shù)相減表達(dá)式)

浮點(diǎn)類型的零值的標(biāo)準(zhǔn)字面量形式為0.0。 當(dāng)然其它很多形式也是合法的,比如0.、.0、0e00x0p0等。

虛部字面量形式

一個(gè)虛部值的字面量形式由一個(gè)浮點(diǎn)數(shù)字面量或者一個(gè)整數(shù)字面量和其后跟隨的一個(gè)小寫的字母i組成。 在Go 1.13之前,如果虛部中i前的部分為一個(gè)整數(shù)字面量,則其必須為并且總是被視為十進(jìn)制形式。 一些例子:

1.23i
1.i
.23i
123i
0123i   // == 123i(兼容性使然。見(jiàn)下)
1.23E2i // == 123i
1e-1i
011i   // == 11i(兼容性使然。見(jiàn)下)
00011i // == 11i(兼容性使然。見(jiàn)下)
// 下面這幾行從Go 1.13開(kāi)始才能編譯通過(guò)。
0o11i    // == 9i
0x11i    // == 17i
0b11i    // == 3i
0X.8p-0i // == 0.5i

注意:在Go 1.13之前,虛部字面量中字母i前的部分只能為浮點(diǎn)數(shù)字面量。 為了兼容老的Go版本,從Go 1.13開(kāi)始,一些虛部字面量中表現(xiàn)為(不以0o0O開(kāi)頭的)八進(jìn)制形式的整數(shù)字面量仍被視為浮點(diǎn)數(shù)字面量。 比如上例中的011i、0123i00011i。

虛部字面量用來(lái)表示復(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等表示也是合法的。

數(shù)值字面表示中使用下劃線分段來(lái)增強(qiáng)可讀性

從Go 1.13開(kāi)始,下劃線_可以出現(xiàn)在整數(shù)、浮點(diǎn)數(shù)和虛部數(shù)字面量中,以用做分段符以增強(qiáng)可讀性。 但是要注意,在一個(gè)數(shù)值字面表示中,一個(gè)下劃線_不能出現(xiàn)在此字面表示的首尾,并且其兩側(cè)的字符必須為(相應(yīng)進(jìn)制的)數(shù)字字符或者進(jìn)制表示頭。

一些合法和不合法使用下劃線的例子:

// 合法的使用下劃線的例子
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不是一個(gè)合法的八進(jìn)制數(shù)字
1_.5       // .不是一個(gè)合法的十進(jìn)制數(shù)字
1._5       // .不是一個(gè)合法的十進(jìn)制數(shù)字

rune值的字面量形式

上面已經(jīng)提到,rune類型是int32類型的別名。 因此,rune類型(泛指)是特殊的整數(shù)類型。 一個(gè)rune值可以用上面已經(jīng)介紹的整數(shù)類型的字面量形式表示。 另一方面,很多各種整數(shù)類型的值也可以用本小節(jié)介紹的rune字面量形式來(lái)表示。

在Go中,一個(gè)rune值表示一個(gè)Unicode碼點(diǎn)。 一般說(shuō)來(lái),我們可以將一個(gè)Unicode碼點(diǎn)看作是一個(gè)Unicode字符。 但是,我們也應(yīng)該知道,有些Unicode字符由多個(gè)Unicode碼點(diǎn)組成。 每個(gè)英文或中文Unicode字符值含有一個(gè)Unicode碼點(diǎn)。

一個(gè)rune字面量由若干包在一對(duì)單引號(hào)中的字符組成。 包在單引號(hào)中的字符序列表示一個(gè)Unicode碼點(diǎn)值。 rune字面量形式有幾個(gè)變種,其中最常用的一種變種是將一個(gè)rune值對(duì)應(yīng)的Unicode字符直接包在一對(duì)單引號(hào)中。比如:

'a' // 一個(gè)英文字符
'π'
'眾' // 一個(gè)中文字符

下面這些rune字面量形式的變種和'a'是等價(jià)的 (字符a的Unicode值是97)。

'\141'   // 141是97的八進(jìn)制表示
'\x61'   // 61是97的十六進(jìn)制表示
'\u0061'
'\U00000061'

注意:\之后必須跟隨三個(gè)八進(jìn)制數(shù)字字符(0-7)表示一個(gè)byte值, \x之后必須跟隨兩個(gè)十六進(jìn)制數(shù)字字符(0-9,a-f和A-F)表示一個(gè)byte值, \u之后必須跟隨四個(gè)十六進(jìn)制數(shù)字字符表示一個(gè)rune值(此rune值的高四位都為0), \U之后必須跟隨八個(gè)十六進(jìn)制數(shù)字字符表示一個(gè)rune值。 這些八進(jìn)制和十六進(jìn)制的數(shù)字字符序列表示的整數(shù)必須是一個(gè)合法的Unicode碼點(diǎn)值,否則編譯將失敗。

下面這些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' == '眾')
}

事實(shí)上,在日常編程中,這四種rune字面量形式的變種很少用來(lái)表示rune值。 它們多用做字符串的雙引號(hào)字面量形式中的轉(zhuǎn)義字符(詳見(jiàn)下一小節(jié))。

如果一個(gè)rune字面量中被單引號(hào)包起來(lái)的部分含有兩個(gè)字符, 并且第一個(gè)字符是\,第二個(gè)字符不是x、 uU,那么這兩個(gè)字符將被轉(zhuǎn)義為一個(gè)特殊字符。 目前支持的轉(zhuǎn)義組合為:

\a   (rune值:0x07) 鈴聲字符
\b   (rune值:0x08) 退格字符(backspace)
\f   (rune值:0x0C) 換頁(yè)符(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) 一個(gè)反斜杠(backslash)
\'   (rune值:0x27) 一個(gè)單引號(hào)(single quote)

其中,\n在日常編程中用得最多。

一個(gè)例子:

	println('\n') // 10
	println('\r') // 13
	println('\'') // 39

	println('\n' == 10)     // true
	println('\n' == '\x0A') // true

rune類型的零值常用 '\000'、'\x00''\u0000'等來(lái)表示。

字符串值的字面量形式

在Go中,字符串值是UTF-8編碼的, 甚至所有的Go源代碼都必須是UTF-8編碼的。

Go字符串的字面量形式有兩種。 一種是解釋型字面表示(interpreted string literal,雙引號(hào)風(fēng)格)。 另一種是直白字面表示(raw string literal,反引號(hào)風(fēng)格)。 下面的兩個(gè)字符串表示形式是等價(jià)的:

// 解釋形式
"Hello\nworld!\n\"你好世界\""

// 直白形式
`Hello
world!
"你好世界"`

在上面的解釋形式(雙引號(hào)風(fēng)格)的字符串字面量中,每個(gè)\n將被轉(zhuǎn)義為一個(gè)換行符,每個(gè)\"將被轉(zhuǎn)義為一個(gè)雙引號(hào)字符。 雙引號(hào)風(fēng)格的字符串字面量中支持的轉(zhuǎn)義字符和rune字面量基本一致,除了一個(gè)例外:雙引號(hào)風(fēng)格的字符串字面量中支持\"轉(zhuǎn)義,但不支持\'轉(zhuǎn)義;而rune字面量則剛好相反。

\、\x\u\U開(kāi)頭的rune字面量(不包括兩個(gè)單引號(hào))也可以出現(xiàn)在雙引號(hào)風(fēng)格的字符串字面量中。比如:

// 這幾個(gè)字符串字面量是等價(jià)的。
"\141\142\143"
"\x61\x62\x63"
"\x61b\x63"
"abc"

// 這幾個(gè)字符串字面量是等價(jià)的。
"\u4f17\xe4\xba\xba"
      // “眾”的Unicode值為4f17,它的UTF-8
      // 編碼為三個(gè)字節(jié):0xe4 0xbc 0x97。
"\xe4\xbc\x97\u4eba"
      // “人”的Unicode值為4eba,它的UTF-8
      // 編碼為三個(gè)字節(jié):0xe4 0xba 0xba。
"\xe4\xbc\x97\xe4\xba\xba"
"眾人"

在UTF-8編碼中,一個(gè)Unicode碼點(diǎn)(rune)可能由1到4個(gè)字節(jié)組成。 每個(gè)英文字母的UTF-8編碼只需要一個(gè)字節(jié);每個(gè)中文字符的UTF-8編碼需要三個(gè)字節(jié)。

直白反引號(hào)風(fēng)格的字面表示中是不支持轉(zhuǎn)義字符的。 除了首尾兩個(gè)反引號(hào),直白反引號(hào)風(fēng)格的字面表示中不能包含反引號(hào)。 為了跨平臺(tái)兼容性,直白反引號(hào)風(fēng)格的字面表示中的回車符(Unicode碼點(diǎn)為0x0D) 將被忽略掉。

字符串類型的零值在代碼里用 ""``表示。

基本數(shù)值類型字面量的適用范圍

一個(gè)數(shù)值型的字面量只有在不需要舍入時(shí),才能用來(lái)表示一個(gè)整數(shù)基本類型的值。 比如,1.0可以表示任何基本整數(shù)類型的值,但1.01卻不可以。 當(dāng)一個(gè)數(shù)值型的字面量用來(lái)表示一個(gè)非整數(shù)基本類型的值時(shí),舍入(或者精度丟失)是允許的。

每種數(shù)值類型有一個(gè)能夠表示的數(shù)值范圍。 如果一個(gè)字面量超出了一個(gè)類型能夠表示的數(shù)值范圍(溢出),則在編譯時(shí)刻,此字面量不能用來(lái)表示此類型的值。

下表是一些例子:

字面表示 此字面表示可以表示哪些類型的值(在編譯時(shí)刻)
256 除了int8和uint8類型外的所有的基本數(shù)值類型。
255 除了int8類型外的所有的基本數(shù)值類型。
-123 除了無(wú)符號(hào)整數(shù)類型外的所有的基本數(shù)值類型。
123 所有的基本數(shù)值類型。
123.000
1.23e2
'a'
1.0+0i
1.23 所有浮點(diǎn)數(shù)和復(fù)數(shù)基本數(shù)值類型。
0x10000000000000000
(16 zeros)
3.5e38 除了float32和complex64類型外的所有浮點(diǎn)數(shù)和復(fù)數(shù)基本數(shù)值類型。
1+2i 所有復(fù)數(shù)基本數(shù)值類型。
2e+308 無(wú)。

注意幾個(gè)溢出的例子:

  • 字面量0x10000000000000000需要65個(gè)比特才能表示,所以在運(yùn)行時(shí)刻,任何基本整數(shù)類型都不能精確表示此字面量。
  • 在IEEE-754標(biāo)準(zhǔn)中,最大的可以精確表示的float32類型數(shù)值為3.40282346638528859811704183484516925440e+38,所以3.5e38不能表示任何float32和complex64類型的值。
  • 在IEEE-754標(biāo)準(zhǔn)中,最大的可以精確表示的float64類型數(shù)值為1.797693134862315708145274237317043567981e+308,因此2e+308不能表示任何基本數(shù)值類型的值。
  • 盡管0x10000000000000000可以用來(lái)表示float32類型的值,但是它不能被任何float32類型的值所精確表示。上面已經(jīng)提到了,當(dāng)使用字面量來(lái)表示非整數(shù)基本數(shù)值類型的時(shí)候,精度丟失是允許的(但溢出是不允許的)。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)