1. A\B\C\D 那个有语法错误?
type S struct {
}
func m(x interface{}) {
}
func g(x *interface{}) {
}
func main() {
s := S{}
p := &s
m(s) //A
g(s) //B
m(p) //C
g(p) //D
}
答案
B D 会编译错误
函数参数为 interface {} 时可以接收任何类型的参数,包括用户自定义类型等,
即使是接收指针类型也用 interface {},而不是使用 *interface {}
永远不要使用一个指针指向一个接口类型,因为它已经是一个指针。
2. 下面代码输出什么?
s1 := []int{1, 2, 3}
s2 := s1[1:]
s2[1] = 4
fmt.Println(s1)
s2 = append(s2, 5, 6, 7)
fmt.Println(s1)
答案
[1 2 4]
[1 2 4]
golang 中切片底层的数据结构是数组。当使用 s1 [1:] 获得切片 s2,和 s1 共享同一个底层数组
这会导致 s2 [1] = 4 语句影响 s1。
而 append 操作会导致底层数组扩容,生成新的数组,因此追加数据后的 s2 不会影响 s1
3. 下面代码输出什么?
func foo() (*int, error) {
var i int = 5
return &i, nil
}
func bar() {
//use p
fmt.Println(*p)
}
func main() {
p, err := foo()
if err != nil {
fmt.Println(err)
return
}
bar()
fmt.Println(*p)
}
答案
bar 函数会发生 panic,空指针异常
因为 err 前面没有声明,所以 p, err := foo () 中的 p 是重新声明的局部变量,而不是我们在前面声明的全局变量 p
4. 下面代码输出什么?
func change(s ...int) {
s = append(s, 3)
}
func main() {
slice := make([]int, 5, 5)
slice[0] = 1
slice[1] = 2
change(slice...)
fmt.Println(slice)
change(slice[0:2]...)
fmt.Println(slice)
}
答案
[1 2 0 0 0]
[1 2 3 0 0]
5. 下列代码输出什么?
package main
import "fmt"
func main() {
var m = map[string]int{
"A": 21,
"B": 22,
"C": 23,
}
counter := 0
for k, v := range m {
if counter == 0 {
delete(m, "A")
}
counter++
fmt.Println(k, v)
}
fmt.Println("counter is ", counter)
fmt.Println("m = ", m)
var a = [5]int{1, 2, 3, 4, 5}
var r [5]int
for i, v := range a {
if i == 0 {
a[1] = 12
a[2] = 13
}
r[i] = v
}
fmt.Println("r = ", r)
fmt.Println("a = ", a)
v := []int{1, 2, 3}
for i := range v {
v = append(v, i)
fmt.Println(v)
}
// map遍历 - 会反映实时变化
m1 := map[int]int{1: 1, 2: 2, 3: 3}
for k := range m1 {
m[fmt.Sprintf("%d", k+10)] = k // 添加的新元素可能会被遍历到
fmt.Println(k, m)
}
ch := make(chan int)
go func() {
ch <- 1
ch <- 2
close(ch)
}()
for v := range ch {
fmt.Println(v)
}
}
答案
切片(slice)的range遍历特点:
- 在遍历开始前,就把切片的长度固定下来了
- 比如切片长度是3,那么就一定只遍历3次
- 即使在遍历过程中append增加了元素,也不会影响遍历次数
- 这是Go语言对切片遍历的特意设计,为了避免无限循环
Map的range遍历特点: - 遍历过程中直接作用于当前map的实际状态
- 如果遍历时删除了元素,后续就不会遍历到该元素
- 如果遍历时添加了新元素,可能会在后续遍历中遇到这个新元素
- map的这种特性让它能够在遍历时动态修改内容
总结特点:
- string: 按Unicode字符遍历,只读
- channel: 持续遍历直到关闭
- array: 值拷贝遍历,固定次数
- slice: 固定长度遍历
- map: 直接引用遍历,可实时修改
6. 下列代码有什么问题?
func main() {
var s []int
s = append(s,1)
var m map[string]int
m["one"] = 1
}
答案
切片可以开箱即用,但 map 需要用 make 函数 进行初始化之后才能赋值
7. 下列代码有什么问题?
func Stop(stop <-chan bool) {
close(stop)
}
答案
只有发送方应该关闭通道
// 只读通道 var readCh <-chan int // close(readCh) // 编译错误
接收方不应该关闭通道
// 只写通道 var writeCh chan<- int close(writeCh) // 正确
通道关闭后不能再发送数据,但可以继续接收数据