Skip to content

Go 基础类面试题,围绕 Slice / Map / Channel / goroutine 四大类型的常见疑问。

开篇碎碎念 ​

整理自 Go-Interview 仓库的 Go基础 模块,目的是把网上常见的 Go 基础面试题按主题归类,方便复习和检索。每道题都尽量保留答案原文,并在题号前加上分类前缀(如「变量与作用域」「Slice 底层」「Map 原理」),便于查漏补缺。

适合人群:刚学完 Go 基础语法、准备找 Go 后端岗位面试的同学。

go基础类 ​

1. go优势 ​

markdown
* 天生支持并发,性能高
* 单一的标准代码格式,比其它语言更具可读性
* 自动垃圾收集比java和python更有效,因为它与程序同时执行

2. go数据类型 ​

go
int string float bool array slice map channel pointer struct interface method

3. go程序中的包是什么 ​

go
* 项目中包含go源文件以及其它包的目录,源文件中的函数、变量、类型都存储在该包中
* 每个源文件都属于一个包,该包在文件顶部使用 package packageName 声明
* 我们在源文件中需要导入第三方包时需要使用 import packageName

4. go支持什么形式的类型转换?将整数转换为浮点数 ​

css
* go支持显示类型转换,以满足严格的类型要
* a := 15
* b := float64(a)
* fmt.Println(b, reflect.TypeOf(b))

5. 什么是 goroutine,你如何停止它? ​

csharp
* goroutine是协程/轻量级线程/用户态线程,不同于传统的内核态线程
* 占用资源特别少,创建和销毁只在用户态执行不会到内核态,节省时间
* 创建goroutine需要使用go关键字
* 可以向goroutine发送一个信号通道来停止它,goroutine内部需要检查信号通道
例子:
```
func main() {
    var wg sync.WaitGroup // 等待组进行多个任务的同步,可以保证并发环境中完成指定数量的任务,每个sync.WaitGroup值在内部维护着一个计数,此计数的初始默认值为0
    var exit = make(chan bool)
    wg.Add(1) // 等待组的计数器+1
    go func() {
        for {
            select {
            case = utf8.RuneCountInString(str){
				wg.Done() // 等待组的计数器-1
				return
			}
			chNum  0xc000086060
	wg.Add(1) // 等待组的计数器+1
	go func() {
		//ch = 80:
			fmt.Println("优秀", a)
			fallthrough // 强制执行下一个case
		case a >= 60:
			fmt.Println("及格", a)
			fallthrough
		default:
			fmt.Println("不及格", a)
		}
	}
}
```

### 27. defer的常用场景

- defer语句经常被**用于处理成对的操作打开/关闭,链接/断开连接,加锁/释放锁**
- 通过defer机制,不论函数逻辑多复杂,都**能保证在任何执行路径下,资源被释放**
- 释放资源的defer语句应该直接跟在请求资源处理错误之后
- 注意:defer一定要放在请求资源处理错误之后

### 28. go中slice的底层实现

1. 切片是基于数组实现的,它的底层是数组,它本身非常小,它可以理解为对底层数组的抽闲
2. 因为基于数组实现,所以它的底层内存是连续分配的,效率非常高,还可以通过索引获取数据
3. 切片本身并不是动态数组或数组指针,它内部实现的数据结构体通过指针引用底层数组
4. 设定相关属性将读写操作限定在指定的区域内,切片本身是一个只读对象,其工作机制类似于数组指针的一种封装
5. 切片对象非常小,因为它只有三个字段的数据结构:指向底层数组的指针、切片的长度、切片的容量

### 29. go中slice的扩容机制,有什么注意点?

1. 首先判断,如果新申请的容量大于2倍的旧容量,最终容量就是新申请的容量
2. 否则判断,如果旧切片的长度小于1024,最终容量就是旧容量的两倍
3. 否则判断,如果旧切片的长度大于等于1024,则最终容量从旧容量开始循环增加原来的1/4,直到最终容量大于新申请的容量
4. 如果最终容量计算值溢出,则最终容量就是新申请的容量

### 30. 扩容前后的slice是否相同?

- 情况一:
  1. **原来数组还有容量可以扩容(实际容量没有填充完)**,这种情况下,扩容之后的切片还是指向原来的数组
  2. 对一个切片的操作可能影响多个指针指向相同地址的切片
