1. 先看下面代码,是否有问题。
1
2
3
4
5
6
7
func main(){
const sh = "zhang"
fmt.Println(&sh, sh)

var nl string = "san"
fmt.Println(&nl, nl)
}

运行结果:

1
2
3
☁  test_all  go run main.go
# command-line-arguments
./main.go:7:14: cannot take the address of sh

在go中,不同于变量是在运行时分配内存空间,常量通常会被编译器在预处理阶段直接展开,作为指令数据使用,所以常量无法寻址。

  1. 单向channel关闭
  • 只读channel不允许关闭
1
2
3
4
5
6
7
8
9
func main(){
ch := make(chan string, 1000)
ch <- "name"
RecvSim(ch)
}
func RecvSim(ch <- chan string) {
fmt.Println(<-ch)
close(ch)
}
1
2
3
☁  test_all  go run main.go
# command-line-arguments
./main.go:35:7: invalid operation: close(ch) (cannot close receive-only channel)
  • 只写channel可以关闭
1
2
3
4
5
6
7
8
9
func main(){
ch := make(chan string, 1000)
SendSim(ch)
}

func SendSim(ch chan <- string) {
ch <- "zzz..."
close(ch)
}
  1. 判断下面代码输出的结果:
    情况A:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type T struct {
ls []int
}

func foo(t T) {
t.ls[0] = 100
}

func main() {
t := T{
ls: []int{1,2,3},
}
foo(t)

fmt.Println(t.ls)
}

正确答案是:[100,2,3]

情况B:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type T struct {
ls []int
}

func foo(t T) {
t.ls = append(t.ls, 100)
t.ls[0] = 1000
}

func main() {
t := T{
ls: []int{1,2,3},
}
foo(t)

fmt.Println(t.ls)
}

正确答案:[1,2,3]

函数foo参数t虽然是传传递,但是复制后的参数t的ls字段最开始指向底层的数组是公用的,所以情况A修改了第一个元素的值也会影响到原来的变量;而情况B犹豫追加了一个元素,导致函数foo的参数t底层数据进行了重新内存分配,指向了新的底层数组,所以不会影响原来的值。

  1. context.WithValue容易忽视的陷阱。

查看下面的代码,说出下面代码输出的结果:

1
2
3
4
5
6
7
func main() {
ctx0 := context.Background()
ctx := context.WithValue(ctx0, "name", "zhangliang")
ctx = context.WithValue(ctx0, "age", 12)
fmt.Println(ctx.Value("name"))
fmt.Println(ctx.Value("age"))
}

正确结果是:

1
2
3
☁  test_all  go run main.go
<nil>
12

这是因为每组context只允许存储一对key-value,后面的age把前面的name覆盖了。正确写法是:

1
2
3
4
5
6
7
func main() {
ctx0 := context.Background()
ctx1 := context.WithValue(ctx0, "name", "zhangliang")
ctx2 := context.WithValue(ctx1, "age", 12)
fmt.Println(ctx2.Value("name"))
fmt.Println(ctx2.Value("age"))
}

输出:

1
2
3
☁  test_all  go run main.go
zhangliang
12

此时的ctx2结构如下:
001