1.7 什么叫字面量和组合字面量? ============================== 说出来不怕大家笑话,在去年年初我刚开始学习 Go 基础的时候,有一个词困扰了我好久,这个词就是 **字面量**\ 。 之所以会让我理解困难,是因为在 Go 之前,我都是写 Python 的,而且写了很多年,在 Python 中万物皆对象,不管一个字面量)有没有变量名来承接,在使用上没有任何区别的,因此在学 Go 之前,我其实都不知道有字面量这么个概念。 .. code:: python >>> {"name": "iswbm"}.get("name") # 使用字面量 'iswbm' >>> >>> profile={"name": "iswbm"} # 使用变量名 >>> profile.get("name") 'iswbm' 那么字面量到底是啥东西?怎么那么多的基础教程里反复会提及,却好像没什么人把这个名词的概念解释一下呢?难道是因为这是常识?尴尬。。 |image0| 相信正在看这篇文章的你,可能也会有此疑问,今天我就梳理一下,我理解中的 **字面量**\ ,是什么意思?它与普通变量有什么区别? 1. 什么是字面量? ----------------- 在 Go 中内置的基本类型有: - 布尔类型:\ ``bool`` - 11个内置的整数数字类型:\ ``int8``, ``uint8``, ``int16``, ``uint16``, ``int32``, ``uint32``, ``int64``, ``uint64``, ``int``, ``uint``\ 和\ ``uintptr`` - 浮点数类型:\ ``float32``\ 和\ ``float64`` - 复数类型:\ ``complex64``\ 和\ ``complex128`` - 字符串类型:\ ``string`` 而这些基本类型值的文本,就是基本类型字面量。 比如下面这两个字符串,都是字符串字面量,没有用变量名或者常量名来指向这两个字面量,因此也称之为 **未命名常量**\ 。 .. code:: go "hello, iswbm" `hello, iswbm` 2. 同值不同字面量 ----------------- 值的字面量(literal)是代码中值的文字表示,一个值可能存在多种字面量表示。 举个例子,十进制的数值 15,可以由三种字面量表示 .. code:: go // 16进制 0xF // 8进制 0o17 // 2进制 0b1111 通过比较,可以看出他们是相等的 .. code:: go import "fmt" func main() { fmt.Println(15 == 0xF) // true fmt.Println(15 == 017) // true fmt.Println(15 == 0b1111) // true } 3. 字面量和变量有啥区别? ------------------------- 下面这是一段很正常的代码 .. code:: go func foo() string { return "hello" } func main() { bar := foo() fmt.Println(&bar) } 可要是换成下面这样 .. code:: go func foo() string { return "hello" } func main() { fmt.Println(&foo()) } 可实际上这段代码是有问题的,运行后会报错 :: ./demo.go:11:14: cannot take the address of foo() 你一定觉得很奇怪吧? 为什么先用变量名承接一下再取地址就不会报错,而直接使用在函数返回后的值上取地址就不行呢? 这是因为,如果不使用一个变量名承接一下,函数返回的是一个字符串的文本值,也就是字符串字面量,而这种基本类型的字面量是不可寻址的。 要想使用 ``&`` 进行寻址,就必须得用变量名承接一下。 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。 翻译成中文大致如下: 组合字面量是为结构体、数组、切片和map构造值,并且每次都会创建新值。它们由字面量的类型后紧跟大括号及元素列表。每个元素前面可以选择性的带一个相关key。 **什么意思呢?所谓的组合字面量其实就是把对象的定义和初始化放在一起了**\ 。 接下来让我们看看结构体、数组、切片和map各自的常规方式和组合字面量方式。 结构体的定义和初始化 ~~~~~~~~~~~~~~~~~~~~ 让我们看一个struct结构体的常规的定义和初始化是怎么样的。 **常规方式** 常规方式这样定义是逐一字段赋值,这样就比较繁琐。 .. code:: golang type Profile struct { Name string Age int Gender string } func main() { // 声明对象 var xm Profile // 属性赋值 xm.Name = "iswbm" xm.Age = 18 xm.Gender = "male" } **组合字面量方式** .. code:: golang type Profile struct { Name string Age int Gender string } func main() { // 声明 + 属性赋值 xm := Profile{ Name: "iswbm", Age: 18, Gender: "male", } } 数组的定义和初始化 ~~~~~~~~~~~~~~~~~~ **常规方式** 在下面的代码中,我们在第1行定义了一个8个元素大小的字符串数组。然后一个一个的给元素赋值。即数组变量的定义和初始化是分开的。 .. code:: golang var planets [8]string planets[0] = "Mercury" //水星 planets[1] = "Venus" //金星 planets[2] = "Earth" //地球 **组合字面量方式** 该示例中,就是将变量balls的定义和初始化合并了在一起。 .. code:: golang balls := [4]string{"basketball", "football", "Volleyball", "Tennis"} slice的定义和初始化 ~~~~~~~~~~~~~~~~~~~ **常规方式** .. code:: golang // 第一种 var s []string //定义切片变量s,s为默认零值nil s = append(s, "hat", "shirt") //往s中增加元素,len(s):2,cap(s):2 // 第二种 s := make([]string, 0, 10) //定义s,s的默认值不为零值 **组合字面量方式** 由上面的常规方式可知,首先都是需要先定义切片,然后再往切片中添加元素。接下来我们看下组合字面量方式。 .. code:: golang s := []string{"hat", "shirt"} //定义和初始化一步完成,自动计算切片的容量和长度 // or var s = []string{"hat", "shirt"} map的定义和初始化 ~~~~~~~~~~~~~~~~~ **常规方式** .. code:: golang //通过make函数初始化 m := make(map[string]int, 10) m["english"] = 99 m["math"] = 98 **组合字面量方式** .. code:: golang m := map[string]int { "english": 99, "math": 98, } //组合字面量初始化多维map m2 := map[string]map[int]string { "english": { 10: "english", }, } 显然,使用组合字面量会比常规方式简单了不少。 5. 字面量的寻址问题 ------------------- 字面量,说白了就是未命名的常量,跟常量一样,他是不可寻址的。 这边以数组字面量为例进行说明 .. code:: go func foo() [3]int { return [3]int{1, 2, 3} } func main() { fmt.Println(&foo()) // cannot take the address of foo() } 关于寻址性的内容,你可以在我的另一篇文章中(\ `1.15 Go中哪些是可寻址,哪些是不可寻址的? `__\ )进行学习,总结得非常详细。 .. |image0| image:: http://image.iswbm.com/20211008234746.png