Golang精编面试题 -- 程序输出

本文是Golang的面试题精选,涵盖了defer执行顺序、并发安全、接口与方法集、类型与赋值等多个核心知识点。通过解析各种代码示例,深入理解Go语言的关键特性和潜在陷阱。
摘要由CSDN通过智能技术生成

1 写出下面代码输出内容

package main

import "fmt"

func main() {
	defer_call()
}

func defer_call() {
	defer func() { fmt.Println("打印前") }()
	defer func() { fmt.Println("打印中") }()
	defer func() { fmt.Println("打印后") }()

	//效果等同于return
	panic("触发异常")
}

参考答案:
打印后
打印中
打印前
panic: 触发异常

[试题解析]
考点:defer执行顺序
defer 是后进先出。
panic 需要等defer 结束后才会向上传递。 出现panic恐慌时候,会先按照defer的后入先出的顺序执行,最后才会执行panic。

2 以下代码有什么问题,说明原因

package main

import "fmt"

type student struct {
	Name string
	Age  int
}

func main() {
	m := make(map[string]*student)

	stus := []student{
		{Name: "zhou", Age: 24},
		{Name: "li", Age: 23},
		{Name: "wang", Age: 22},
	}

	for _, stu := range stus {
		//fmt.Println(stu)
		m[stu.Name] = &stu
	}

	fmt.Println(m)
}

[试题解析]
考点:foreach
这样的写法初学者经常会遇到的,很危险! 与Java的foreach一样,都是使用副本的方式。所以m[stu.Name]=&stu实际上一致指向同一个指针, 最终该指针的值为遍历的最后一个struct的值拷贝。 就像想修改切片元素的属性:

for _, stu := rangestus {
    stu.Age = stu.Age+10}

也是不可行的。 大家可以试试打印出来:

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
    }    
     fork,v:=range m{        
      println(k,"=>",v.Name)
    }    
      // 正确
    for i:=0;i<len(stus);i++ {
       m[stus[i].Name] = &stus[i]
    }    
     fork,v:=range m{        
       println(k,"=>",v.Name)
    }
}

stu作为一个变量只声明了一次,因此其地址是始终不会发生变化的,最终其内部的值为最后一个Student对象的值。

3 下面的代码会输出什么,并说明原因

package main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() {
	runtime.GOMAXPROCS(1)
	wg := sync.WaitGroup{}

	wg.Add(10)

	for i := 0; i < 5; i++ {
		go func() {
			fmt.Println("A: ", i)
			wg.Done()
		}()
	}

	for i := 0; i < 5; i++ {
		go func(i int) {
			fmt.Println("B: ", i)
			wg.Done()
		}(i)
	}

	wg.Wait()
}

[试题解析]
考点:go执行的随机性和闭包
对于第一个循环,当goroutine中的代码得以执行时,主协程中的i早已完成整个循环,因此其值为5;第二个循环由于将01234分别传入了匿名函数func()的闭包中,因此即使go func是延时执行的,对于第一时间传入和锁死的局部变量i是没有影响的;

谁也不知道执行后打印的顺序是什么样的,所以只能说是随机数字。 但是A:均为输出10,B:从0~9输出(顺序不定)。 第一个go func中i是外部for的一个变量,地址不变化。遍历完成后,最终i=10。 故go func执行时,i的值始终是10。

第二个go func中i是函数参数,与外部for中的i完全是两个变量。 尾部(i)将发生值拷贝,go func内部指向值拷贝地址。

4 下面代码会输出什么?

package main

import "fmt"

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("teachershowB")
}

func main() {
	t := Teacher{}
	t.ShowA()
}

[试题解析]
考点:go的组合继承
showA方法内的p对象是一个People对象,它是t内置匿名成员People的指针,因此它调用的showB方法也是People的showB方法;

这是Golang的组合模式,可以实现OOP的继承。 被组合的类型People所包含的方法虽然升级成了外部类型Teacher这个组合类型的方法(一定要是匿名字段),但它们的方法(ShowA())调用时接受者并没有发生变化。 此时People类型并不知道自己会被什么类型组合,当然也就无法调用方法时去使用未知的组合者Teacher类型的功能。
showAshowB

5 下面代码会触发异常吗?请详细说明

package main

import (
	"fmt"
	"runtime"
)

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随机性
考虑到select的两个case都不是IO阻塞的,因此两种情形随机发生;
select会随机选择一个可用通用做收发操作。 所以代码是有肯触发异常,也有可能不会。 单个chan如果无缓冲时,将会阻塞。但结合 select可以在多个chan间等待执行。有三点原则:

select 中只要有一个case能return,则立刻执行。
当如果同一时间有多个case均能return则伪随机方式抽取任意一个执行。
如果没有一个case能return则可以执行”default”块。

6 下面代码输出什么?

package main

import "fmt"

func calc(index string, a, b int) int {
	ret := a + b
	fmt.Println(index, a, b, ret)
	return ret
}

func main061() {
	a := 1
	b := 2

	defer calc("1", 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值