1429 words
7 minutes

Go语言教程:深入理解不可寻址值

在Go语言中,理解值的可寻址性是编写高效代码的关键之一。本文将通过一个示例代码,深入探讨Go语言中哪些值是可寻址的,哪些值是不可寻址的,并解释其背后的原理。

示例代码解析#

以下是我们将要解析的示例代码:

package main
type Named interface {
// Name 用于获取名字。
Name() string
}
type Dog struct {
name string
}
func (dog *Dog) SetName(name string) {
dog.name = name
}
func (dog Dog) Name() string {
return dog.name
}
func main() {
// 示例1。
const num = 123
//_ = &num // 常量不可寻址。
//_ = &(123) // 基本类型值的字面量不可寻址。
var str = "abc"
_ = str
//_ = &(str[0]) // 对字符串变量的索引结果值不可寻址。
//_ = &(str[0:2]) // 对字符串变量的切片结果值不可寻址。
str2 := str[0]
_ = &str2 // 但这样的寻址就是合法的。
//_ = &(123 + 456) // 算术操作的结果值不可寻址。
num2 := 456
_ = num2
//_ = &(num + num2) // 算术操作的结果值不可寻址。
//_ = &([3]int{1, 2, 3}[0]) // 对数组字面量的索引结果值不可寻址。
//_ = &([3]int{1, 2, 3}[0:2]) // 对数组字面量的切片结果值不可寻址。
_ = &([]int{1, 2, 3}[0]) // 对切片字面量的索引结果值却是可寻址的。
//_ = &([]int{1, 2, 3}[0:2]) // 对切片字面量的切片结果值不可寻址。
//_ = &(map[int]string{1: "a"}[0]) // 对字典字面量的索引结果值不可寻址。
var map1 = map[int]string{1: "a", 2: "b", 3: "c"}
_ = map1
//_ = &(map1[2]) // 对字典变量的索引结果值不可寻址。
//_ = &(func(x, y int) int {
// return x + y
//}) // 字面量代表的函数不可寻址。
//_ = &(fmt.Sprintf) // 标识符代表的函数不可寻址。
//_ = &(fmt.Sprintln("abc")) // 对函数的调用结果值不可寻址。
dog := Dog{"little pig"}
_ = dog
//_ = &(dog.Name) // 标识符代表的函数不可寻址。
//_ = &(dog.Name()) // 对方法的调用结果值不可寻址。
//_ = &(Dog{"little pig"}.name) // 结构体字面量的字段不可寻址。
//_ = &(interface{}(dog)) // 类型转换表达式的结果值不可寻址。
dogI := interface{}(dog)
_ = dogI
//_ = &(dogI.(Named)) // 类型断言表达式的结果值不可寻址。
named := dogI.(Named)
_ = named
//_ = &(named.(Dog)) // 类型断言表达式的结果值不可寻址。
var chan1 = make(chan int, 1)
chan1 <- 1
//_ = &(<-chan1) // 接收表达式的结果值不可寻址。
}

不可寻址值的分类#

1. 常量和字面量#

常量和字面量是不可寻址的,因为它们在编译时就已经确定了值,且没有内存地址。例如:

const num = 123
//_ = &num // 常量不可寻址。

2. 基本类型值的字面量#

基本类型值的字面量也是不可寻址的,因为它们没有分配内存地址:

//_ = &(123) // 基本类型值的字面量不可寻址。

3. 字符串索引和切片结果#

字符串的索引和切片结果值不可寻址,因为字符串是不可变的:

var str = "abc"
//_ = &(str[0]) // 对字符串变量的索引结果值不可寻址。
//_ = &(str[0:2]) // 对字符串变量的切片结果值不可寻址。

4. 算术操作的结果#

算术操作的结果值不可寻址,因为它们是临时值,没有分配内存地址:

//_ = &(123 + 456) // 算术操作的结果值不可寻址。

5. 数组字面量的索引和切片结果#

数组字面量的索引和切片结果值不可寻址,因为它们是临时值:

//_ = &([3]int{1, 2, 3}[0]) // 对数组字面量的索引结果值不可寻址。
//_ = &([3]int{1, 2, 3}[0:2]) // 对数组字面量的切片结果值不可寻址。

6. 字典变量的索引结果#

字典变量的索引结果值不可寻址,因为字典的实现细节使得其值的地址不固定:

var map1 = map[int]string{1: "a", 2: "b", 3: "c"}
//_ = &(map1[2]) // 对字典变量的索引结果值不可寻址。

7. 函数字面量和调用结果#

函数字面量和调用结果值不可寻址,因为它们是临时值:

//_ = &(func(x, y int) int {
// return x + y
//}) // 字面量代表的函数不可寻址。
//_ = &(fmt.Sprintf) // 标识符代表的函数不可寻址。
//_ = &(fmt.Sprintln("abc")) // 对函数的调用结果值不可寻址。

8. 结构体字面量的字段#

结构体字面量的字段不可寻址,因为它们是临时值:

//_ = &(Dog{"little pig"}.name) // 结构体字面量的字段不可寻址。

9. 类型转换和断言结果#

类型转换和断言结果值不可寻址,因为它们是临时值:

//_ = &(interface{}(dog)) // 类型转换表达式的结果值不可寻址。
dogI := interface{}(dog)
//_ = &(dogI.(Named)) // 类型断言表达式的结果值不可寻址。

10. 接收表达式的结果#

接收表达式的结果值不可寻址,因为它们是临时值:

var chan1 = make(chan int, 1)
chan1 <- 1
//_ = &(<-chan1) // 接收表达式的结果值不可寻址。

可寻址值的例外#

尽管大多数情况下上述值是不可寻址的,但也有一些例外情况。例如,对切片字面量的索引结果值是可寻址的:

_ = &([]int{1, 2, 3}[0]) // 对切片字面量的索引结果值却是可寻址的。

总结#

理解值的可寻址性对于编写高效的Go代码至关重要。通过上述示例和解释,我们可以更好地理解哪些值是可寻址的,哪些值是不可寻址的,以及其背后的原因。希望这篇教程能帮助你在Go语言编程中更加得心应手。

Share

If this article helped you, please share it with others!

Go语言教程:深入理解不可寻址值
https://blog.ithuo.net/posts/go-language-tutorial-understanding-unaddressable-values/
Author
Derick
Published at
2022-05-14
License
CC BY-NC-SA 4.0
Last updated on 2022-05-14,1355 days ago

Some content may be outdated

Table of Contents