- 情况二:
  1. 原来数组的容量已经达到了最大值,在扩容,go默认会先开辟一块内存区域,把原来的值拷贝过来
  2. 然后再执行append操作,这种情况丝毫不影响原数组
- 注意:要复制一个slice最好使用copy函数

### 31. go中的参数传递、引用传递

1. go语言中的**所有的传参都是值传递(传值),都是一个副本,一个拷贝**,
2. 因为拷贝的内容有时候是非引用类型(int, string, struct)等,这样在函数中就无法修改原内容数据
3. 有的是引用类型(指针、slice、map、chan),这样就可以修改原内容数据

- go中的引用类型包含slice、map、chan,它们有复杂的内部结构,除了申请内存外,还需要初始化相关属性
- 内置函数new计算类型大小,为其分配零值内存,返回指针。
- 而make会被编译器翻译成具体的创建函数,由其分配内存并初始化成员结构,返回对象而非指针

### 32. 哈希概念讲解

1. 哈希表又称为**散列表,由一个直接寻址表和一个哈希函数组成**

2. 由于哈希表的大小是有限的而要存储的数值是无限的,因此对于任何哈希函数,都会出现两个不同元素映射到相同位置的情况,这种情况叫做哈希冲突

4. 通过**拉链法解决哈希冲突**:
   \* 哈希表每个位置都连接一个链表,当冲突发生是,冲突的元素将会被加到该位置链表的最后

4. 哈希表的查找速度起决定性作用的就是**哈希函数**: **除法哈希发、乘法哈希法、全域哈希法**

5. 哈希表的应用?

   字典与集合都是通过哈希表来实现的

   md5曾经是密码学中常用的哈希函数,可以把任意长度的数据映射为128位的哈希值

### 33. go中的map底层实现

1. go中map的底层实现就是一个散列表,因此实现map的过程实际上就是实现散列表的过程
2. 在这个散列表中,**主要出现的结构体由两个,一个是hmap、一个是bmap**
3. go中也有一个哈希函数,用来对map中的键生成哈希值
4. hash结果的低位用于把k/v放到bmap数组中的哪个bmap中
5. 高位用于key的快速预览,快速试错

### 34. go中的map如何扩容

1. 翻倍扩容:如果map中的键值对个数/桶的个数>6.5,就会引发翻倍扩容
2. 等量扩容:当B=2的B次方就会引发等量扩容
3. 当B>15时,如果溢出桶的个数>=2的15次方时就会引发等量扩容

### 35. go中map的查找

1. go中的map采用的是哈希查找表,由哈希函数通过key和哈希因此计算出哈希值,
2. 根据hamp中的B来确定放到哪个桶中,如果B=5,那么就根据哈希值的后5位确定放到哪个桶中
3. 在用哈希值的高8位确定桶中的位置,如果当前的bmap中未找到,则去对应的overflow bucket中查找
4. 如果当前map处于数据搬迁状态,则优先从oldbuckets中查找

### 36. 介绍一下channel

1. go中不要通过共享内存来通信,而要**通过通信实现共享内存**
2. go中的**csp并发模型,中文名通信顺序进程,就是通过goroutine和channel实现的**
3. **channel收发遵循先进先出,分为有缓冲通道(异步通道),无缓冲通道(同步通道)**

### 37. go中channel的特性

1. 给一个nil的channel发送数据,会造成永久阻塞
2. 从一个nil的channel接收数据,会造成永久阻塞
3. 给一个已经关闭的channel发送数据,会造成panic
4. 从一个已经关闭的channel接收数据,如果缓冲区为空,会返回零值
5. 无缓冲的channel是同步的,有缓冲的channel是异步的
6. 关闭一个nil channel会造成panic

### channel中ring buffer的实现

1. channel中使用了ring buffer(环形缓冲区)来缓存写入数据,
2. ring buffer有很多好处,而且非常适合实现FiFo的固定长度队列
3. channel中包含buffer、sendx、recvx
4. recvx指向最早被读取的位置,sendx指向再次写入时插入的位置

来源:[go面试题-基础类 - 专职 - 博客园 (cnblogs.com)](https://www.cnblogs.com/mayanan/p/15836710.html)

基于 MIT 许可发布