第46章:一些简单的总结

第46章:一些简单的总结

一些简单的总结

索引:

  • 哪些种类型的值可以有间接底层部分?
  • 哪些种类型的值可以用做内置len(以及cap、close、delete和make)函数调用的实参?
  • 各种容器类型比较
  • 哪些种类型的值可以用组合字面量(T{…})表示?
  • 各种类型的尺寸
  • 哪些种类型的零值使用预声明的nil标识符表示?
  • 我们可以为什么样的类型声明方法?
  • 什么样的类型可以被内嵌在结构体类型中?
  • 哪些函数调用将在编译时刻被估值?
  • 哪些值是可寻址的?
  • 哪些类型不支持比较?
  • 哪些代码元素允许被声明却不使用?
  • 哪些具名代码元素可多个被一起声明在一对小括号()中?
  • 哪些具名代码元素的声明可以被声明在函数内也可以被声明在函数外?
  • 哪些表达式的估值结果可以包含一个额外的可选的值?
  • 几种导致当前协程永久阻塞的方法
  • 几种衔接字符串的方法
  • 官方标准编译器中实现的一些优化
  • 在Go程序运行中将会产生恐慌或者崩溃的情形

哪些种类型的值可以有间接底层部分?

在Go中,下列种类的类型的值可以有间接底层部分:

  • 字符串类型
  • 函数类型
  • 切片类型
  • 映射类型
  • 通道类型
  • 接口类型

注意:此答案基于标准编译器的实现。事实上,函数类型的值是否有间接底层部分是难以证明的。 另外,字符串和接口类型的值在逻辑上应该被认为是不含间接底层部分。 请阅读值部一文(第17章)获取更多信息。

哪些种类型的值可以用做内置len(以及cap、close、delete和make)函数调用的实参?

| len| cap| close| delete| make
---|---|---|---|---|---
字符串值| 可以| | | |
数组或者数组指针值| 可以| 可以| | |
切片值| 可以| 可以| | | 可以
映射值| 可以| | | 可以| 可以
通道值| 可以| 可以| 可以| | 可以

可以被用做内置函数len调用的参数的值的类型都可以被称为(广义上的)容器类型。 这些容器类型的值都可以跟在for-range循环的range关键字后。

各种容器类型比较

类型 容器值是否支持添加新的元素? 容器值中的元素是否可以被替换? 容器值中的元素是否可寻址? 访问容器值元素是否会更改容器长度? 容器值是否可以有间接底层部分?
字符串 否 否 否 否 是(1)
数组 否 是(2) 是(2) 否 否
切片 否(3) 是 是 否 是
映射 是 是 否 否 是
通道 是(4) 否 否 是 是

(1) 对于标准编译器和运行时来说。
(2) 对于可寻址的数组值来说。
(3) 一般说来,一个切片的长度只能通过将另外一个切片赋值给它来被整体替换修改,这里我们不视这种情况为“添加新的元素”。 其实,切片的长度也可以通过调用reflect.SetLen来单独修改。增加切片的长度可以看作是一种变相的向切片添加元素。 但reflect.SetLen函数的效率很低,因此很少使用。
(4) 对于带缓冲并且缓冲未满的通道来说。

哪些种类型的值可以用组合字面量(T{…})表示?

下面在四种类型的值(除了切片和映射类型的零值)可以用组合字面量表示。

类型(T) T{}是类型T的零值?
结构体类型 是
数组类型 是
切片类型 否
(零值用nil表示)
映射类型 否
(零值用nil表示)

各种类型的尺寸

详见值复制成本(第34章)一文。

哪些种类型的零值使用预声明的nil标识符表示?

下面这些类型的零值可以用预声明的nil标识符表示。

类型(T) T(nil)的尺寸
指针 1 word
切片 3 words
映射 1 word
通道 1 word
函数 1 word
接口 2 words

上表列出的尺寸为标准编译器的结果。 一个word(原生字)在32位的架构中为4个字节,在64位的架构中为8个字节。 一个Go值的间接底层部分(第17章)未统计在尺寸中。

一个类型的零值的尺寸和其它非零值的尺寸是一致的。

我们可以为什么样的类型声明方法?

详见方法(第22章)一文。

什么样的类型可以被内嵌在结构体类型中?

详见类型内嵌(第24章)一文。

哪些函数调用将在编译时刻被估值?

