Go 面试题
下面面试题均收集来自go语言中文网
1.下面是否可以编译通过?不能通过是为什么
func main() {
list := new([]int)
list = append(list, 1)
fmt.Println(list)
}
解析:不能通过,因为 new 函数返回到 是一个指针,而 append
函数是对切片的操作,不是指针,可以修改成*list= append(*list,1)
.
channel 、slice、map 必须通过 make 初始化,这题也可以使用make
函数,make([]int,0),0表示初始化有 0 个元素,这样结果才会是[1],如果写成 make([]int,1),表示有一个元素了,执行结果就为[0,1],默认有一个 0 元素。
2.是否可以编译通过?如果通过,输出什么?
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3}
s2 := []int{4, 5}
s1 = append(s1, s2)
fmt.Println(s1)
}
解析:不能通过,append切片时,要后面追加…,正确应该:s1 = append(s1, s2...)
,此时结果才会是 []int{1, 2, 3,4,5}
3.已知字符串 s=’hello’,把第一个字母变成 c
s := "hello"
c := []byte(s) // 将字符串 s 转换为 []byte 类型
c[0] = 'c'
s = string(c) // 再转换回 string 类型
fmt.Printf("%s", s)// cello
4、以下代码有什么问题,说明原因
type student struct {
Name string
Age int
}
func pase_student() {
m := make(map[string]*student)
stus := []student{
{Name: "zhou",Age: 24},
{Name: "li",Age: 23},
{Name: "wang",Age: 22},
}
for _,stu := range stus {
m[stu.Name] =&stu
}
}
解答:考点 for
, 与Java的foreach一样,都是使用副本的方式。所以m[stu.Name]=&stu实际上一致指向同一个指针, 最终该指针的值为遍历的最后一个struct的值拷贝,正确写法因该是:
for i:=0;i<len(stus);i++ {
m[stus[i].Name] = &stus[i]
}
5.下面的代码会输出什么,并说明原因
func main() {
runtime.GOMAXPROCS(1)
wg := sync.WaitGroup{}
wg.Add(20)
for i := 0; i < 10; i++ {
go func() {
fmt.Println("A: ", i)
wg.Done()
}()
}
for i:= 0; i < 10; i++ {
go func(i int) {
fmt.Println("B: ", i)
wg.Done()
}(i)
}
wg.Wait()
}
解答:考察 go执行的随机性和闭包。谁也不知道执行后打印的顺序是什么样的,所以只能说是随机数字。 但是A:均为输出10,B:从0~9输出(顺序不定)。 第一个go func中i是外部for的一个变量,地址不变化。遍历完成后,最终i=10。 故go func执行时,i的值始终是10。
第二个go func中i是函数参数,与外部for中的i完全是两个变量。 尾部(i)将发生值拷贝,go func内部指向值拷贝地址。
6、下面代码会触发异常吗
func main() {
runtime.GOMAXPROCS(1)
int_chan := make(chan int, 1)
string_chan := make(chan string, 1)
int_chan <- 1
string_chan <- "hello"
select {
case value := <-int_chan:
fmt.Println(value)
case value := <-string_chan:
panic(value)
}
}
解答:select会随机选择一个可用通用做收发操作。 所以代码是有肯触发异常,也有可能不会。 单个chan如果无缓冲时,将会阻塞。但结合 select可以在多个chan间等待执行。有三点原则:
1.select 中只要有一个case能return,则立刻执行。
2.当如果同一时间有多个case均能return则伪随机方式抽取任意一个执行。
3.如果没有一个case能return则可以执行”default”块。
上面语句可能会触发panic,case到下面的时候就触犯了,case到上面的就打印1.
7、下面代码输出什么?
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
}
解答:这道题考察 defer ,需要注意到defer执行顺序和值传递. index:1肯定是最后执行的,但是index:1的第三个参数是一个函数,所以最先被调用 calc("10",1,2)==>10,1,2,3
,执行index:2时,与之前一样,需要先调用 calc("20",0,2)==>20,0,2,2
,执行到b=1时候开始调用,index:2==>calc("2",0,2)==>2,0,2,2
,最后执行 index:1==>calc("1",1,3)==>1,1,3,4
.
8、select可以用于什么,常用语gorotine的完美退出
解答:golang 的 select 就是监听 IO 操作,当 IO 操作发生时,触发相应的动作
每个case语句里必须是一个IO操作,确切的说,应该是一个面向channel的IO操作
9、map如何顺序读取
map不能顺序读取,是因为他是无序的,想要有序读取,首先的解决的问题就是,把key变为有序,所以可以把key放入切片,对切片进行排序,遍历切片,通过key取值。
10、交替打印数字和字母,使用两个 goroutine
交替打印序列,一个 goroutinue
打印数字, 另外一个goroutine
打印字母, 最终效果如下 12AB34CD56EF78GH910IJ
解答:问题很简单,使用 channel
来控制打印的进度。使用两个 channel
,来分别控制数字和字母的打印序列, 数字打印完成后通过 channel
通知字母打印, 字母打印完成后通知数字打印,然后周而复始的工作。
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
chan_n := make(chan bool)
chan_c := make(chan bool, 1)
done := make(chan struct{})
go func() {
for i := 1; i < 11; i += 2 {
<-chan_c
fmt.Print(i)
fmt.Print(i + 1)
chan_n <- true
}
}()
go func() {
char_seq := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"}
for i := 0; i < 10; i += 2 {
<-chan_n
fmt.Print(char_seq[i])
fmt.Print(char_seq[i+1])
chan_c <- true
}
done <- struct{}{}
}()
chan_c <- true
<-done
}
看完上面的代码,是不是会有些疑惑,为什么 chan_c
需要缓存,而 chan_n
不需要呢?
当两个打印 goroutine
无限交替运行时,没有缓存是OK的,但很明显上面的示例不是,打印数字的 goroutine
先退出,也就没有了 goroutine
来读取 chan_c
中的内容了, 而打印字母的goroutine
就会阻塞在 chan_c <- true
,这样就导致了死锁。
11.下面两段代码输出什么。
// 1.
func main() {
s := make([]int, 5)
s = append(s, 1, 2, 3)
fmt.Println(s)
}
// 2.
func main() {
s := make([]int,0)
s = append(s,1,2,3,4)
fmt.Println(s)
}
两段代码分别输出:
[0 0 0 0 0 1 2 3]
[1 2 3 4]
参考解析:这道题考的是使用 append 向 slice 添加元素,第一段代码常见的错误是 [1 2 3],需要注意。
12.下面这段代码有什么缺陷
func funcMui(x,y int)(sum int,error){
return x+y,nil
}
参考答案:第二个返回值没有命名。
参考解析:
在函数有多个返回值时,只要有一个返回值有命名,其他的也必须命名。如果有多个返回值必须加上括号();如果只有一个返回值且命名也必须加上括号()。这里的第一个返回值有命名 sum,第二个没有命名,所以错误。
13.new() 与 make() 的区别
参考答案:
new(T) 和 make(T,args) 是 Go 语言内建函数,用来分配内存,但适用的类型不同。
new(T) 会为 T 类型的新值分配已置零的内存空间,并返回地址(指针),即类型为 *T
的值。换句话说就是,返回一个指针,该指针指向新分配的、类型为 T 的零值。适用于值类型,如数组、结构体等。
make(T,args) 返回初始化之后的 T 类型的值,这个值并不是 T 类型的零值,也不是指针 *T
,是经过初始化之后的 T 的引用。make() 只适用于 slice、map 和 channel.
14 下面这段代码能否通过编译?不能的话,原因是什么?如果通过,输出什么?
func main() {
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"}}
if sm1 == sm2 {
fmt.Println("sm1 == sm2")
}
}
参考答案及解析:编译不通过 invalid operation: sm1 == sm2
这道题目考的是结构体的比较,有几个需要注意的地方:
- 结构体只能比较是否相等,但是不能比较大小。
- 相同类型的结构体才能够进行比较,结构体是否相同不但与属性类型有关,还与属性顺序相关,sn3 与 sn1 就是不同的结构体;
sn3:= struct {
name string
age int
}{age:11,name:"qq"}
- 如果 struct 的所有成员都可以比较,则该 struct 就可以通过 == 或 != 进行比较是否相等,比较时逐个项进行比较,如果每一项都相等,则两个结构体才相等,否则不相等;
那什么是可比较的呢,常见的有 bool、数值型、字符、指针、数组等,像切片、map、函数等是不能比较的。
15 下面这段代码能否通过编译?如果通过,输出什么?
package main
import "fmt"
type MyInt1 int
type MyInt2 = int
func main() {
var i int =0
var i1 MyInt1 = i
var i2 MyInt2 = i
fmt.Println(i1,i2)
}
参考答案及解析:编译不通过,cannot use i (type int) as type MyInt1 in assignment。
这道题考的是类型别名与类型定义的区别。
第 5 行代码是基于类型 int 创建了新类型 MyInt1,第 6 行代码是创建了 int 的类型别名 MyInt2,注意类型别名的定义时 = 。所以,第 10 行代码相当于是将 int 类型的变量赋值给 MyInt1 类型的变量,Go 是强类型语言,编译当然不通过;而 MyInt2 只是 int 的别名,本质上还是 int,可以赋值。
第 10 行代码的赋值可以使用强制类型转化 var i1 MyInt1 = MyInt1(i).
16 关于字符串连接,下面语法正确的是?
A. str := ‘abc’ + ‘123’
B. str := “abc” + “123”
C str := ‘123’ + “abc”
D. fmt.Sprintf(“abc%d”, 123)
参考答案及解析:BD。知识点:字符串连接。除了以上两种连接方式,还有 strings.Join()、buffer.WriteString()等。
17 下面这段代码能否编译通过?如果可以,输出什么?
const (
x = iota
_
y
z = "zz"
k
p = iota
)
func main() {
fmt.Println(x,y,z,k,p)
}
参考答案及解析:编译通过,输出:0 2 zz zz 5。知识点:iota 的使用。iota在const关键字出现时将被重置为0(const内部的第一行之前),const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。z=”zz”,下面k 默认就等于了上面z的值,下面的p虽然等于iota,但是没有const关键字,所以还是自增。
18 下面赋值正确的是()
A. var x = nil
B. var x interface{} = nil
C. var x string = nil
D. var x error = nil
参考答案及解析:BD。知识点:nil 值。nil 只能赋值给指针、chan、func、interface、map 或 slice 类型的变量。强调下 D 选项的 error 类型,它是一种内置接口类型,看下方贴出的源码就知道,所以 D 是对的。
type error interface {
Error() string
}
19 关于init函数,下面说法正确的是()
A. 一个包中,可以包含多个 init 函数;
B. 程序编译时,先执行依赖包的 init 函数,再执行 main 包内的 init 函数;
C. main 包中,不能有 init 函数;
D. init 函数可以被其他函数调用;
1.参考答案及解析:AB。关于 init() 函数有几个需要注意的地方:
init() 函数是用于程序执行前做包的初始化的函数,比如初始化包里的变量等;
一个包可以出线多个 init() 函数,一个源文件也可以包含多个 init() 函数;
同一个包中多个 init() 函数的执行顺序没有明确定义,但是不同包的init函数是根据包导入的依赖关系决定的(看下图);
init() 函数在代码中不能被显示调用、不能被引用(赋值给函数变量),否则出现编译错误;
一个包被引用多次,如 A import B,C import B,A import C,B 被引用多次,但 B 包只会初始化一次;
引入包,不可出现死循坏。即 A import B,B import A,这种情况编译失败;
20 下面这段代码输出什么以及原因?
func hello() []string {
return nil
}
func main() {
h := hello
if h == nil {
fmt.Println("nil")
} else {
fmt.Println("not nil")
}
}
A. nil
B. not nil
C. compilation error
答案及解析:B。这道题目里面,是将 hello() 赋值给变量 h,而不是函数的返回值,所以输出 not nil。如果换成h:=hello()
;那么h= nil;就打印nil了。
21 下面这段代码能否编译通过?如果可以,输出什么?
func GetValue() int {
return 1
}
func main() {
i := GetValue()
switch i.(type) {
case int:
println("int")
case string:
println("string")
case interface{}:
println("interface")
default:
println("unknown")
}
}
参考答案及解析:编译失败。考点:类型选择,类型选择的语法形如:i.(type),其中 i 是接口,type 是固定关键字,需要注意的是,只有接口类型才可以使用类型选择。看下关于接口的文章,这里要断言类型,需要用到反射。
22 关于channel,下面语法正确的是()
A. var ch chan int
B. ch := make(chan int)
C. <- ch
D. ch <-
参考答案及解析:ABC。A、B都是声明 channel;C 读取 channel;写 channel 是必须带上值,所以 D 错误。
23 下面这段代码输出什么?
type person struct {
name string
}
func main() {
var m map[person]int
p := person{"mike"}
fmt.Println(m[p])
}
A.0
B.1
C.Compilation error
参考答案及解析:A。打印一个 map 中不存在的值时,返回元素类型的零值。这个例子中,m 的类型是 map[person]int,因为 m 中不存在 p,所以打印 int 类型的零值,即 0。
24 下面这段代码输出什么?
func main() {
a := 5
b := 8.1
fmt.Println(a + b)
}
A.13.1
B.13
C.compilation error
参考答案及解析:C。a 的类型是 int,b 的类型是 float,两个不同类型的数值不能相加,编译报错。
25 下面这段代码输出什么?
package main
import (
"fmt"
)
func main() {
a := [5]int{1, 2, 3, 4, 5}
t := a[3:4:4]
fmt.Println(t[0])
}
A.3
B.4
C.compilation error
参考答案及解析:B。知识点:操作符 [i,j]。基于数组(切片)可以使用操作符 [i,j] 创建新的切片,从索引 i,到索引 j 结束,截取已有数组(切片)的任意部分,返回新的切片,新切片的值包含原数组(切片)的 i 索引的值,但是不包含 j 索引的值。i、j 都是可选的,i 如果省略,默认是 0,j 如果省略,默认是原数组(切片)的长度。i、j 都不能超过这个长度值。
假如底层数组的大小为 k,截取之后获得的切片的长度和容量的计算方法:长度:j-i,容量:k-i。
截取操作符还可以有第三个参数,形如 [i,j,k],第三个参数 k 用来限制新切片的容量,但不能超过原数组(切片)的底层数组大小。截取获得的切片的长度和容量分别是:j-i、k-i。
所以例子中,切片 t 为 [4],长度和容量都是 1。
26 下面这段代码输出什么?
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。Go 中的数组是值类型,可比较,另外一方面,数组的长度也是数组类型的组成部分,所以 a 和 b 是不同的类型,是不能比较的,所以编译错误。
27 关于 cap() 函数的适用类型,下面说法正确的是()
A. array
B. slice
C. map
D. channel
参考答案及解析:ABD。知识点:cap(),cap() 函数不适用 map。
28 下面这段代码输出什么?
func main() {
var i interface{}
if i == nil {
fmt.Println("nil")
return
}
fmt.Println("not nil")
}
A. nil
B. not nil
C. compilation error
参考答案及解析:A。当且仅当接口的动态值和动态类型都为 nil 时,接口类型值才为 nil。
29 下面这段代码输出什么?
func main() {
s := make(map[string]int)
delete(s, "h")
fmt.Println(s["h"])
}
A. runtime panic
B. 0
C. compilation error
参考答案及解析:B。删除 map 不存在的键值对时,不会报错,相当于没有任何作用;获取不存在的减值对时,返回值类型对应的零值,所以返回 0。
30 下面这段代码输出什么?
type People struct{}
func (p *People) ShowA() {
fmt.Println("showA")
p.ShowB()
}
func (p *People) ShowB() {
fmt.Println("showB")
}
type Teacher struct {
People
}
func (t *Teacher) ShowB() {
fmt.Println("teacher showB")
}
func main() {
t := Teacher{}
t.ShowB()
}
参考答案及解析:teacher showB。知识点:结构体嵌套。在嵌套结构体中,People 称为内部类型,Teacher 称为外部类型;通过嵌套,内部类型的属性、方法,可以为外部类型所有,就好像是外部类型自己的一样。此外,外部类型还可以定义自己的属性和方法,甚至可以定义与内部相同的方法,这样内部类型的方法就会被“屏蔽”。这个例子中的 ShowB() 就是同名方法。
31 下面这段代码输出什么?
func hello(i int) {
fmt.Println(i)
}
func main() {
i := 5
defer hello(i)
i = i + 10
}
参考答案及解析:5。这个例子中,hello() 函数的参数在执行 defer 语句的时候会保存一份副本,在实际调用 hello() 函数时用,所以是 5.
32 下面这段代码输出什么?
type People struct{}
func (p *People) ShowA() {
fmt.Println("showA")
p.ShowB()
}
func (p *People) ShowB() {
fmt.Println("showB")
}
type Teacher struct {
People
}
func (t *Teacher) ShowB() {
fmt.Println("teacher showB")
}
func main() {
t := Teacher{}
t.ShowA()
}
参考答案及解析:
showA
showB
知识点:结构体嵌套。这道题可以结合上面 的题一起看,Teacher 没有自己 ShowA(),所以调用内部类型 People 的同名方法,需要注意的是第 5 行代码调用的是 People 自己的 ShowB 方法。
33 下面代码输出什么?
func main() {
str := "hello"
str[0] = 'x'
fmt.Println(str)
}
A. hello
B. xello
C. compilation error
参考代码及解析:C。知识点:常量,Go 语言中的字符串是只读的。如果要修改,需要转换成字节切片去操作,然后再转成字符串。
34 下面代码输出什么?
func incr(p *int) int {
*p++
return *p
}
func main() {
p :=1
incr(&p)
fmt.Println(p)
}
A. 1
B. 2
C. 3
参考答案及解析:B。知识点:指针,incr() 函数里的 p 是 *int
类型的指针,指向的是 main() 函数的变量 p 的地址。第 2 行代码是将该地址的值执行一个自增操作,incr() 返回自增后的结果。
35 对 add() 函数调用正确的是()
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。知识点:可变函数。
36 下面这段代码输出什么?
type A interface {
ShowA() int
}
type B interface {
ShowB() int
}
type Work struct {
i int
}
func (w Work) ShowA() int {
return w.i + 10
}
func (w Work) ShowB() int {
return w.i + 20
}
func main() {
c := Work{3}
var a A = c
var b B = c
fmt.Println(a.ShowA())
fmt.Println(b.ShowB())
}
参考答案及解析:13 23。知识点:接口。一种类型实现多个接口,结构体 Work 分别实现了接口 A、B,所以接口变量 a、b 调用各自的方法 ShowA() 和 ShowB(),输出 13、23。
37 .切片 a、b、c 的长度和容量分别是多少?
func main() {
s := [3]int{1, 2, 3}
a := s[:0]
b := s[:2]
c := s[1:2:cap(s)]
}
参考答案及解析:a、b、c 的长度和容量分别是 0 3、2 3、1 2。知识点:数组或切片的截取操作。截取操作有带 2 个或者 3 个参数,形如:[i:j] 和 [ik],假设截取对象的底层数组长度为 l。在操作符 [i:j] 中,如果 i 省略,默认 0,如果 j 省略,默认底层数组的长度,截取得到的切片长度和容量计算方法是 j-i、l-i。操作符 [i
k],k 主要是用来限制切片的容量,但是不能大于数组的长度 l,截取得到的切片长度和容量计算方法是 j-i、k-i。
38 下面代码中 A B 两处应该怎么修改才能顺利编译?
func main() {
var m map[string]int //A
m["a"] = 1
if v := m["b"]; v != nil { //B
fmt.Println(v)
}
}
参考答案及解析:
func main() {
m := make(map[string]int)
m["a"] = 1
if v,ok := m["b"]; ok {
fmt.Println(v)
}
}
在 A 处只声明了map m ,并没有分配内存空间,不能直接赋值,需要使用 make(),都提倡使用 make() 或者字面量的方式直接初始化 map。
B 处,v,k := m[“b”] 当 key 为 b 的元素不存在的时候,v 会返回值类型对应的零值,k 返回 false。
39 下面代码输出什么?
type A interface {
ShowA() int
}
type B interface {
ShowB() int
}
type Work struct {
i int
}
func (w Work) ShowA() int {
return w.i + 10
}
func (w Work) ShowB() int {
return w.i + 20
}
func main() {
c := Work{3}
var a A = c
var b B = c
fmt.Println(a.ShowB())
fmt.Println(b.ShowA())
}
A. 23 13
B. compilation error
参考答案及解析:B。知识点:接口的静态类型。a、b 具有相同的动态类型和动态值,分别是结构体 work 和 {3};a 的静态类型是 A,b 的静态类型是 B,接口 A 不包括方法 ShowB(),接口 B 也不包括方法 ShowA(),编译报错。看下编译错误:
a.ShowB undefined (type A has no field or method ShowB)
b.ShowA undefined (type B has no field or method ShowA)
40 下面代码输出什么?
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())
}
A. 1 1
B. 0 1
C. 1 0
D. 0 0
参考答案及解析:B。知识点:defer、返回值。注意一下,increaseA() 的返回参数是匿名,increaseB() 是具名。
41 下面代码输出什么?
type A interface {
ShowA() int
}
type B interface {
ShowB() int
}
type Work struct {
i int
}
func (w Work) ShowA() int {
return w.i + 10
}
func (w Work) ShowB() int {
return w.i + 20
}
func main() {
var a A = Work{3}
s := a.(Work) //把a 转换成Work 类型
fmt.Println(s.ShowA())
fmt.Println(s.ShowB())
}
A. 13 23
B. compilation error
参考答案及解析:A。知识点:类型断言。
42 下面代码段输出什么?
type Person struct {
age int
}
func main() {
person := &Person{28}
// 1.
defer fmt.Println(person.age)
// 2.
defer func(p *Person) {
fmt.Println(p.age)
}(person)
// 3.
defer func() {
fmt.Println(person.age)
}()
person.age = 29
}
参考答案及解析:29 29 28。变量 person 是一个指针变量 。
1.person.age 此时是将 28 当做 defer 函数的参数,会把 28 缓存在栈中,等到最后执行该 defer 语句的时候取出,即输出 28;
2.defer 缓存的是结构体 Person{28} 的地址,最终 Person{28} 的 age 被重新赋值为 29,所以 defer 语句最后执行的时候,依靠缓存的地址取出的 age 便是 29,即输出 29;
3.闭包引用,输出 29;
又由于 defer 的执行顺序为先进后出,即 3 2 1,所以输出 29 29 28。
43 下面代码输出什么?
type Person struct {
age int
}
func main() {
person := &Person{28}
// 1.
defer fmt.Println(person.age)
// 2.
defer func(p *Person) {
fmt.Println(p.age)
}(person)
// 3.
defer func() {
fmt.Println(person.age)
}()
person = &Person{29}
}
参考答案及解析:29 28 28。这道题在上个题目的基础上做了一点点小改动,前一题最后一行代码 person.age = 29 是修改引用对象的成员 age,这题最后一行代码 person = &Person{29} 是修改引用对象本身,来看看有什么区别。
1处.person.age 这一行代码跟之前含义是一样的,此时是将 28 当做 defer 函数的参数,会把 28 缓存在栈中,等到最后执行该 defer 语句的时候取出,即输出 28;
2处.defer 缓存的是结构体 Person{28} 的地址,这个地址指向的结构体没有被改变,最后 defer 语句后面的函数执行的时候取出仍是 28;
3处.闭包引用,person 的值已经被改变,指向结构体 Person{29},所以输出 29.
由于 defer 的执行顺序为先进后出,即 3 2 1,所以输出 29 28 28。
44 下面的两个切片声明中有什么区别?哪个更可取?
A. var a []int
B. a := []int{}
参考答案及解析:A 声明的是 nil 切片;B 声明的是长度和容量都为 0 的空切片。第一种切片声明不会分配内存,优先选择。
45 A、B、C、D 哪些选项有语法错误?
type S struct {
}
func f(x interface{}) {
}
func g(x *interface{}) {
}
func main() {
s := S{}
p := &s
f(s) //A
g(s) //B
f(p) //C
g(p) //D
}
参考答案及解析:BD。函数参数为 interface{} 时可以接收任何类型的参数,包括用户自定义类型等,即使是接收指针类型也用 interface{},而不是使用 *interface{}
。
永远不要使用一个指针指向一个接口类型,因为它已经是一个指针。
46 下面 A、B 两处应该填入什么代码,才能确保顺利打印出结果?
type S struct {
m string
}
func f() *S {
return __ //A
}
func main() {
p := __ //B
fmt.Println(p.m) //print "foo"
}
参考答案及解析:
A. &S{"foo"}
B. *f() 或者 f()
f() 函数返回参数是指针类型,所以可以用 & 取结构体的指针;B 处,如果填 *f()
,则 p 是 S 类型;如果填 f(),则 p 是 *S
类型,不过都可以使用 p.m 取得结构体的成员。
47 下面的代码有几处语法问题,各是什么?
package main
import (
"fmt"
)
func main() {
var x string = nil
if x == nil {
x = "default"
}
fmt.Println(x)
}
参考答案及解析:两个地方有语法问题。golang 的字符串类型是不能赋值 nil 的,也不能跟 nil 比较。
48 return 之后的 defer 语句会执行吗,下面这段代码输出什么?
var a bool = true
func main() {
defer func(){
fmt.Println("1")
}()
if a == true {
fmt.Println("2")
return
}
defer func(){
fmt.Println("3")
}()
}
参考答案及解析:2 1。defer 关键字后面的函数或者方法想要执行必须先注册,return 之后的 defer 是不能注册的, 也就不能执行后面的函数或方法
48 下面这段代码输出什么?为什么?
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]
我们知道,golang 中切片底层的数据结构是数组。当使用 s1[1:] 获得切片 s2,和 s1 共享同一个底层数组,这会导致 s2[1] = 4 语句影响 s1。
而 append 操作会导致底层数组扩容,生成新的数组,因此追加数据后的 s2 不会影响 s1。
但是为什么对 s2 赋值后影响的却是 s1 的第三个元素呢?这是因为切片 s2 是从数组的第二个元素开始,s2 索引为 1 的元素对应的是 s1 索引为 2 的元素。
49 下面选项正确的是?
func main() {
if a := 1; false {
} else if b := 2; false {
} else {
println(a, b)
}
}
A. 1 2
B. compilation error
参考答案及解析:A。知识点:代码块和变量作用域。
50 下面这段代码输出什么?
func main() {
m := map[int]string{0:"zero",1:"one"}
for k,v := range m {
fmt.Println(k,v)
}
}
参考答案及解析:
0 zero
1 one
// 或者
1 one
0 zero
map 的输出是无序的。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: