前言
本文是探讨的是"Go基础题
"
平常经常遇到的类型,如有不对, 还望斧正, 感谢!
经典题一 循环变量引用问题
请你分析下面代码的运行结果
代码语言:go复制func main() {
slice := []int{0,1,2,3}
m := make(map[int]*int)
for key,val := range slice {
m[key] = &val
}
for k,v := range m {
fmt.Println(k,"is",*v)
}
}
答案
代码语言:go复制 0 is 3
1 is 3
2 is 3
3 is 3
解析
很有意思的一道题,如果知道的原理的话,就可以不用往下面看了。
重点是在这里,在for循环中,每次向映射里面追加一个,val的地址。 在Go中,val只分配一次地址, 在三次循环中val中存储的值分别为0,1,2,3。但是加到映射m里面的是val的地址,不是val的值,然后最后val存储的值是3
代码语言:go复制 for k,v := range m {
fmt.Println(k,"is",*v)
}
经典题二 defer和panic执行顺序问题
请你分析下面代码的运行结果
代码语言:go复制package main
import (
"fmt"
)
func main() {
defer_call()
}
func defer_call() {
defer func() { fmt.Println("一") }()
defer func() { fmt.Println("二") }()
defer func() { fmt.Println("三") }()
panic("触发异常")
}
答案
代码语言:go复制 三
二
一
panic: 触发异常
解析
同样是很经典的问题
defer 的执行顺序是后进先出,是一个栈的结构,并且defer是会在函数返回之前执行。当出现 panic 语句的时候,会先按照 defer 的后进先出的顺序执行,最后才会执行panic。
经典题三 切片扩容问题
请你分析下面代码的运行结果
代码语言:go复制func SliceDemo(s []int){
s = append(s,0)
for i := range s {
s[i]
}
}
func main (){
s1 := []int{1,2}
s2 :=s1
s2 = append(s2,3)
SliceDemo(s1)
SliceDemo(s2)
fmt.Println(s1,s2)
}
答案
代码语言:go复制[1,2][2,3,4]
解析
切片扩容问题,其实主要是append函数的事,考虑两个点,切片的长度和容积,其实可以简单认为 append函数扩容的时候,一旦长度大于容积的时候,就会创建一个原先容积两倍的新的切片,注意是新的切片。
接下来我来具体分析一下
首先在 main 函数中,定义了一个名为 s1 的整数切片,包含元素 1, 2。
将 s1 的值赋给一个新的变量 s2。现在 s1 和 s2 指向同一个底层数组(共享相同的内存地址)。
使用 append 函数将元素 3 添加到 s2 中,此时会创建一个新的底层数组,并将原始数组中的所有元素复制到新数组中,同时添加新的元素 3。所以现在 s1 和 s2 指向不同的底层数组。
调用 SliceDemo 函数并传入参数 s1。
在 SliceDemo 函数中,使用 append 函数将元素 0 添加到 s 切片中。因为传递的是值拷贝而不是指针,所以这里的 s 是 s1 的副本,因此不会影响 s1 的底层数组。
使用 for 循环遍历 s 切片,并将每个元素加 1。同样,由于这是对 s 副本的操作,所以不会影响 s1 的底层数组。
函数调用结束,返回到 main 函数。
再次调用 SliceDemo 函数并传入参数 s2。
同样的过程再次发生:在 SliceDemo 函数中,使用 append 函数将元素 0 添加到 s 切片中。这次操作发生在 s2 的副本上,所以不会影响 s2 的底层数组。
使用 for 循环遍历 s 切片,并将每个元素加 1。这次操作发生在 s2 的副本上,所以不会影响 s2 的底层数组。
函数调用结束,返回到 main 函数。
最后,打印 s1 和 s2 的值。由于之前的函数调用没有改变这两个切片的底层数组,所以输出仍然是 1, 2 和 1, 2, 3。
我正在参与2023腾讯技术创作特训营第四期有奖征文,快来和我瓜分大奖!