Go:什么是字面量和組合字面量?
# 0. 前言
說出來不怕大家笑話,在去年年初我剛開始學(xué)習(xí) Go 基礎(chǔ)的時(shí)候,有一個(gè)詞困擾了我好久,這個(gè)詞就是 字面量。
之所以會(huì)讓我理解困難,是因?yàn)樵?Go 之前,我都是寫 Python 的,而且寫了很多年,在 Python 中萬物皆對象,不管一個(gè)字面量)有沒有變量名來承接,在使用上沒有任何區(qū)別的,因此在學(xué) Go 之前,我其實(shí)都不知道有字面量這么個(gè)概念。
- >>> {"name": "iswbm"}.get("name") # 使用字面量
 - 'iswbm'
 - >>>
 - >>> profile={"name": "iswbm"} # 使用變量名
 - >>> profile.get("name")
 - 'iswbm'
 
那么字面量到底是啥東西?怎么那么多的基礎(chǔ)教程里反復(fù)會(huì)提及,卻好像沒什么人把這個(gè)名詞的概念解釋一下呢?難道是因?yàn)檫@是常識(shí)?尷尬。。
相信正在看這篇文章的你,可能也會(huì)有此疑問,今天我就梳理一下,我理解中的 字面量,是什么意思?它與普通變量有什么區(qū)別?
# 1. 什么是字面量?
在 Go 中內(nèi)置的基本類型有:
- 布爾類型:bool
 - 11個(gè)內(nèi)置的整數(shù)數(shù)字類型:int8, uint8, int16, uint16, int32, uint32, int64, uint64, int, uint和uintptr
 - 浮點(diǎn)數(shù)類型:float32和float64
 - 復(fù)數(shù)類型:complex64和complex128
 - 字符串類型:string
 
而這些基本類型值的文本,就是基本類型字面量。
比如下面這兩個(gè)字符串,都是字符串字面量,沒有用變量名或者常量名來指向這兩個(gè)字面量,因此也稱之為 未命名常量。
- "hello, iswbm"
 - `hello,
 - iswbm`
 
# 2. 同值不同字面量
值的字面量(literal)是代碼中值的文字表示,一個(gè)值可能存在多種字面量表示。
舉個(gè)例子,十進(jìn)制的數(shù)值 15,可以由三種字面量表示
- // 16進(jìn)制
 - 0xF
 - // 8進(jìn)制
 - 0o17
 - // 2進(jìn)制
 - 0b1111
 
通過比較,可以看出他們是相等的
- import "fmt"
 - func main() {
 - fmt.Println(15 == 0xF) // true
 - fmt.Println(15 == 017) // true
 - fmt.Println(15 == 0b1111) // true
 - }
 
# 3. 字面量和變量有啥區(qū)別?
下面這是一段很正常的代碼
- func foo() string {
 - return "hello"
 - }
 - func main() {
 - bar := foo()
 - fmt.Println(&bar)
 - }
 
可要是換成下面這樣
- func foo() string {
 - return "hello"
 - }
 - func main() {
 - fmt.Println(&foo())
 - }
 
可實(shí)際上這段代碼是有問題的,運(yùn)行后會(huì)報(bào)錯(cuò)
- ./demo.go:11:14: cannot take the address of foo()
 
