好似又过了几个秋
Go每日一题 (str)
关于字符串连接,下面语法正确的是?
- A. str := ‘abc’ + ‘123’
- B. str := “abc” + “123”
- C. str := ‘123’ + “abc”
- D. fmt.Sprintf(“abc%d”, 123)
答案解析
BD
除了以上两种连接方式,还有strings.Join()
、buffer.WriteString()
等。
Go每日一题 (结构体 引用)
以下代码能否编译?
1 | package main |
答案解析
编译失败
./main.go:18:2: cannot assign to struct field list[“student”].Name in map
- 修改成以下可编译运行
1 | package main |
Go每日一题 (结构体 range 遍历)
以下代码有什么问题,说明原因
1 | package main |
答案解析
遍历结果出现错误,输出结果为
zhou => wang
li => wang
wang => wang
- 正确写法
1 | package main |
- 或者修改
1 | // 将数组依次添加到map中 |
Go每日一题 (nil)
下面赋值正确的是:
- A. var x = nil
- B. var x interface{} = nil
- C. var x string = nil
- D. var x error = nil
答案解析
答案及解析:BD。
nil 只能赋值给指针、chan、func、interface、map 或 slice 类型的变量。
强调下 D 选项的 error 类型,它是一种内置接口类型,看它的源码就知道,所以 D 是对的。
Go每日一题 (继承与多态)
以下代码能通过编译吗?为什么?
1 | package main |
答案解析
编译失败
- 需要修改成以下
1 | func main() { |
Go每日一题 (interface 接口底层类型问题)
以下代码打印出来什么内容,说出为什么。
1 | package main |
答案解析
BBBBBBB
- 空接口eface
1 | type eface struct { // 空接口 |
- 非空接口iface
1 | type iface struct { |
Go每日一题 (encoding/json 导出)
以下代码输出什么?
1 | package main |
- A:{“Time”: “2020-12-20T00:00:00Z”, “N”: 5};
- B:”2020-12-20T00:00:00Z”;
- C:{“N”: 5};
- D
答案解析
B
json.Marshal
函数优先调用MarshalJSON
,然后是MarshalText
,如果都没有,才会走正常的类型编码逻辑。
如果值实现了json.Marshaler
接口并且不是nil
指针,则Marshal
函数会调用其MarshalJSON
方法以生成 JSON。
如果不存在MarshalJSON
方法,但该值实现encoding.TextMarshaler
接口,则Marshal
函数调用其MarshalText
方法并将结果编码为 JSON 字符串。
在本题中匿名结构体内嵌了Time结构体,而该结构体实现了MarshalJSON 方法,因此会调用MarshalJSON方法以生成 JSON。
Go每日一题 (类型 数据溢出 补码)
今天给两道类似的题目,注意,有半数以上的人可能会做错!
- 题一
1 | package main |
- 题二
1 | package main |
它们分别输出什么?
答案解析
题一 -128,题二编译错误?
因为 var b int8 = -128 / a
不是常量表达式,因此 untyped 常量 -128 隐式转换为 int8 类型(即和 a 的类型一致),
所以 -128 / a
的结果是 int8 类型,值是 128,超出了 int8 的范围。因为结果不是常量,允许溢出,
128 的二进制表示是 10000000,正好是 -128 的补码。所以,第一题的结果是 -128。
对于 var b int8 = -128 / a
,因为 a 是 int8 类型常量,所以 -128 / a 是常量表达式,在编译器计算,结果必然也是常量。
因为 a 的类型是 int8,因此 -128 也会隐式转为 int8 类型,128 这个结果超过了 int8 的范围,但常量不允许溢出,因此编译报错。
Go每日一题 (init)
关于 init 函数,下面说法正确的是:
- A. 一个包中,可以包含多个 init 函数;
- B. 程序运行时,先执行依赖包的 init 函数,再执行 main 包内的 init 函数;
- C. main 包中,不能有 init 函数;
- D. init 函数可以被其他函数调用;
答案解析
案及解析:AB。
go文件的初始化顺序
- 引入的包
- 当前包中的常量变量
- 当前包的init
- main函数
Go每日一题 (Go 中 函数 是 一等公民)
下面这段代码输出什么以及原因?
1 | func hello() []string { |
- A. nil
- B. not nil
- C. compilation error
答案解析
B
这道题目里面,是将函数 hello 赋值给变量 h,而不是函数的返回值(即不是进行函数调用),所以输出 not nil。注意 Go 中函数是一等公民。
Go每日一题 (类型断言)
下面这段代码能否编译通过?如果可以,输出什么?
1 | func GetValue() int { |
答案解析
答案及解析:编译失败。
考点:类型断言,类型断言的语法形如:i.(type),其中 i 是接口,type 是固定关键字,需要注意的是,只有接口类型才可以使用类型断言。
Go每日一题 (channel 延迟关闭)
执行下面的代码会发生什么?
1 | package main |
答案解析
ok
close
panic: send on closed channel
记住channel的一些关键特性
- 给一个 nil channel 发送数据,造成永远阻塞
- 从一个 nil channel 接收数据,造成永远阻塞
- 给一个已经关闭的 channel 发送数据,引起 panic
- 从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值
- 无缓冲的channel是同步的,而有缓冲的channel是非同步的
15字口诀:“空读写阻塞,写关闭异常,读关闭空零”
Go每日一题 (WaitGroup)
以下代码有什么问题?
1 | package main |
答案解析
输出结果不唯一,代码存在风险, 所有 go 语句未必都能执行到。
这是使用 WaitGroup 经常犯下的错误!请各位同学多次运行就会发现输出都会不同甚至又出现报错的问题。
这是因为 go 执行太快了,导致 wg.Add(1) 还没有执行 main 函数就执行完毕了。wg.Add 的位置放错了
- 改为下面的代码试试:
1 | package main |
原子操作边界问题,在边界前加锁,边界后解锁。
Go每日一题 (channel)
关于 channel,下面语法正确的是
- A. var ch chan int
- B. ch := make(chan int)
- C. <- ch
- D. ch <-
答案解析
答案及解析:ABC。
A、B 都是声明 channel;C 读取 channel;写 channel 是必须带上值,所以 D 错误。
Go每日一题 (map 0 值)
下面这段代码输出什么?
1 | type person struct { |
- A. 0
- B. 1
- C. Compilation error
答案解析
答案及解析:A。
m 是一个 map,值是 nil。从 nil map 中取值不会报错,而是返回相应的零值,这里值是 int 类型,因此返回 0。
map[p]
在经过 编译后,走的是 runtime.mapaccess1
的逻辑;
而看到 mapaccess1
函数,对于 hmap 是 nil 的情况是直接返回空值;源代码如下:
1 | func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { |
Go每日一题 (可变参数函数)
下面这段代码输出什么?
1 | func hello(num ...int) { |
- A. 18
- B. 5
- C. Compilation error
答案解析
答案及解析:A. 18。
可变参数是切片,切片是引用,所以func内赋值会带出来。
Go每日一题 (强类型)
下面这段代码输出什么?
1 | func main() { |
- A. 13.1
- B. 13
- C. compilation error
答案解析
答案及解析:C。
a 的类型是 int,b 的类型是 float,两个不同类型的数值不能相加,编译报错。
Go语言的类型机制更加严格,没有隐式类型转换,所以不同类型的数据不能直接参与同一个运算。
Go每日一题 (json slice)
以下代码输出什么?
1 | package main |
- A: [1 2 3] [1 2 3] ;
- B: [1 2 3] [3 4 5];
- C: [1 2 3] [3 4 5 6 7 8 9];
- D: [1 2 3] [3 4 5 0 0 0]
答案解析
结果为什么是 [1 2 3] [3 4 5] 呢?
这道题涉及到两个知识点:
- json解析
- slice
第一次解析,aa.Child 是:[1 2 3],cap = 4。第二次解析,aa.Child 先被重置,之后将 3,4,5,7,8,9 一个个 append,
最后 aa.Child 是:[3 4 5 6 7 8 9], cap = 6。
aa在第一次赋值时,就已经确定了长度和容量;第二次反序列化json时,aa的长度和容量是没有变化的,
但是底层数组却发生了变化(底层数组的数据内容,长度,容量都改变了),aa是从底层数组上取数据的
Go每日一题 (数据竞争 main goroutine 和其他 goroutine 调度问题 局部变量问题)
以下代码有什么问题,怎么解决?
1 | total, sum := 0, 0 |
答案解析
1 | func main() { |
解决一下问题:
- i 使用的问题
- data race。因为存在多 goroutine 同时写 total 变量的问题,所以有数据竞争。可以加上 -race 参数验证:
- main 函数先退出了,开启的 goroutine 根本没有机会执行。
Go每日一题 (数据类型)
下面这段代码输出什么?
1 | func main() { |
- A. compilation error
- B. equal
- C. not equal
答案解析
Go 中的数组是值类型,可比较,另外一方面,
数组的长度也是数组类型的组成部分,所以 a 和 b 是不同的类型,是不能比较的,所以编译错误。
Go每日一题 (cap)
以下哪种类型可以使用 cap() 函数?
- A. array
- B. slice
- C. map
- D. channel
答案解析
答案及解析:ABD。
知识点:cap(),cap() 函数不适用 map。
Go每日一题 (interface 零值)
下面这段代码输出什么?
1 | func main() { |
- A. nil
- B. not nil
- C. compilation error
答案解析
答案及解析:A。
当且仅当接口的动态值和动态类型都为 nil 时,接口类型值才为 nil。
Go每日一题 (map 零值)
下面这段代码输出什么?
1 | func main() { |
- A. runtime panic
- B. 0
- C. compilation error
答案解析
答案及解析:B。
删除 map 不存在的键值对时,不会报错,相当于没有任何作用;获取不存在的减值对时,返回值类型对应的零值,所以返回 0。
Go每日一题 (符号输出)
下面这段代码输出什么?
1 | func main() { |
- A. -5 +5
- B. +5 +5
- C. 0 0
答案解析
答案及解析:A。
%d
表示输出十进制数字,+
表示输出数值的符号。这里不表示取反。
Go每日一题 (接口 内嵌 组合)
下面这段代码输出什么?
1 | type People struct{} |
答案解析
答案及解析:teacher showB。
知识点:结构体嵌套。
在嵌套结构体中,People 称为内部类型,Teacher 称为外部类型;
通过嵌套,内部类型的属性、方法,可以为外部类型所有,就好像是外部类型自己的一样。
此外,外部类型还可以定义自己的属性和方法,甚至可以定义与内部相同的方法,这样内部类型的方法就会被“屏蔽”。
这个例子中的 ShowB()
就是同名方法。