1.关于 cap() 函数的适用类型,下面说法正确的是()
-
A. array
-
B. slice
-
C. map
-
D. channel
参考答案及解析:ABD。知识点:cap(),cap() 函数不适用 map。
2.下面这段代码输出什么?
2.下面这段代码输出什么?
func main() { var i interface{} if i == nil { fmt.Println("nil") return } fmt.Println("not nil") }
-
A. nil
-
B. not nil
-
C. compilation error
参考答案及解析:A。当且仅当接口的动态值和动态类型都为 nil 时,接口类型值才为 nil。
func main() { s := make(map[string]int) delete(s, "h") fmt.Println(s["h"]) }
-
A. runtime panic
-
B. 0
-
C. compilation error
参考答案及解析:B。删除 map 不存在的键值对时,不会报错,相当于没有任何作用;获取不存在的减值对时,返回值类型对应的零值,所以返回 0。
第二题
1.下面属于关键字的是()
-
A.func
-
B.struct
-
C.class
-
D.defer
参考答案及解析:ABD。知识点:Go 语言的关键字。Go 语言有 25 个关键字,看下图:
2.下面这段代码输出什么?
func main() { i := -5 j := +5 fmt.Printf("%+d %+d", i, j) }
-
A. -5 +5
-
B. +5 +5
-
C. 0 0
参考答案及解析:A。%d表示输出十进制数字,+表示输出数值的符号。这里不表示取反。
3.下面这段代码输出什么?
type People struct{} func (p *People) ShowA() { fmt.Println("showA") p.ShowB() } func (p *People) ShowB() { fmt.Println("showB") } type Teacher struct { People } func (t *Teacher) ShowB() { fmt.Println("teacher showB") } func main() { t := Teacher{} t.ShowB() }
参考答案及解析:teacher showB。知识点:结构体嵌套。在嵌套结构体中,People 称为内部类型,Teacher 称为外部类型;通过嵌套,内部类型的属性、方法,可以为外部类型所有,就好像是外部类型自己的一样。此外,外部类型还可以定义自己的属性和方法,甚至可以定义与内部相同的方法,这样内部类型的方法就会被“屏蔽”。这个例子中的 ShowB() 就是同名方法。
第三题
1.定义一个包内全局字符串变量,下面语法正确的是()
-
A. var str string
-
B. str := “”
-
C. str = “”
-
D. var str = “”
参考答案及解析:AD。B 只支持局部变量声明;C 是赋值,str 必须在这之前已经声明;
2.下面这段代码输出什么?
func hello(i int) { fmt.Println(i) } func main() { i := 5 defer hello(i) i = i + 10 }
参考答案及解析:5。这个例子中,hello() 函数的参数在执行 defer 语句的时候会保存一份副本,在实际调用 hello() 函数时用,所以是 5.
type People struct{} func (p *People) ShowA() { fmt.Println("showA") p.ShowB() } func (p *People) ShowB() { fmt.Println("showB") } type Teacher struct { People } func (t *Teacher) ShowB() { fmt.Println("teacher showB") } func main() { t := Teacher{} t.ShowA() }
参考答案及解析:
showA
showB
知识点:结构体嵌套。这道题可以结合第 12 天的第三题一起看,Teacher 没有自己 ShowA(),所以调用内部类型 People 的同名方法,需要注意的是第 5 行代码调用的是 People 自己的 ShowB 方法。
第四题
1.下面代码输出什么?
func main() { str := "hello" str[0] = 'x' fmt.Println(str) }
-
A. hello
-
B. xello
-
C. compilation error
参考代码及解析:C。知识点:常量,Go 语言中的字符串是只读的。
2.下面代码输出什么?
func incr(p *int) int { *p++ return *p } func main() { p :=1 incr(&p) fmt.Println(p) }
-
A. 1
-
B. 2
-
C. 3
参考答案及解析:B。知识点:指针,incr() 函数里的 p 是 *int
类型的指针,指向的是 main() 函数的变量 p 的地址。第 2 行代码是将该地址的值执行一个自增操作,incr() 返回自增后的结果。
3.对 add() 函数调用正确的是()
func add(args ...int) int { sum := 0 for _, arg := range args { sum += arg } return sum }
-
A. add(1, 2)
-
B. add(1, 3, 7)
-
C. add([]int{1, 2})
-
D. add([]int{1, 3, 7}…)
参考答案及解析:ABD。知识点:可变函数
第五天
1.下面代码下划线处可以填入哪个选项?
func main() { var s1 []int var s2 = []int{} if __ == nil { fmt.Println("yes nil") }else{ fmt.Println("no nil") } }
-
A. s1
-
B. s2
-
C. s1、s2 都可以
参考答案及解析:A。知识点:nil 切片和空切片。nil 切片和 nil 相等,一般用来表示一个不存在的切片;空切片和 nil 不相等,表示一个空的集合。
2.下面这段代码输出什么?
func main() { i := 65 fmt.Println(string(i)) }
-
A. A
-
B. 65
-
C. compilation error
参考答案及解析:A。UTF-8 编码中,十进制数字 65 对应的符号是 A。
3.下面这段代码输出什么?
type A interface { ShowA() int } type B interface { ShowB() int } type Work struct { i int } func (w Work) ShowA() int { return w.i + 10 } func (w Work) ShowB() int { return w.i + 20 } func main() { c := Work{3} var a A = c var b B = c fmt.Println(a.ShowA()) fmt.Println(b.ShowB()) }
参考答案及解析:13 23。知识点:接口。一种类型实现多个接口,结构体 Work 分别实现了接口 A、B,所以接口变量 a、b 调用各自的方法 ShowA() 和 ShowB(),输出 13、23。
第六题
1.切片 a、b、c 的长度和容量分别是多少?
func main() { s := [3]int{1, 2, 3} a := s[:0] b := s[:2] c := s[1:2:cap(s)] }
参考答案及解析:a、b、c 的长度和容量分别是 0 3、2 3、1 2。知识点:数组或切片的截取操作。截取操作有带 2 个或者 3 个参数,形如:[i:j] 和 [i:j:k],假设截取对象的底层数组长度为 l。在操作符 [i:j] 中,如果 i 省略,默认 0,如果 j 省略,默认底层数组的长度,截取得到的切片长度和容量计算方法是 j-i、l-i。操作符 [i:j:k],k 主要是用来限制切片的容量,但是不能大于数组的长度 l,截取得到的切片长度和容量计算方法是 j-i、k-i。
2.下面代码中 A B 两处应该怎么修改才能顺利编译?
func main() { var m map[string]int //A m["a"] = 1 if v := m["b"]; v != nil { //B fmt.Println(v) } }
func main() { m := make(map[string]int) m["a"] = 1 if v,ok := m["b"]; ok { fmt.Println(v) } }
在 A 处只声明了map m ,并没有分配内存空间,不能直接赋值,需要使用 make(),都提倡使用 make() 或者字面量的方式直接初始化 map。
B 处,v,k := m[“b”] 当 key 为 b 的元素不存在的时候,v 会返回值类型对应的零值,k 返回 false。
type A interface { ShowA() int } type B interface { ShowB() int } type Work struct { i int } func (w Work) ShowA() int { return w.i + 10 } func (w Work) ShowB() int { return w.i + 20 } func main() { c := Work{3} var a A = c var b B = c fmt.Println(a.ShowB()) fmt.Println(b.ShowA()) }
-
A. 23 13
-
B. compilation error
参考答案及解析:B。知识点:接口的静态类型。a、b 具有相同的动态类型和动态值,分别是结构体 work 和 {3};a 的静态类型是 A,b 的静态类型是 B,接口 A 不包括方法 ShowB(),接口 B 也不包括方法 ShowA(),编译报错。看下编译错误:
a.ShowB undefined (type A has no field or method ShowB)
b.ShowA undefined (type B has no field or method ShowA)
第七题
1.下面代码中,x 已声明,y 没有声明,判断每条语句的对错。
1. x, _ := f() 2. x, _ = f() 3. x, y := f() 4. x, y = f()
参考答案及解析:错、对、对、错。知识点:变量的声明。1.错,x 已经声明,不能使用 :=;2.对;3.对,当多值赋值时,:= 左边的变量无论声明与否都可以;4.错,y 没有声明。
2.下面代码输出什么?
func increaseA() int { var i int defer func() { i++ }() return i } func increaseB() (r int) { defer func() { r++ }() return r } func main() { fmt.Println(increaseA()) fmt.Println(increaseB()) }
-
A. 1 1
-
B. 0 1
-
C. 1 0
-
D. 0 0
参考答案及解析:B。知识点:defer、返回值。注意一下,increaseA() 的返回参数是匿名,increaseB() 是具名。关于 defer 与返回值的知识点,后面我会写篇文章详细分析,到时候可以看下文章的讲解。
下面代码输出什么?
type A interface { ShowA() int } type B interface { ShowB() int } type Work struct { i int } func (w Work) ShowA() int { return w.i + 10 } func (w Work) ShowB() int { return w.i + 20 } func main() { var a A = Work{3} s := a.(Work) fmt.Println(s.ShowA()) fmt.Println(s.ShowB()) }
-
A. 13 23
-
B. compilation error
参考答案及解析:A。知识点:类型断言。这道题可以和第 15 天的第三题 和第 16 天的第三题结合起来看
第八题
1.下面代码段输出什么?
type Person struct { age int } func main() { person := &Person{28} // 1. defer fmt.Println(person.age) // 2. defer func(p *Person) { fmt.Println(p.age) }(person) // 3. defer func() { fmt.Println(person.age) }() person.age = 29 }
参考答案及解析:29 29 28。变量 person 是一个指针变量 。
1.person.age 此时是将 28 当做 defer 函数的参数,会把 28 缓存在栈中,等到最后执行该 defer 语句的时候取出,即输出 28;
2.defer 缓存的是结构体 Person{28} 的地址,最终 Person{28} 的 age 被重新赋值为 29,所以 defer 语句最后执行的时候,依靠缓存的地址取出的 age 便是 29,即输出 29;
3.闭包引用,输出 29;
又由于 defer 的执行顺序为先进后出,即 3 2 1,所以输出 29 29 28。
第九题
1.下面这段代码正确的输出是什么?
func f() { defer fmt.Println("D") fmt.Println("F") } func main() { f() fmt.Println("M") }
A. F M D
B. D F M
C. F D M
参考答案及解析:C。被调用函数里的 defer 语句在返回之前就会被执行,所以输出顺序是 F D M。
2.下面代码输出什么?
type Person struct { age int } func main() { person := &Person{28} // 1. defer fmt.Println(person.age) // 2. defer func(p *Person) { fmt.Println(p.age) }(person) // 3. defer func() { fmt.Println(person.age) }() person = &Person{29} }
参考答案及解析:29 28 28。这道题在第 19 天题目的基础上做了一点点小改动,前一题最后一行代码 person.age = 29 是修改引用对象的成员 age,这题最后一行代码 person = &Person{29} 是修改引用对象本身,来看看有什么区别。
1处.person.age 这一行代码跟之前含义是一样的,此时是将 28 当做 defer 函数的参数,会把 28 缓存在栈中,等到最后执行该 defer 语句的时候取出,即输出 28;
2处.defer 缓存的是结构体 Person{28} 的地址,这个地址指向的结构体没有被改变,最后 defer 语句后面的函数执行的时候取出仍是 28;
3处.闭包引用,person 的值已经被改变,指向结构体 Person{29},所以输出 29.
由于 defer 的执行顺序为先进后出,即 3 2 1,所以输出 29 28 28。
总结:
1、切片cap()容量和长度如何区分?
2、map类型数据结构首先要make然后存储数据
3、切片数据结构和通道数据结构
4、接口数据类型和结构体类型调用 & 通过断言转化
5、defer的作用