GO·NOTE

一份 Go 开发工程师的学习笔记

0%

Golang 面试题·卷二

线上测试地址:https://play.studygolang.com/

未解析:第一题、第七题

1、以下代码打印出来什么内容,说出为什么?(20分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"fmt"
)

type People interface {
Show()
}

type Student struct{}

func (stu *Student) Show() {

}

func live() People {
var stu *Student
return stu
}

func main() {
if live() == nil {
fmt.Println("AAAAAAA")
} else {
fmt.Println("BBBBBBB")
}
}
参考答案

结果:

1
BBBBBBB

解析:

interface 的结构为以下两种的其中一个,代码中将 nil的*student 赋值给 People接口类型,其实为:people.Data = nil。但是people不为nil。

1
2
3
4
5
6
7
8
9
type iface struct { 
tab *itab
data unsafe.Pointer
}

type eface struct {
_type *_type
data unsafe.Pointer
}

解决方案:

1、知道类型的情况下,自然是可以使用类型断言后判空。如ai, ok := i.(*int),之后判断ai == nil。

2、不知道是何种类型的指针,就只好借助反射了vi := reflect.ValueOf(i),后使用vi.IsNil()来判断。但如果i里放到不是一个指针,调用IsNil会出异常,则可能要写一个这样的函数来判空

1
2
3
4
5
6
7
func IsNil(i interface{}) bool {
defer func() {
recover()
}()
vi := reflect.ValueOf(i)
return vi.IsNil()
}

但有这样强加一个defer的recover确实不好看,于是借助类型判断变成这样

1
2
3
4
5
6
7
func IsNil(i interface{}) bool {
vi := reflect.ValueOf(i)
if vi.Kind() == reflect.Ptr {
return vi.IsNil()
}
return false
}

参考文章:

https://www.jianshu.com/p/97bfe8104e03
https://www.cnblogs.com/chenqionghe/p/11357013.html
https://blog.csdn.net/lanyang123456/article/details/83715090

2.是否可以编译通过?如果通过,输出什么?(20分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
i := GetValue()

switch i.(type) {
case int:
println("int")
case string:
println("string")
case interface{}:
println("interface")
default:
println("unknown")
}

}

func GetValue() int {
return 1
}
参考答案

结果:

1
cannot type switch on non-interface value i (type *string)

解析:

两点规则:

1、golang中x.(type)语句,只能在 switch case中使用。类似:

1
2
3
4
5
6
7
8
9
10
switch i.(type) {
case int:
println("int")
case string:
println("string")
case interface{}:
println("interface")
default:
println("unknown")
}

2、x.(type)必须为 interface 类型,否则会编译报错,因为如果不是 interface,也没有.(type)的必要。

参考文档:

https://my.oschina.net/robin3d/blog/1862756

3.下面函数有什么问题?(20分)

1
2
3
func funcMui(x,y int)(sum int,error){
return x+y,nil
}
参考答案

结果:

1
Function has both named and unnamed return parameters '(sum int,error)'

解析:

函数的返回值,要么都被定义名称,要不都不定义。其中 int 类型定义了,error却没有定义

4.是否可以编译通过?如果通过,输出什么?(20分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

func main() {

println(DeferFunc1(1))
println(DeferFunc2(1))
println(DeferFunc3(1))
}

func DeferFunc1(i int) (t int) {
t = i
defer func() {
t += 3
}()
return t
}

func DeferFunc2(i int) int {
t := i
defer func() {
t += 3
}()
return t
}

func DeferFunc3(i int) (t int) {
defer func() {
t += i
}()
return 2
}}

5.是否可以编译通过?如果通过,输出什么?(20分)

1
2
3
4
5
func main() {
list := new([]int)
list = append(list, 1)
fmt.Println(list)
}
参考答案

结果:

1
first argument to append must be slice; have *[]int

解析:

题目中 list 为数组指针,而非 slice,不具有 append 方法