你一定覺得很奇怪吧?
為什么先用變量名承接一下再取地址就不會(huì)報(bào)錯(cuò),而直接使用在函數(shù)返回后的值上取地址就不行呢?
這是因?yàn)?,如果不使用一個(gè)變量名承接一下,函數(shù)返回的是一個(gè)字符串的文本值,也就是字符串字面量,而這種基本類型的字面量是不可尋址的。
要想使用 & 進(jìn)行尋址,就必須得用變量名承接一下。
# 4. 什么是組合字面量?
首先看下Go文檔中對組合字面量(Composite Literal)的定義:
Composite literals construct values for structs, arrays, slices, and maps and create a new value each time they are evaluated. They consist of the type of the literal followed by a brace-bound list of elements. Each element may optionally be preceded by a corresponding key。
翻譯成中文大致如下:組合字面量是為結(jié)構(gòu)體、數(shù)組、切片和map構(gòu)造值,并且每次都會(huì)創(chuàng)建新值。它們由字面量的類型后緊跟大括號(hào)及元素列表。每個(gè)元素前面可以選擇性的帶一個(gè)相關(guān)key。
什么意思呢?所謂的組合字面量其實(shí)就是把對象的定義和初始化放在一起了。
接下來讓我們看看結(jié)構(gòu)體、數(shù)組、切片和map各自的常規(guī)方式和組合字面量方式。
結(jié)構(gòu)體的定義和初始化
讓我們看一個(gè)struct結(jié)構(gòu)體的常規(guī)的定義和初始化是怎么樣的。
常規(guī)方式
常規(guī)方式這樣定義是逐一字段賦值,這樣就比較繁瑣。
- type Profile struct {
 - Name string
 - Age int
 - Gender string
 - }
 - func main() {
 - // 聲明對象
 - var xm Profile
 - // 屬性賦值
 - xm.Name = "iswbm"
 - xm.Age = 18
 - xm.Gender = "male"
 - }
 
組合字面量方式
- type Profile struct { Name string Age int Gender string}func main() { // 聲明 + 屬性賦值 xm := Profile{ Name: "iswbm", Age: 18, Gender: "male", }}
 
數(shù)組的定義和初始化
常規(guī)方式
在下面的代碼中,我們在第1行定義了一個(gè)8個(gè)元素大小的字符串?dāng)?shù)組。然后一個(gè)一個(gè)的給元素賦值。即數(shù)組變量的定義和初始化是分開的。
- var planets [8]stringplanets[0] = "Mercury" //水星planets[1] = "Venus" //金星planets[2] = "Earth" //地球
 
組合字面量方式
該示例中,就是將變量balls的定義和初始化合并了在一起。
- balls := [4]string{"basketball", "football", "Volleyball", "Tennis"}
 
slice的定義和初始化
常規(guī)方式
- // 第一種var s []string //定義切片變量s,s為默認(rèn)零值nils = append(s, "hat", "shirt") //往s中增加元素,len(s):2,cap(s):2// 第二種s := make([]string, 0, 10) //定義s,s的默認(rèn)值不為零值
 
組合字面量方式
由上面的常規(guī)方式可知,首先都是需要先定義切片,然后再往切片中添加元素。接下來我們看下組合字面量方式。
- s := []string{"hat", "shirt"} //定義和初始化一步完成,自動(dòng)計(jì)算切片的容量和長度
 - // or
 - var s = []string{"hat", "shirt"}
 
map的定義和初始化
常規(guī)方式
- //通過make函數(shù)初始化
 - m := make(map[string]int, 10)
 - m["english"] = 99
 - m["math"] = 98
 
組合字面量方式
- m := map[string]int {
 - "english": 99,
 - "math": 98,
 - }
 - //組合字面量初始化多維map
 - m2 := map[string]map[int]string {
 - "english": {
 - 10: "english",
 - },
 - }
 
顯然,使用組合字面量會(huì)比常規(guī)方式簡單了不少。
# 5. 字面量的尋址問題
字面量,說白了就是未命名的常量,跟常量一樣,他是不可尋址的。
這邊以數(shù)組字面量為例進(jìn)行說明
- func foo() [3]int {
 - return [3]int{1, 2, 3}
 - }
 - func main() {
 - fmt.Println(&foo())
 - // cannot take the address of foo()
 - }
 
關(guān)于尋址性的內(nèi)容,你可以在我的上一篇文章中(Go 面試題 008:Go 中的可尋址和不可尋址怎么理解?)進(jìn)行學(xué)習(xí),總結(jié)得非常詳細(xì)。
是不是很簡單?跟著明哥一起來攻克 Go 的各個(gè)邊邊角角的知識(shí)吧
加油噢,我們下篇見
本文轉(zhuǎn)載自微信公眾號(hào)「Go編程時(shí)光」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系Go編程時(shí)光公眾號(hào)。
















 
 
 










 
 
 
 