61. 下面这段代码输出什么?
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
解析:
知识点:iota 的用法。
iota 是 golang 语言的常量计数器,只能在常量的表达式中使用。
iota 在 const 关键字出现时将被重置为0,const中每新增一行常量声明将使 iota 计数一次。
62. 下面这段代码输出什么?为什么?
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 nilp is not nil
解析:
这道题会不会有点诧异,我们分配给变量 p 的值明明是 nil,然而 p 却不是 nil。记住一点,当且仅当动态值和动态类型都为 nil 时,接口类型值才为 nil。上面的代码,给变量 p 赋值之后,p 的动态值是 nil,但是动态类型却是 *Student,是一个 nil 指针,所以相等条件不成立。
63. 下面这段代码输出什么?
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
解析:
知识点:iota 的用法、类型的 String() 方法。
64. 下面代码输出什么?
type Math struct {
x, y int
}
var m = map[string]Math{
"foo": Math{2, 3},
}
func main() {
m["foo"].x = 4
fmt.Println(m["foo"].x)
}
- A. 4
- B. compilation error
答:B
解析:
cannot assign to struct field m["foo"].x in mapX = YXYX
有两个解决办法:
- 使用临时变量
type Math struct { x, y int } var m = map[string]Math{ "foo": Math{2, 3}, } func main() { tmp := m["foo"] tmp.x = 4 m["foo"] = tmp fmt.Println(m["foo"].x) } - 修改数据结构
type Math struct { x, y int } var m = map[string]*Math{ "foo": &Math{2, 3}, } func main() { m["foo"].x = 4 fmt.Println(m["foo"].x) fmt.Printf("%#v", m["foo"]) // %#v 格式化输出详细信息 }
65. 下面的代码有什么问题?
func main() {
fmt.Println([...]int{1} == [2]int{1})
fmt.Println([]int{1} == []int{1})
}
答:有两处错误
解析:
[…]int{1}[2]int{1}
66. 下面这段代码输出什么?如果编译错误的话,为什么?
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)
}
- A. 5 5
- B. runtime error
答:B
解析:
知识点:变量作用域。
:=:=bar()
正确的做法是将 main() 函数修改为:
func main() {
var err error
p, err = foo()
if err != nil {
fmt.Println(err)
return
}
bar()
fmt.Println(*p)
}
67. 下面这段代码能否正常结束?
func main() {
v := []int{1, 2, 3}
for i := range v {
v = append(v, i)
}
}
答:不会出现死循环,能正常结束
解析:
循环次数在循环开始前就已经确定,循环内改变切片的长度,不影响循环次数。
68. 下面这段代码输出什么?为什么?
func main() {
var m = [...]int{1, 2, 3}
for i, v := range m {
go func() {
fmt.Println(i, v)
}()
}
time.Sleep(time.Second * 3)
}
答:
2 3
2 3
2 3
解析:
for range 使用短变量声明(:=)的形式迭代变量,需要注意的是,变量 i、v 在每次循环体中都会被重用,而不是重新声明。
各个 goroutine 中输出的 i、v 值都是 for range 循环结束后的 i、v 最终值,而不是各个goroutine启动时的i, v值。可以理解为闭包引用,使用的是上下文环境的值。
两种可行的 fix 方法:
- 使用函数传递
for i, v := range m { go func(i,v int) { fmt.Println(i, v) }(i,v) } - 使用临时变量保留当前值
for i, v := range m { i := i // 这里的 := 会重新声明变量,而不是重用 v := v go func() { fmt.Println(i, v) }() }
69. 下面这段代码输出什么?
func f(n int) (r int) {
defer func() {
r += n
recover()
}()
var f func()
defer f()
f = func() {
r += 2
}
return n + 1
}
func main() {
fmt.Println(f(3))
}
答:7
解析:
r = n +1
70. 下面这段代码输出什么?
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]
解析:
range 表达式是副本参与循环,就是说例子中参与循环的是 a 的副本,而不是真正的 a。就这个例子来说,假设 b 是 a 的副本,则 range 循环代码是这样的:
for i, v := range b {
if i == 0 {
a[1] = 12
a[2] = 13
}
r[i] = v
}
因此无论 a 被如何修改,其副本 b 依旧保持原值,并且参与循环的是 b,因此 v 从 b 中取出的仍旧是 a 的原值,而非修改后的值。
如果想要 r 和 a 一样输出,修复办法:
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 12 13 4 5]
a = [1 12 13 4 5]
修复代码中,使用 *[5]int 作为 range 表达式,其副本依旧是一个指向原数组 a 的指针,因此后续所有循环中均是 &a 指向的原数组亲自参与的,因此 v 能从 &a 指向的原数组中取出 a 修改后的值。
71. 下面这段代码输出什么?
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]
解析:
知识点:可变函数、append()操作。
...[i,j]
72. 下面这段代码输出什么?
func main() {
var a = []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 12 13 4 5]
a = [1 12 13 4 5]
解析:
切片在 go 的内部结构有一个指向底层数组的指针,当 range 表达式发生复制时,副本的指针依旧指向原底层数组,所以对切片的修改都会反应到底层数组上,所以通过 v 可以获得修改后的数组元素。
73. 下面这段代码输出结果正确正确吗?
type Foo struct {
bar string
}
func main() {
s1 := []Foo{
{"A"},
{"B"},
{"C"},
}
s2 := make([]*Foo, len(s1))
for i, value := range s1 {
s2[i] = &value
}
fmt.Println(s1[0], s1[1], s1[2])
fmt.Println(s2[0], s2[1], s2[2])
}
输出:
{A} {B} {C}
&{A} &{B} &{C}
答:s2 的输出结果错误
解析:
&{C} &{C} &{C}
可行的解决办法如下:
for i := range s1 {
s2[i] = &s1[i]
}
74. 下面代码里的 counter 的输出值?
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)
}
- A. 2
- B. 3
- C. 2 或 3
答:C
解析:
for range map 是无序的,如果第一次循环到 A,则输出 3;否则输出 2。
75. 关于协程,下面说法正确是()
- A. 协程和线程都可以实现程序的并发执行;
- B. 线程比协程更轻量级;
- C. 协程不存在死锁问题;
- D. 通过 channel 来进行协程间的通信;
答:A D
76. 关于循环语句,下面说法正确的有()
- A. 循环语句既支持 for 关键字,也支持 while 和 do-while;
- B. 关键字 for 的基本使用方法与 C/C++ 中没有任何差异;
- C. for 循环支持 continue 和 break 来控制循环,但是它提供了一个更高级的 break,可以选择中断哪一个循环;
- D. for 循环不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量;
答:C D
77. 下面代码输出正确的是?
func main() {
i := 1
s := []string{"A", "B", "C"}
i, s[i-1] = 2, "Z"
fmt.Printf("s: %v \n", s)
}
- A. s: [Z,B,C]
- B. s: [A,Z,C]
答:A
解析:
知识点:多重赋值。
多重赋值分为两个步骤,有先后顺序:
- 计算等号左边的索引表达式和取址表达式,接着计算等号右边的表达式;
- 赋值;
i, s[0] = 2, "Z"
78. 关于类型转化,下面选项正确的是?
A.
type MyInt int
var i int = 1
var j MyInt = i
B.
type MyInt int
var i int = 1
var j MyInt = (MyInt)i
C.
type MyInt int
var i int = 1
var j MyInt = MyInt(i)
D.
type MyInt int
var i int = 1
var j MyInt = i.(MyInt)
答:C
解析:
知识点:强制类型转化
79. 关于switch语句,下面说法正确的有?
- A. 条件表达式必须为常量或者整数;
- B. 单个case中,可以出现多个结果选项;
- C. 需要用break来明确退出一个case;
- D. 只有在case中明确添加fallthrough关键字,才会继续执行紧跟的下一个case;
答:B D
80. 如果 Add() 函数的调用代码为:
func main() {
var a Integer = 1
var b Integer = 2
var i interface{} = &a
sum := i.(*Integer).Add(b)
fmt.Println(sum)
}
则Add函数定义正确的是()
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
}
答:A C
解析:
知识点:类型断言、方法集。
81. 关于 bool 变量 b 的赋值,下面错误的用法是?
- A. b = true
- B. b = 1
- C. b = bool(1)
- D. b = (1 == 2)
答:B C
82. 关于变量的自增和自减操作,下面语句正确的是?
A.
i := 1
i++
B.
i := 1
j = i++
C.
i := 1
++i
D.
i := 1
i--
答:A D
解析:
知识点:自增自减操作。
i++ 和 i-- 在 Go 语言中是语句,不是表达式,因此不能赋值给另外的变量。此外没有 ++i 和 --i。
83. 关于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{}
答:A C D
84. 关于函数声明,下面语法正确的是?
- A. func f(a, b int) (value int, err error)
- B. func f(a int, b int) (value int, err error)
- C. func f(a, b int) (value int, error)
- D. func f(a int, b int) (int, int, error)
答:A B D
85. 关于整型切片的初始化,下面正确的是?
- A. s := make([]int)
- B. s := make([]int, 0)
- C. s := make([]int, 5, 10)
- D. s := []int{1, 2, 3, 4, 5}
答:B C D
86. 下面代码会触发异常吗?请说明。
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
87. 关于channel的特性,下面说法正确的是?
- A. 给一个 nil channel 发送数据,造成永远阻塞
- B. 从一个 nil channel 接收数据,造成永远阻塞
- C. 给一个已经关闭的 channel 发送数据,引起 panic
- D. 从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值
答:A B C D
88. 下面代码有什么问题?
const i = 100
var j = 123
func main() {
fmt.Println(&j, j)
fmt.Println(&i, i)
}
答:编译报错
解析:
cannot take the address of i
89. 下面代码能否编译通过?如果通过,输出什么?
func GetValue(m map[int]string, id int) (string, bool) {
if _, exist := m[id]; exist {
return "exist", true
}
return nil, false
}
func main() {
intmap := map[int]string{
1: "a",
2: "b",
3: "c",
}
v, err := GetValue(intmap, 3)
fmt.Println(v, err)
}
答:不能通过编译
解析:
cannot use nil as type string in return argument
90. 关于异常的触发,下面说法正确的是?
- A. 空指针解析;
- B. 下标越界;
- C. 除数为0;
- D. 调用panic函数;
答:A B C D