# go 面试题

# 第一天

# 1. 下面这段代码输出什么?

type Direction int

const (
    North Direction = iota + 1
    East
    South
    West
)

func (d Direction) String() string {
    return [...]string{"North", "East", "South", "West"}[d]
}

func main() {
    fmt.Println(South)
}
参考答案及解析:

  答案:West。
  知识点:iota 的用法、类型的 String() 方法。根据 iota 的用法推断出 South 的值是 3;另外,如果类型定义了 String() 方法,当使用 fmt.Printf()、fmt.Print() 和 fmt.Println() 会自动使用 String() 方法,实现字符串的打印。
  

# 2. 下面代码输出什么?

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 map。 错误原因:对于类似 X = Y的赋值操作,必须知道 X 的地址,才能够将 Y 的值赋给 X,但 go 中的 map 的 value 本身是不可寻址的。 有两个解决办法: 1.使用临时变量

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)
}

2.修改数据结构

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 格式化输出详细信息
}

# 第二天

# 1. 下面的代码有什么问题?

func main() {
    fmt.Println([...]int{1} == [2]int{1})
    fmt.Println([]int{1} == []int{1})
}
参考答案及解析:

  答案:有两处错误。
  1. go 中不同类型是不能比较的,而数组长度是数组类型的一部分,所以 […]int{1} 和 [2]int{1} 是两种不同的类型,不能比较;
  2. 切片是不能比较的;
  

# 2.下面这段代码输出什么?

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。

知识点:变量作用域。问题出在操作符:=,对于使用:=定义的变量,如果新变量与同名已定义的变量不在同一个作用域中,那么 Go 会新定义这个变量。对于本例来说,main() 函数里的 p 是新定义的变量,会遮住全局变量 p,导致执行到bar()时程序,全局变量 p 依然还是 nil,程序随即 Crash。

正确的做法是将 main() 函数修改为:

func main() {
    var err error
    p, err = foo()
    if err != nil {
        fmt.Println(err)
        return
    }
    bar()
    fmt.Println(*p)
}

这道题目引自 Tony Bai 老师的一篇文章,原文讲的很详细,推荐。https://tonybai.com/2015/01/13/a-hole-about-variable-scope-in-golang/

# 第三天

通过让 IPAddr 类型实现 fmt.Stringer 来打印点号分隔的地址。

例如,IPAddr{1, 2, 3, 4} 应当打印为 “1.2.3.4”。

package main

import (
   "fmt"
)

type IPAddr [4]byte


func main() {
   hosts := map[string]IPAddr{
      "loopback":  {127, 0, 0, 1},
      "googleDNS": {8, 8, 8, 8},
   }
   for name, ip := range hosts {
      fmt.Printf("%v: %v\n", name, ip)
   }
}

// TODO: 给 IPAddr 添加一个 "String() string" 方法
func (ip IPAddr) String() string {
   var str string
   for _, i := range ip {
      str = str + fmt.Sprintf("%v.", i)
   }
   return str[:len(str)-1]
}