记录一些考点综合的面试题。
(为了避免提示,题目只有序号没有标题)
1
下面的代码会输出什么,并说明原因
func main() {
runtime.GOMAXPROCS(1)
wg := sync.WaitGroup{}
wg.Add(20)
for i := 0; i < 10; i++ {
// 下面这种方式,可以定义同名新变量,避免闭包引用变量问题
// i := i
go func() {
fmt.Println("A: ", i)
wg.Done()
}()
}
for i:= 0; i < 10; i++ {
gofunc(i int) {
fmt.Println("B: ", i)
wg.Done()
}(i)
}
wg.Wait()
}
- go 执行的随机性
- 闭包中外部变量引用
- 可以用重定义变量的方式,避免闭包导致同一变量引用
2
下面的代码会输出什么,并说明原因
const s = "Go101.org"
var a byte = 1 << len(s) / 128
var b byte = 1 << len(s[:]) / 128
func main() {
println(len(s), len(s[:]))
println(a, b)
}
- 未确定类型常量
- 常量在编译期的运算过程
- 移位溢出过程
3
- 如下两段代码会输出什么?
struct{}的指针能否比较?为什么?
// 代码 1
type People struct {}
func main() {
a := &People{}
b := &People{}
fmt.Println(a == b)
}
// 代码 2
type People struct {}
func main() {
a := &People{}
b := &People{}
fmt.Printf("%p %p\n", a, b)
fmt.Println(a == b)
}
-
答案:
- 代码 1 会输出:
false - 代码 2 会输出:
true
- 代码 1 会输出:
- 思路:
- 按照官方说法,
struct{}(空对象)会统一在系统中占用同一资源,所以节省容量。上面代码 2 是符合的 - 由于 Go 的逃逸检测,
fmt.Printf(...)函数使用了反射,所以a、b两个对象被检测为逃逸,并且指向了同一个全局地址zerobase - 所有 0 字节容量的堆对象都会指向
zerobase这个地址,所以相等,是正确的结果 - 由于 Go 编译器的优化,代码 1 的对象被认为没有逃逸在栈中,两个栈中对象的地址一定不同,所以编译器直接填写了
false值- 在编译时添加
-gcflags="-N -l"参数,就可以不进行编译优化,代码 1 就会打印true了。当然,这种方法是不可取的
- 在编译时添加
- 按照官方说法,
- 实际使用:
- 实际中不会很多,在不关心 struct 成员变量只关心接口函数的情况下才会使用
struct{}指针 - 使用中不能依赖”空对象指针是否相等”作为逻辑条件,类似 map 的顺序不能被依赖一样
- 实际中不会很多,在不关心 struct 成员变量只关心接口函数的情况下才会使用
- 其他:
println函数不会造成逃逸,但是这个函数官方本身不再支持,也不该这么依赖
4
- 下面代码会输出什么?
func main() {
var e error
e = GetErr()
log.Println(e, e == nil)
}
type MyErr struct {
Msg string
}
func (m *MyErr) Error() string {
return "test"
}
func GetErr() *MyErr {
return nil
}
-
错误答案:
nil true -
真实情况的输出:
nil false -
为什么会输出
false?error是一个 interface,底层可能有两种数据格式:runtime.eface表示不包含任何方法的空接口runtime.iface表示包含方法的接口
type eface struct {
_type *_type
data unsafe.Pointer
}
type iface struct {
tab *itab
data unsafe.Pointer
}
-
可以看到只有
type和pointer相同的情况下,runtime.eface才符合==。 由于上面GetErr()返回的类型是*MyErr,所以被赋值的e的类型被设为eface{*MyErr,nil}; 而外面的nilinterface 的类型是eface{nilType,nil},所以(e == nil) == false - 应对方案:
- 通常我们函数返回类型时要用 error 这种 interface 类型,这样返回的
nil就不会带类型,就能匹配== - 如果返回的类型不是 interface,那调用者要注意和 nil 的比较情况
- 通常我们函数返回类型时要用 error 这种 interface 类型,这样返回的
- 比如下面这种方式就是正确的:
func GetErr() error {
return nil
}
5
- 下面代码会如何运行?输出什么?
func main() {
s := []int{1,2,3,4,5}
for _, v:=range s {
s =append(s, v)
fmt.Printf("len(s)=%v\n",len(s))
}
}
range是 Go 的语法糖,可以理解为一个只触发一次的函数,调用时就确定了 len- sli、map 是相同逻辑
6
- 下面代码会输出吗?
func main() {
runtime.GOMAXPROCS(1)
go func() {
fmt.Println("sub goroutine")
}()
for {}
}
- 在 1.14 版(包括)之后,可以成功打印”sub goroutine”;在 1.14 版之前会锁死;