Golang基础题
1、下面这段代码的输出什么?
func main() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
}
输出:
打印后
打印中
打印前
panic: 触发异常
......解析:协程遇到panic时,遍历本协程的defer链表(defer 是一个链表的结构,新的defer会被追加到defer链表的最前面,可理解为后进先出),并执行defer。在执行defer过程中,遇到recover则停止panic,返回recover处继续往下执行。如果没有遇到recover,遍历完本协程的defer链表后,向stderr抛出panic信息
2、下面代码会输出什么?
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
a := 1
b := 2
defer calc("1", a, calc("10", a, b))
a = 0
defer calc("2", a, calc("20", a, b))
b = 1
}
输出:
10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4解析:这里需要注意的是,当执行到defer定义时,首先会对参数进行求值,然后参数被压入函数调用栈,此时不会进入defer函数体,而是直到函数返回时才调用defer函数体。参数被压入函数调用栈时,如果参数是值类型,那么将复制值,如果参数是指针,那么将复制指针而不是复制指针指向的值。
3、下面代码输出什么?
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.Printf("key: %d, value: %d \n", k, *v)
}
}
输出:
key: 0, value: 3
key: 1, value: 3
key: 2, value: 3
key: 3, value: 3解析:整个循环中,val都是同一个全局变量(与for range 结构有关),地址是一样的,每次遍历该地址上的值都会被新的值覆盖掉,所以在遍历结束后,该地址存的值是切片上的最后一个元素3。具体for range 坑点可以参考这篇文章Golang 之 我被 for-range 循环进去了
4、下面代码会输出什么
func main() {
i := make([]int, 5)
i = append(i, 1, 2, 3)
fmt.Println(i)
j := make([]int, 0)
j = append(j, 1, 2, 3, 4)
fmt.Println(j)
}
输出:
[0 0 0 0 0 1 2 3]
[1 2 3 4]解析:make函数创建切片时,如果有指定长度,则分配一个指定长度的数组,初始化值为0,并返回一个引用该数组的slice
5、下面这段代码有错误吗?
func funcMui(x,y int)(sum int,error){
return x+y,nil
}
解析:第二个返回值没有命名,在函数有多个返回值时,只要有一个返回值有命名,其他的也必须命名。如果有多个返回值必须加上括号 ();如果只有一个返回值且命名也必须加上括号 ()。这里的第一个返回值有命名 sum,第二个没有命名,所以错误。
6、new() 和 make() 的区别
解析:new 和 make的区别:
- new 返回的是指向类型的指针,即类型 *Type;make 返回的还是引用类型本身;
- new 分配的空间被清零,也就是类型的零值(int类型的零值是0,string类型的零值是"",引用类型的零值是nil)。make 分配空间后,会进行初始化;
- new 可以分配任意类型的数据,make 只能用来分配及初始化类型为 slice、map、chan 的数据
7、下面代码能否编译通过,如果能,输出什么?
func main() {
list := new([]int)
list = append(list, 1)
fmt.Println(list)
s1 := []int{1, 2, 3}
s2 := []int{4, 5}
s1 = append(s1, s2)
fmt.Println(s1)
}
> 解析:编译错误
首先 new([]int) 之后的 list 是一个 *[]int 类型的指针,不能对指针执行append操作;
再看 s1 = append(s1, s2),编译失败,追加一个切片,切片需要解包,正确姿势是:s1 = append(s1, s2...)
8、下面代码能否编译通过
func Test7(t *testing.T) {
sn1 := struct {
age int
name string
}{age: 11, name: "qq"}
sn2 := struct {
age int
name string
}{age: 11, name: "qq"}
// 可以比较
if sn1 == sn2 {
fmt.Println("sn1 == sn2")
}
sm1 := struct {
age int
m map[string]string
}{age: 11, m: map[string]string{"a": "1"}}
sm2 := struct {
age int
m map[string]string
}{age: 11, m: map[string]string{"a": "1"}}
// 编译错误,map不能进行比较
if sm1 == sm2 {
fmt.Println("sm1 == sm2")
}
}
解析:
详细解析可参考文章:golang之struct能不能比较
9、通过指针变量 p 访问其成员变量 name,有哪几种方式?
A. p.name
B. (&p).name
C. (*p).name
D. p->name
答案:AC
10、关于字符串连接,下面语法正确的是
A. str := 'abc' + '123'
B. str := "abc" + "123"
C. str := '123' + "abc"
D. fmt.Sprintf("abc%d", 123)
答案:BD
11、关于iota,下面代码会输出什么?
func main() {
const (
x = iota
_
i
j
y
z = "aa"
o
k = iota
p
q
)
fmt.Println(x,i,j, y, z,o, k, p, q)
}
输出:
0
2
3
4
aa
aa
7
8
9解析:iota 初始值为0,往下进行自增1 进行赋值,遇到自定义赋值的常量,则对当前常量进行自定义赋值,后面常量的值跟当前常量的值一致,直到又遇到自定义赋值的常量,在这个过程中iota一直保持自增状态
12、下面赋值正确的是
A. var x = nil
B. var x interface{} = nil
C. var x string = nil
D. var x error = nil
答案:BD
13、关于channel,下面语法正确的是?
A. var ch chan int
B. ch := make(chan int)
C. <- ch
D. ch <-
答案:ABC
解析:往channel写数据时,<- 右端必须要有值
14、下面代码输出什么?
func hello (num ...int) {
num[0] = 18
}
func main() {
i := []int{5, 6, 7}
hello(i...)
fmt.Println(i[0])
}
A.18
B.5
C.Compilation error
答案:18
解析:func hello (num ...int) 为函数传入可变参数的表达式。...type本质上是一个数组切片,也就是[]type,切片作为函数参数传的是指针拷贝,具体的坑点可参考文章:(正经版)面试官:切片作为函数参数是传值还是传引用?
15、下面选择哪个?
func main() {
a := 5
b := 8.1
fmt.Println(a + b)
}
A.13.1
B.13
C.compilation error
答案:C
解析:整型和浮点型不能相加
16、下面代码输出什么?
func Test15(t *testing.T) {
a := [5]int{1, 2, 3, 4, 5}
s := a[3:4:4]
fmt.Println(s[0])
}
答案:4
解析:a[low : high : max],其中由 a[low : high] 生成切片,切片的容量等于(max - low)
17、下面代码输出什么?
func main() {
a := [2]int{5, 6}
b := [3]int{5, 6}
if a == b {
fmt.Println("equal")
} else {
fmt.Println("not equal")
}
}
A. compilation error
B. equal
C. not equal
答案:A
解析:数组的比较的前提是两者要有相同的长度
18、下列哪个类型可以使用cap() 函数
A. array
B. slice
C. map
D. channel
答案:ABD
解析:cap()函数只支持 数组(array),数组指针(*array),切片(slice),通道(channel)
19、下面代码输出什么?
func main() {
var i interface{}
if i == nil {
fmt.Println("nil")
return
}
fmt.Println("not nil")
}
答案:nil
解析:引用类型的零值为 nil, 常见的引用类型有: point, function, interface, slice, channel, map
bool 的零值为 false;整数的零值为 0;浮点数的零值为 0.0;string 的零值为 空字符串 ""
20、下面代码输出什么?
func main() {
s := make(map[string]int)
delete(s, "h")
fmt.Println(s["h"])
}
答案:0
解析:使用内置函数delete删除map的键值对时,如果map为nil 或者键值对不存在时,delete不会做任何操作,程序不会报错;获取不存在的键值对时,返回值为对应类型的零值,所以返回0
21、下面代码输出什么?
func main() {
i := -5
j := +5
fmt.Printf("%+d %+d", i, j)
}
答案:-5 +5
解析:%+d 是带符号输出
22、定义一个全局字符串变量,下列正确的是?
A. var str string
B. str := ""
C. str = ""
D. var str = ""
答案:AD
解析:B选项只支持局部变量声明;C是赋值语句,str 必须在这之前就已经声明
23、下面代码输出什么?
func f(i int) {
fmt.Println(i)
}
func main() {
i := 5
defer f(i)
i += 10
}
答案:5
解析:f() 函数的参数在执行defer语句的时候会保存一份副本,在实际调用f()函数时使用,所以是5
24、下面代码输出什么?
func main() {
str := "hello"
str[0] = 'x'
fmt.Println(str)
}
A. hello
B. xello
C. compilation error
答案:C 编译错误
解析:Golang中,字符串是只读的,可以打印,如 fmt.Println(str[0]),不可修改
25、关于可变参数的函数调用正确的是?
func add(args ...int) int {
sum := 0
for _, arg := range args {
sum += arg
}
return sum
}
A. add(1, 2)
B. add(1, 3, 7)
C. add([]int{1, 2})
D. add([]int{1, 3, 7}…)
答案:ABD
解析:可变参数如果是切片,必须是 []type{}... 格式,别问,问就是规定的
26、下面代码填入哪个变量会输出 yes nil
func main() {
var s1 []int
var s2 = []int{}
if ___ == nil {
fmt.Println("yes nil")
} else {
fmt.Println("no nil")
}
}
答案:s1
解析:s1 表示nil切片(切片的零值)nil 切片和 nil 相等;s2 表示空切片,表示一个空的集合,和nil不相等
27、下面代码会输出什么?
func main() {
i := 65
fmt.Println(string(i))
}
A. A
B. 65
C. compilation error
答案:A
解析:UTF-8编码中,十进制数字65对应的符号是A
28、切片 a,b,c 的容量分别是多少?
func main() {
s := [3]int{1, 2, 3}
a := s[:0]
b := s[:2]
c := s[1:2:cap(s)]
fmt.Println(cap(a))
fmt.Println(cap(b))
fmt.Println(cap(c))
}
答案:
3
3
2解析:数组的容量等于数据的长度,s[a️c] 容量计算可以参考第16题,切片容量计算可参考文章:(正经版)面试官:切片作为函数参数是传值还是传引用?
29、下面代码输出什么?
func increaseA() int {
var i int
defer func() {
i++
}()
return i
}
func increaseB() (r int) {
defer func() {
r++
}()
return r
}
func main() {
fmt.Println(increaseA())
fmt.Println(increaseB())
}
答案:
0
1解析:参考文章:关于defer坑坑洼洼的使用细节你mark了吗
30、函数 f1(),f2(),f3() 分别返回什么?
func f1() (r int) {
defer func() {
r++
}()
return 0
}
func f2() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
func f3() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
func main() {
fmt.Println(f1())
fmt.Println(f2())
fmt.Println(f3())
}
答案:
1
5
1解析:参考文章:关于defer坑坑洼洼的使用细节你mark了吗
31、下面代码输出什么?
type Person struct {
age int
}
func main() {
person := &Person{28}
defer fmt.Printf("输出1:%v\n", person.age)
defer func(p *Person) {
fmt.Printf("输出2:%v\n", p.age)
}(person)
defer func() {
fmt.Printf("输出3:%v\n", person.age)
}()
person.age = 29
}
答案:
输出3:29
输出2:29
输出1:28解析:
输出3作为闭包引用,所以输出29
输出2作为函数参数,相当于变量拷贝,这里是指针变量的拷贝,指针变量的值依然是指向原有变量的地址,因此最终会输出29
输出1作为函数参数,在 defer 定义时就把值传递给 defer 函数体缓存起来,因此输出28
32、下面的两个切片声明中有上面区别?哪个更可取?
A. var a []int
B. a := []int{}
答案:A
解析:
A 声明的是 nil 切片;
B 声明的是长度和容量都为 0 的空切片。
A 的声明不会分配内存,优先选择
33、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 {}
永远不要使用一个指针指向一个接口类型,因为它已经是一个指针。
34、下面代码输出什么?
func main() {
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]解析:相信看过大叔之前文章的同学一下就能回答上来,不赘述,附上文章链接:(正经版)面试官:切片作为函数参数是传值还是传引用?
35、下列代码输出什么?
func main() {
if a := 1; false {
} else if b := 2; false {
} else {
fmt.Println(a, b)
}
}
答案:1 2
解析:if 语句声明变量,该变量的作用域只属于整个 if block 结构
36、下面代码输出什么
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
a := 1
b := 2
defer calc("A", a, calc("10", a, b))
a = 0
defer calc("B", a, calc("20", a, b))
b = 1
}
答案:
10 1 2 3
20 0 2 2
B 0 2 2
A 1 3 4解析:参考文章:关于defer坑坑洼洼的使用细节你mark了吗
37、下列代码输出什么
func main() {
m := map[int]string{0: "zero", 1: "one"}
for k, v := range m {
fmt.Println(k, v)
}
}
答案:
1 one
0 zero
或
0 zero
1 one
解析:map 输出是无序的
38、下面代码能否正常打印输出
type People interface {
Speak(string)
}
type Student struct{}
func (stu *Student) Speak(think string) {
fmt.Println(think)
}
func main() {
var peo People
student := Student{}
peo = student
peo.Speak("speak")
}
答案:编译失败,Student does not implement People (Speak method has pointer receiver)
解析:
因为是 *Student 实现了接口 Speak,并不是类型Student,
但是如果是 Student 类型实现了 Speak 方法,那么用 值类型的Student{}
或是指针类型的&Student{}
都可以访问到该方法。
39、下面代码输出什么
const (
a = iota
b = iota
)
const (
name = "name"
c = iota
d = iota
)
func main() {
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
}
答案:0 1 1 2
解析:
1、每个常量块(const block) 结构体都会重置和初始化 iota 的值为 0,往后常量块中每新增一行常量声明 iota将自增 1
2、即便 iota 在常量块(const block) 中并非是首行出现,iota 也会在首行进行初始化(值为 0),完后常量进行自增 1
40、下面代码输出什么?
func main() {
const a = iota
const b = iota + 3
const c, d = iota, iota + 3
const (
e = iota // e=0
f = iota + 4 // f=5
g // g=6
)
println(a, b, c, d, e, f, g)
}
答案:0 3 0 3 0 5 6
解析:参考38题
41、下面代码输出什么?
type People interface {
Show()
}
type Student struct{}
func (stu *Student) Show() {
}
func main() {
var s *Student
if s == nil {
fmt.Println("s is nil")
} else {
fmt.Println("s is not nil")
}
var p People = s
if p == nil {
fmt.Println("p is nil")
} else {
fmt.Println("p is not nil")
}
}
答案:
s is nil
p is not nil解析:当且仅当动态值和动态类型都为 nil 时,接口类型值才为 nil。上面的代码,给变量 p 赋值之后,
p 的动态值是 nil,但是动态类型却是 *Student,是一个 nil 指针,所以相等条件不成立。
42、下面代码输出什么
type Direction int
const (
North Direction = iota
East
South
West
)
func (d Direction) String() string {
return [...]string{"North", "East", "South", "West"}[d]
}
func main() {
fmt.Println(South)
}
答案:South
解析:
在go中如果重写了String 方法,那在调用fmt.Println时就会自动去执行 String 方法
43、下面代码能够正常输出吗
type Square struct {
x, y int
}
var m = map[string]Square{
"foo": Square{2, 3},
}
func main() {
m["foo"].x = 1
fmt.Println(m["foo"].x)
}
答案:编译失败
解析:
对于类似 X = Y 的赋值操作,必须知道 X 的地址,才能够将 Y 的值赋给 X,
但 go 中的 map 的 value 本身是不可寻址的。
正确的写法有两种:
第一种:square := m["foo"] square.x = 1
这种写法无法修改 m["foo"] 中 x 变量的值> 第二种:
var m = map[string]*Square{ "foo": &Square{2, 3}, } m["foo"].x = 1
这种可以修改 m["foo"] 中 x 变量的值
44、下面代码输出什么
var p *int
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
45、下面代码输出什么
func main() {
v := []int{1, 2, 3}
for i := range v {
v = append(v, i)
fmt.Println(v)
}
}
答案:
[1 2 3 0]
[1 2 3 0 1]
[1 2 3 0 1 2]解析:可参考文章:Golang 之 我被 for-range 循环进去了
46、下面代码会输出什么?
func main() {
var m = [...]int{1, 2, 3}
for i, v := range m {
go func() {
fmt.Println(i, v)
}()
}
time.Sleep(time.Second * 1)
}
答案:
2 3
2 3
2 3\解析:for range 使用短变量声明 (:=) 的形式迭代变量,
需要注意的是,变量 i、v 在每次循环体中都会被重用,而不是重新声明。详细可参考文章:Golang 之 我被 for-range 循环进去了
47、下面代码会输出什么?
func test(n int) (r int) {
// 第一个defer
defer func() {
r += n
recover()
}()
var f func()
// 第二个defer
defer f()
f = func() {
r += 2
}
return n + 1
}
func main() {
fmt.Println(test(3))
}
答案:7
解析:
首先,defer 是和返回值被命名的函数一起使用的,而且 defer 语句定义时对外部变量引用的方式是闭包引用(细节可参考文章:关于defer坑坑洼洼的使用细节你mark了吗,建议先看完文章再往下看,否则有的同学可能会有点接不上)
为什么第二个 defer 不起作用呢?其实第二个 defer 的延迟函数啥都没干,甚至程序执行到 f() 时还抛出 panic 了。我们都知道 程序在执行到 defer 定义时并不会马上执行,而是对捕捉到的 defer 结果进行链表构建,把关联的函数入栈缓存,在程序 return 前再进行出栈执行。那么问题就很明显了,我们看在程序执行到 defer f() 时,此时的 f 是啥?此时的 f 的值为 nil,所以最后出栈执行一个nil 函数,就相当于:var f func() f() // panic: runtime error: invalid memory address or nil pointer dereference
因此这也是为什么第一个 defer 为什么要有 recover() 方法。后面的 f = func() { r += 2 } 是赋值操作,对返回值 r 没影响
48、下面代码输出什么
func test(n int) (r int) {
// 第一个defer
defer func() {
r += n
}()
var f func()
f = func() {
r += 2
}
// 第二个defer
defer f()
return n + 1
}
func main() {
fmt.Println(test(3))
}
答案:9
解析:参考47题
49、下面代码输出什么
func main() {
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)
}
答案:
r = [1 2 3 4 5]
a = [1 12 13 4 5]\解析:参考文章:Golang 之 我被 for-range 循环进去了
50、下面代码输出什么?
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]解析:参考文章:(正经版)面试官:切片作为函数参数是传值还是传引用?
51、下面代码输出什么
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)
}
答案:
counter is 2 或者 counter is 3
for range map 是无序的
52、关于协程,下列说法正确的是
A. 协程和线程都可以实现程序的并发执行;
B. 线程比协程更轻量级;
C. 协程不存在死锁问题;
D. 通过 channel 来进行协程间的通信;
答案:AD
53、关于循环语句,下面说法正确的是
A. 循环语句既支持 for 关键字,也支持 while 和 do-while;
B. 关键字 for 的基本使用方法与 C/C++ 中没有任何差异;
C. for 循环支持 continue 和 break 来控制循环,但是它提供了一个更高级的 break,可以选择中断哪一个循环;
D. for 循环不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量;
答案:CD
解析:
C : golang中,goto语句可以无条件地转移到过程中指定的行,通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。例如:func main() { for a:=0;a<5;a++{ fmt.Println(a) if a>3{ goto Loop } } Loop: //放在for后边 fmt.Println("test") }
D:for 语句执行变量平衡初始化
for i, j := 0, 5; i < j; i, j = i+1, j-1 { fmt.Println("hhh") }
54、下面代码输出什么?
func main() {
i := 1
s := []string{"A", "B", "C"}
i, s[i-1] = 2, "Z"
fmt.Printf("s: %v \n", s)
}
答案:s: [Z B C]
解析:golang运算符优先级:
(+ 、-)> (=、+=、-=、*=、/=、 %=、 >=、 <<=、&=、^=、|=)
55、关于 switch 语句,下面说法正确的是
A. 条件表达式必须为常量或者整数;
B. 单个 case 中,可以出现多个结果选项;
C. 需要用 break 来明确退出一个 case;
D. 只有在 case 中明确添加 fallthrough 关键字,才会继续执行紧跟的下一个 case;
答案:BD
解析:
switch/case 后是一个表达式(即:常量,变量,一个有返回的函数都可以);A 错误
无需用一个 break 来明确退出一个 case,B 错
56、下列 Add 函数定义正确的是:
func Test54(t *testing.T) {
var a Integer = 1
var b Integer = 2
var i interface{} = &a
sum := i.(*Integer).Add(b)
fmt.Println(sum)
}
A.
type Integer int
func (a Integer) Add(b Integer) Integer {
return a + b
}
B.
type Integer int
func (a Integer) Add(b *Integer) Integer {
return a + *b
}
C.
type Integer int
func (a *Integer) Add(b Integer) Integer {
return *a + b
}
D.
type Integer int
func (a *Integer) Add(b *Integer) Integer {
return *a + *b
}
答案:AC
解析:Add()函数的参数应该是 Integer 类型,BD错误
57、关于 bool 变量 b 的赋值,下面错误的用法是?
A. b = true
B. b = 1
C. b = bool(1)
D. b = (1 == 2)
答案:BC
解析:
golang 中 bool 类型变量的值只能是 true / false,B 错误
golang中没有 bool(1) 这种强制转换的表达方式,C 错误
58、关于变量的自增和自减操作,下面语句正确的是:
A
i := 1
i++
B
i := 1
j = i++
C
i := 1
++i
D
i := 1
i--
答案:AD
解析:golang 中没有 ++i 或者 --i这种表达式,B 中 新变量 j 的声明和初始化应该使用 :=
59、关于 GetPodAction 定义,下面赋值正确的是
type Fragment interface {
Exec(transInfo *TransInfo) error
}
type GetPodAction struct {
}
func (g GetPodAction) Exec(transInfo *TransInfo) error {
...
return nil
}
A. var fragment Fragment = new(GetPodAction)
B. var fragment Fragment = GetPodAction
C. var fragment Fragment = &GetPodAction{}
D. var fragment Fragment = GetPodAction{}
答案:ACD
60、关于整型切片的初始化,下面正确的是
A. s := make([]int)
B. s := make([]int, 0)
C. s := make([]int, 5, 10)
D. s := []int{1, 2, 3, 4, 5}
答案:BCD
解析:使用内置方法 make() 初始化切片时必须指定长度
61、下面代码是否会触发异常
func main() {
runtime.GOMAXPROCS(1)
intChan := make(chan int, 1)
stringChan := make(chan string, 1)
intChan <- 1
stringChan <- "hello"
select {
case value := <-intChan:
fmt.Println(value)
case value := <-stringChan:
panic(value)
}
}
答案:可能会,可能不会
解析:当两个 chan 同时有值时,即 select 中的 case 都匹配时,select 会随机选择一个可用通道做收发操作
62、关于 channel 的特性,下面说法正确的是
A. 给一个 nil channel 发送数据,造成永远阻塞
B. 从一个 nil channel 接收数据,造成永远阻塞
C. 给一个已经关闭的 channel 发送数据,引起 panic
D. 从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值
答案:ABCD
解析:概念性的知识点玩命记就是了
63、下面代码有什么问题:
const i = 100
var j = 123
func main() {
fmt.Println(&j, j)
fmt.Println(&i, i)
}
答案:编译失败:cannot take the address of i
解析:golang 中,常量无法寻址,是不能进行做指针操作的
64、下面代码输出什么?
func main() {
x := []string{"a", "b", "c"}
for v := range x {
fmt.Print(v)
}
}
答案:012
解析:range 一个返回值时,这个值是下标,两个值时,第一个是下标,第二个是值,当 x 为 map 时,第一个是 key,第二个是 value
65、关于无缓冲和有缓冲的 channel,下面说法正确的是?
A. 无缓冲的 channel 是默认的缓冲为 1 的 channel;
B. 无缓冲的 channel 和有缓冲的 channel 都是同步的;
C. 无缓冲的 channel 和有缓冲的 channel 都是非同步的;
D. 无缓冲的 channel 是同步的,而有缓冲的 channel 是非同步的;
答案:D
66、下面代码输出什么?
func Foo(x interface{}) {
if x == nil {
fmt.Println("empty interface")
return
}
fmt.Println("non-empty interface")
}
func main() {
var x *int = nil
Foo(x)
}
答案:non-empty interface
解析:
接口除了有静态类型,还有动态类型和动态值,
当且仅当动态值和动态类型都为 nil 时,接口类型值才为 nil。
这里的 x 的动态类型是 *int,所以 x 不为 nil
67、关于 select 机制,下面说法正确的是
A. select 机制用来处理异步 IO 问题;
B. select 机制最大的一条限制就是每个 case 语句里必须是一个 IO 操作;
C. golang 在语言级别支持 select 关键字;
D. select 关键字的用法与 switch 语句非常类似,后面要带判断条件;
答案:ABC
解析:
select后不用带判断条件,是case后带判断条件。
select的用法与switch非常类似,由select开始一个新的选择块,每个选择条件由case语句来描述。与switch语句可以选择任何可使用相等比较的条件相比,select有比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作,确切的说,应该是一个面向channel的IO操作。
68、下面代码有什么问题
func Stop(stop <-chan bool) {
close(stop)
}
func main() {
c := make(chan bool)
Stop(c)
}
答案:编译失败:invalid operation: close(stop) (cannot close receive-only channel)
解析:从输出的错误信息也可以知道,有方向的 channel 不可以被关闭
69、下列代码输出什么
func main() {
var x = []int{2: 2, 3, 0: 1}
fmt.Println(x)
}
答案:[1, 0, 2, 3]
解析:切片初始化特性如下:
var x = []int{2: 2, 3, 0: 1, 1} // [1 1 2 3] var x = []int{2: 2, 3, 0: 1, 4:4} // [1 0 2 3 4] var x = []int{2: 2, 3, 0: 1, 5:4} // [1 0 2 3 0 4]
70、下面代码输出什么
func incr(p *int) int {
*p++
return *p
}
func main() {
v := 1
incr(&v)
fmt.Println(v)
}
答案:2
71、下面代码输出什么
func main() {
var a = []int{1, 2, 3, 4, 5}
var r = make([]int, 0)
for i, v := range a {
if i == 0 {
a = append(a, 6, 7)
}
r = append(r, v)
}
fmt.Println(r)
}
答案:[1,2,3,4,5]
解析:for range 循环时,首先会对循环对象的长度进行拷贝,循环的次数取决于拷贝的值,该拷贝值不受循环对象在循环过程中长度变化的影响
72、下面代码有什么问题
func main() {
var s []int
s = append(s,1)
var m map[string]int
m["one"] = 1
}
答案:编译失败:panic: assignment to entry in nil map
解析:切片声明即可使用,但 map 需要用 make 函数 进行初始化之后才能赋值
73、下面代码输出什么
func main() {
fmt.Println("1")
defer fmt.Println("in main")
defer func() {
fmt.Println("3")
}()
fmt.Println("2")
defer func() {
defer func() {
panic("panic again and again")
}()
panic("panic again")
}()
panic("panic once")
}
答案:
1
2
3
in main
panic: panic once
panic: panic again
panic: panic again and again解析:协程遇到panic时,遍历本协程的defer链表,并执行defer。在执行defer过程中,遇到recover则停止panic,返回recover处继续往下执行。如果没有遇到recover,遍历完本协程的defer链表后,向stderr抛出panic信息,可参考文章 关于defer坑坑洼洼的使用细节你mark了吗
关注公众号「大叔说码」 跟大叔一起打基础,我们下期见~