new(T) 为每个新的类型T分配一片内存,初始化为 0 并且返回类型为*T的内存地址:这种方法 返回一个指向类型为 T,值为 0 的地址的指针,它适用于值类型如数组和结构体;它相当于 &T{}。

make(T) 返回一个类型为 T 的初始值,它只适用于3种内建的引用类型:slice、map 和 channel。

换言之,new 函数分配内存,make 函数初始化;

6.是否可以编译通过?如果通过,输出什么?(20分)

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

func main() {
s1 := []int{1, 2, 3}
s2 := []int{4, 5}
s1 = append(s1, s2)
fmt.Println(s1)
}
参考答案

结果:

1
.\test.go:8:13: cannot use s2 (type []int) as type int in append

解析:

1
func append(slice []Type, elems ...Type) []Type

apend 函数接受的是可变长参数。题目中s2为[]int类型,非可变长参数。s2…为可变长。
... 将 s2 变为可变长参数。

扩展:

的用法主要有以下两种:

第一个用法主要是用于函数有多个不定参数的情况,可以接受多个不确定数量的参数。
第二个用法是slice可以被打散进行传递。

7.是否可以编译通过?如果通过,输出什么?(20分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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")
}
}
参考答案

结果:

1
.\test.go:29:9: invalid operation: sm1 == sm2 (struct containing map[string]string cannot be compared)

解析:

暂无解析

8.是否可以编译通过?如果通过,输出什么?(20分)

1
2
3
4
5
6
7
8
9
10
11
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)
}
参考答案

同第一题

9.是否可以编译通过?如果通过,输出什么?(20分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func GetValue(m map[int]string, id int) (string, bool) {
if _, exist := m[id]; exist {
return "存在数据", true
}
return nil, false
}
func main() {
intmap := map[int]string{
1:"a",
2:"bb",
3:"ccc",
}

v,err := GetValue(intmap,3)
fmt.Println(v,err)
}
参考答案

结果:

1
cannot use nil as type string in return argument

解析:

string 不可定义为nil,它的零值为””,可以定义为 nil 的类型只有以下几种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import (
"fmt"
)

type Student struct {}

func main() {

var sli []int = nil
var c chan int = nil
var m map[string]string = nil
var stu *Student = nil
var i interface{} = nil

fmt.Println(sli,c,m,stu,i)
}
1
[] <nil> map[] <nil> <nil>

10.是否可以编译通过?如果通过,输出什么?(20分)

1
2
3
4
5
6
7
8
9
10
11
const (
x = iota
y
z = "zz"
k
p = iota
)

func main() {
fmt.Println(x,y,z,k,p)
}
参考答案

结果:

1
0 1 zz zz 4

解析:

iota是golang语言的常量计数器,只能在常量的表达式中使用。

iota在const关键字出现时将被重置为0,const中每新增一行常量声明将使iota计数一次。

使用方法:

  1. iota只能在常量的表达式中使用
1
2
fmt.Println(iota)
编译错误: undefined: iota
  1. 每次 const 出现时,都会让 iota 初始化为0
1
2
3
4
5
const a = iota    // a=0
const (
    b = iota     //b=0
    c            //c=1
)
  1. 自定义类型,自增长常量经常包含一个自定义枚举类型,允许你依靠编译器完成自增设置
1
2
3
4
5
6
7
type status int
const (
    unknow status = iota    // 0
    queued                  // 1
    queued                  // 2
    queued                  // 3
)
  1. 可跳过的值
1
2
3
4
5
6
7
8
9
type status int
const (
    unknow status = iota     // 0
    queued                   // 1
    queued                   // 2
    _
    _
    queued                   // 5
)
  1. 定义在一行的情况
1
2
3
4
5
6
7
8
9
10
11
12
const (
    a, b = iota + 1, iota + 2
    c, d
    e, f
)

a //1
b //2
c //2
d //3
e //3
f //4

6、中间插队且下个变量没有赋值时,下个变量会和上个变量一样。iota自动加1

1
2
3
4
5
6
7
8
9
const (
x = iota
y
z = "zz"
k
p = iota
)

0 1 zz zz 4