如果一个函数调用在编译时刻被估值,则估值结果为一个常量。

函数 返回类型 其调用是否总是在编译时刻估值?
unsafe.Sizeof uintptr 总是如此。

但是请注意,如果这样的一个函数调用的实参类型为一个类型参数,则此函数调用的结果将不被视为一个常量。
unsafe.Alignof
unsafe.Offsetof

len

| int| 否

Go语言白皮书中提到:

  • 如果表达式s表示一个字符串常量,则表达式len(s)将在编译时刻估值;
  • 如果表达式s表示一个数组或者数组的指针,并且s中不含有数据接收操作和估值结果为非常量的函数调用,则表达式len(s)和cap(s)将在编译时刻估值。

请注意,即使这样的一个函数调用在编译时刻被估值,如果函数调用的实参类型为一个类型参数,则此函数调用的结果将不被视为一个常量。

cap

real

| 默认类型为
float64
(结果为类型不确定值)| 否

Go语言白皮书提到: 表达式real(s)和imag(s)在s为一个复数常量表达式时才在编译时刻估值。

imag

complex| 默认类型为
complex128
(结果为类型不确定值)| 否

Go语言白皮书提到: 表达式complex(sr, si)只有在sr和si都为常量表达式的时候才在编译时刻估值。

哪些值是可寻址的?

请阅读此条问答(第51章)获取详情。

哪些类型不支持比较?

请阅读此条问答(第51章)获取详情。

哪些代码元素允许被声明却不使用?

| 允许被声明却不使用?
---|---
包引入| 不允许
类型| 允许
变量| 包级全局变量允许,但局部变量不允许(对于官方标准编译器)。
常量| 允许
函数| 允许
跳转标签| 不允许

哪些具名代码元素可多个被一起声明在一对小括号()中?

下面这些同种类的代码元素可多个被一起声明在一对小括号()中:

  • 包引入
  • 类型
  • 变量
  • 常量

函数是不能多个被一起声明在一对小括号()中的。跳转标签也不能。

哪些具名代码元素的声明可以被声明在函数内也可以被声明在函数外?

下面这些代码元素的声明既可以被声明在函数内也可以被声明在函数外:

  • 类型
  • 变量
  • 常量

包引入必须被声明在其它种类的代码元素的声明之前。

函数必须声明在任何函数体之外。匿名函数可以定义在函数体内,但那不属于声明。

跳转标签必须声明在函数体内。

哪些表达式的估值结果可以包含一个额外的可选的值?

下列表达式的估值结果可以包含一个额外的可选的值:

| 语法| 额外的可选的值(语法示例中的ok)的含义| 舍弃额外的可选的值会对估值行为发生影响吗?
---|---|---|---
映射元素访问| e, ok = aMap[key]| 键值key对应的条目是否存储在映射值中| 否
数据接收| e, ok = <- aChannel| 被接收到的值e是否是在通道关闭之前发送的| 否
类型断言| v, ok = anInterface.(T)| 接口值的动态类型是否为类型T| 是
(当可选的值被舍弃并且断言失败的时候,将产生一个恐慌。)

几种导致当前协程永久阻塞的方法

无需引入任何包,我们可以使用下面几种方法使当前协程永久阻塞:

  1. 向一个永不会被接收数据的通道发送数据。
make(chan struct{}) <- struct{}{}
// 或者
make(chan<- struct{}) <- struct{}{}

````- 从一个未被并且将来也不会被发送数据的(并且保证永不会被关闭的)通道读取数据。

<-make(chan struct{}) // 或者 <-make(<-chan struct{}) // 或者 for range make(<-chan struct{}) {}

``- 从一个nil通道读取或者发送数据。

chan struct{}(nil) <- struct{}{} // 或者 <-chan struct{}(nil) // 或者 for range chan struct{}(nil) {}

`- 使用一个不含任何分支的select流程控制代码块。

select{} ` `` ````

` `

几种衔接字符串的方法

详见字符串(第19章)一文。

官方标准编译器中实现的一些优化

详见Go语言101维基中的一文。

在Go程序运行中将会产生恐慌或者崩溃的情形

详见Go语言101维基中的一文。

`

` `


本书由老貘历时三年写成。目前本书仍在不断改进和增容中。你的赞赏是本书和Go101.org网站不断增容和维护的动力。

(请搜索关注微信公众号“Go 101”或者访问github.com/golang101/golang101获取本书最新版)

`