每月一次,重在积累
Go每日一题(slice)
下面这段代码输出什么?为什么?
1 | func main() { |
答案解析
[1 2 4]
[1 2 4]
当使用 s1[1:] 获得切片 s2,和 s1 共享同一个底层数组,这会导致 s2[1] = 4 语句影响 s1。
而 append 操作会导致底层数组扩容,生成新的数组,因此追加数据后的 s2 不会影响 s1。
Go每日一题(if)
下面选项正确的是?
1 | func main() { |
- A. 1 2
- B. compilation error
答案解析
Go每日一题(map 遍历)
下面这段代码输出什么?
1 | func main() { |
答案解析
0 zero
1 one
// 或者
1 one
0 zero
map 的输出是无序的。
Go每日一题(defer)
下面这段代码输出什么?
1 | func main() { |
答案解析
1 | 10 1 2 3 |
程序执行到 main() 函数三行代码的时候,会先执行 calc() 函数的 b 参数
程序执行到末尾的时候,按照栈先进后出的方式依次执行defer
Go每日一题(结构体指针)
以下代码输出什么:
1 | package main |
- A:运行时 panic;
- B:32;
- C:编译错误;
- D:0
答案解析
正确答案:B。
注意这里不是定义一个结构体类型,而是定义一个结构体类型指针变量,即 x 是一个指针,指针类型是一个匿名结构体。
很显然,x 的值是 nil,因为没有初始化,可以打印证实这一点。
内建函数 len 返回 v 的长度,这取决于具体类型:
- 数组:v 中元素的数量
- 数组指针:*v 中元素的数量(v 为 nil 时 panic)
- 切片、map:v 中元素的数量;若 v 为 nil,len(v) 即为零
- 字符串:v 中字节的数量
- 通道:通道缓存中队列(未读取)元素的数量;若 v 为 nil,len(v) 即为零
Go每日一题(类型方法定义)
下面这段代码输出什么?为什么?
1 | func (i int) PrintInt () { |
- A. 1
- B. compilation error
答案解析
基于类型创建的方法必须定义在同一个包内,上面的代码基于 int 类型创建了 PrintInt() 方法,
由于 int 类型和方法 PrintInt() 定义在不同的包内,所以编译出错。解决的办法可以定义一种新的类型:
1 | type Myint int |
Go每日一题(接口实现)
下面这段代码输出什么?为什么?
1 | type People interface { |
- A. speak
- B. compilation error
答案解析
答案及解析:B。
值类型 Student 没有实现接口的 Speak() 方法,而是指针类型 *Student 实现改方法。
Go每日一题(const iota)
下面这段代码输出什么?
1 | const ( |
答案解析
答案及解析:0 1 1 2。
iota 是 golang 语言的常量计数器,只能在常量的表达式中使用
iota 在 const 关键字出现时将被重置为 0,const 中每新增一行常量声明将使 iota 计数一次。
Go每日一题(iota String())
下面这段代码输出什么?
1 | type Direction int |
答案解析
South 根据 iota 的用法推断 South 的值是 2;另外,
如果定义了 String()方法,当使用 fmt.Printf()、fmt.Print() 和 fmt.Println()会自动使用 String()方法, 实现字符串打印
所以,最终输出South
Go每日一题(map)
下面代码输出什么?
1 | type Math struct { |
- A. 4
- B. compilation error
答案解析
答案及解析:B
类似于 X=Y 的赋值操作,必须知道 X 的地址,才能够将 Y 的值赋给 X,但 go 中的 map 的 value 本身是不可寻地址。
Go每日一题(chan deadlock)
以下代码的输出结果:
1 | package main |
- A:default
- B:panic
答案解析
答案及解析:B
deadlock:无数据取,满数据写
Go每日一题(类型比较)
下面的代码有什么问题?
1 | func main() { |
答案解析
答案及解析:有两处错误
- go 中不同类型是不能比较的,而数组长度是数组类型的一部分,所以 […]int{1} 和 [2]int{1} 是两种不同的类型,不能比较;
- 切片是不能比较的;
Go每日一题(变量作用域)
下面这段代码输出什么?
1 | var p *int |
- A. 5 5
- B. runtime error
答案解析
答案及解析:B。
变量作用域。问题出在操作符 :=,对于使用 := 定义的变量,
如果新变量与同名已定义的变量不在同一个作用域中,那么 Go 会新定义这个变量。
对于本例来说,main() 函数里的 p 是新定义的变量,会遮住全局变量 p,导致执行到 bar()时程序,全局变量 p 依然还是 nil,程序随即 Crash。
Go每日一题(range 循环)
下面这段代码能否正常结束?
1 | func main() { |
答案解析
答案及解析:不会出现死循环,能正常结束。
循环次数在循环开始前就已经确定,循环内改变切片的长度,不影响循环次数。
Go每日一题(可变参数 append)
下面这段代码输出什么?
1 | func change(s ...int) { |
答案解析
答案及解析:
[1 2 0 0 0]
[1 2 3 0 0]
Go 提供的语法糖…,可以将 slice 传进可变函数,不会创建新的切片。
第一次调用 change() 时,append() 操作使切片底层数组发生了扩容,原 slice 的底层数组不会改变;
第二次调用 change() 函数时,使用了操作符[i,j]获得一个新的切片,假定为 slice1, 它的底层数组和原切片底层数组是重合的,不过 slice1 的长度、容量分别是 2、5,所以在 change() 函数中对 slice1 底层数组的修改会影响到原切片。
Go每日一题(array)
下面这段代码输出什么?
1 | func main() { |
答案解析
r = [1 12 13 4 5]
a = [1 12 13 4 5]
切片在 go 的内部结构有一个指向底层数组的指针,当 range 表达式发生复制时,副本的指针依旧指向原底层数组,
所以对切片的修改都会反应到底层数组上,所以通过 v 可以获得修改后的数组元素。
Go每日一题(range)
下面这段代码输出结果正确吗?
1 | type Foo struct { |
输出:
{A} {B} {C}
&{A} &{B} &{C}
答案解析
答案及解析:s2 的输出结果错误。
s2 的输出是 &{C} &{C} &{C},在前面题目我们提到过,for range 使用短变量声明(:=)的形式迭代变量时,变量 i、value 在每次循环体中都会被重用,而不是重新声明。
所以 s2 每次填充的都是临时变量 value 的地址,而在最后一次循环中,value 被赋值为{c}。因此,s2 输出的时候显示出了三个 &{c}。
Go每日一题(map)
下面代码里的 counter 的输出值?
1 | func main() { |
- A. 2
- B. 3
- C. 2 或 3
答案解析
答案及解析:C。
for range map 是无序的,如果第一次循环到 A,则输出 3;否则输出 2。
Go每日一题(goroutine)
关于协程,下面说法正确是()
- A. 协程和线程都可以实现程序的并发执行;
- B. 线程比协程更轻量级;
- C. 协程不存在死锁问题;
- D. 通过 channel 来进行协程间的通信;
答案解析
答案及解析:AD。
Go每日一题(循环)
关于循环语句,下面说法正确的有()
- A. 循环语句既支持 for 关键字,也支持 while 和 do-while;
- B. 关键字 for 的基本使用方法与 C/C++ 中没有任何差异;
- C. for 循环支持 continue 和 break 来控制循环,但是它提供了一个更高级的 break,可以选择中断哪一个循环;
- D. for 循环不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量;
答案解析
答案及解析:CD。
平行赋值: a[i], a[j] = a[j], a[i]
Go每日一题(多重赋值)
下面代码输出正确的是?
1 | func main() { |
- A. s: [Z,B,C]
- B. s: [A,Z,C]
答案解析
答案及解析:A。
多重赋值。
多重赋值分为两个步骤,有先后顺序: 计算等号左边的索引表达式和取址表达式,接着计算等号右边的表达式; 赋值;
所以本例,会先计算 s[i-1],等号右边是两个表达式是常量,所以赋值运算等同于 i, s[0] = 2, “Z”
Go每日一题(强制类型转化)
关于类型转化,下面选项正确的是?
1 | A. |
答案解析
答案及解析:C
知识点:强制类型转化。
D 是类型断言
Go每日一题(switch)
关于 switch 语句,下面说法正确的有?
- A. 条件表达式必须为常量或者整数;
- B. 单个 case 中,可以出现多个结果选项;
- C. 需要用 break 来明确退出一个 case;
- D. 只有在 case 中明确添加 fallthrough 关键字,才会继续执行紧跟的下一个 case;
答案解析
答案及解析:BD。
Go每日一题(类型断言 方法集)
如果 Add() 函数的调用代码为:
1 | func main() { |
则 Add 函数定义正确的是:
1 | A. |
答案解析
答案及解析:AC。
知识点:类型断言、方法集。
go 中有些的变量不可以寻址,指的是不能通过&获得其地址。
所以 func( *A ) 只能接收 *A, func( A ) 可以接收 A 或者 *A ,通过指针一定能得到变量的值 *A -> A
Go每日一题(range)
下面这段代码输出什么,说明原因。
1 | func main() { |
答案解析
0 -> 3
1 -> 3
2 -> 3
3 -> 3
for range 循环的时候会创建每个元素的副本,而不是元素的引用,所以 m[key] = &val 取的都是变量 val 的地址,所以最后 map 中的所有元素的值都是变量 val 的地址,因为最后 val 被赋值为 3,所有输出都是 3