540 lines
15 KiB
Markdown
540 lines
15 KiB
Markdown
|
|
---
|
|||
|
|
tags: [Go, 考试, Week02, 武科大服务端]
|
|||
|
|
create time: 2026-05-14 12:00
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 金山考试 - 武科大服务端-第二次课
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
本次考试覆盖 Go 语言进阶核心知识点,包括切片(slice)、数组与切片的底层关系、Map 操作、结构体与方法、接口实现、标准库(JSON / 文件 / 时间)、指针与内存分配、并发编程(channel / select / sync)等。适合用于检验 Go 入门阶段学习成果。
|
|||
|
|
|
|||
|
|
**知识点地图:**
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph LR
|
|||
|
|
A[Go 进阶基础] --> B[切片 slice]
|
|||
|
|
A --> C[映射 map]
|
|||
|
|
A --> D[结构体 struct]
|
|||
|
|
A --> E[方法 method]
|
|||
|
|
A --> F[接口 interface]
|
|||
|
|
A --> G[标准库 JSON/文件/时间]
|
|||
|
|
A --> H[指针 new &]
|
|||
|
|
A --> I[并发 channel select sync]
|
|||
|
|
B --> B1[底层引用共享]
|
|||
|
|
B --> B2[make vs new]
|
|||
|
|
D --> D1[字段访问 .]
|
|||
|
|
E --> E1[值接收者 vs 指针接收者]
|
|||
|
|
I --> I1[无缓冲 Channel 同步语义]
|
|||
|
|
I --> I2[select 多路复用]
|
|||
|
|
I --> I3[WaitGroup / Mutex / RWMutex]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 正文
|
|||
|
|
|
|||
|
|
### 一、单选题
|
|||
|
|
|
|||
|
|
#### 1. 错误的切片声明方式
|
|||
|
|
|
|||
|
|
【单选题】下列关于 Go 语言的切片声明,哪个是错误的?(5分)
|
|||
|
|
|
|||
|
|
A. `var slice1 []int`
|
|||
|
|
B. `slice2 := []int{}`
|
|||
|
|
C. `slice3 := make([]int, 0)`
|
|||
|
|
D. `slice4 := new([]int)`
|
|||
|
|
|
|||
|
|
**正确答案:**D
|
|||
|
|
**作答答案:**D
|
|||
|
|
|
|||
|
|
> [!warning] new vs make
|
|||
|
|
> - `make(T, args)`:仅用于 **slice、map、channel**,返回类型 T 本身并初始化
|
|||
|
|
> - `new(T)`:返回指向类型 T 的零值指针 `*T`,不初始化内部结构
|
|||
|
|
> - `new([]int)` 返回的是 `*[]int`,虽然能编译但不能直接当作切片使用
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 2. 创建长度 0 容量 10 的切片
|
|||
|
|
|
|||
|
|
【单选题】要创建一个初始长度为 0,容量为 10 的整数切片,应该使用(5分)
|
|||
|
|
|
|||
|
|
A. `new([]int)`
|
|||
|
|
B. `make([]int, 0, 10)`
|
|||
|
|
C. `new([10]int)`
|
|||
|
|
D. `make([10]int)`
|
|||
|
|
|
|||
|
|
**正确答案:**B
|
|||
|
|
**作答答案:**B
|
|||
|
|
|
|||
|
|
> [!info] make 的参数含义
|
|||
|
|
> `make([]int, len, cap)` — len 和 cap 均可选:
|
|||
|
|
> - `make([]int, 0, 10)` → 长度 0、容量 10
|
|||
|
|
> - `make([]int, 10)` → 长度 10、容量 10(cap 省略时等于 len)
|
|||
|
|
> - 数组用 `[N]T` 语法声明,不需要 make
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 3. 数组 vs 切片的长度特性
|
|||
|
|
|
|||
|
|
【单选题】以下关于数组和切片的说法,正确的是(5分)
|
|||
|
|
|
|||
|
|
A. 数组的长度可以动态改变,切片不行
|
|||
|
|
B. 切片的长度可以动态改变,数组不行
|
|||
|
|
C. 数组和切片的长度都可以动态改变
|
|||
|
|
D. 数组和切片的长度都不能动态改变
|
|||
|
|
|
|||
|
|
**正确答案:**B
|
|||
|
|
**作答答案:**B
|
|||
|
|
|
|||
|
|
> [!tip] 核心区别一句话
|
|||
|
|
> 数组长度是**类型的一部分**(`[5]int` 和 `[10]int` 是不同的类型),不可变;切片是对数组的**轻量描述符**(ptr + len + cap),长度可以通过 append/slice 操作动态变化。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 4. 切片修改对底层数组的影响
|
|||
|
|
|
|||
|
|
【单选题】给定以下 Go 代码,运行后输出结果是什么?(5分)
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import "fmt"
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
arr := [5]int{1, 2, 3, 4, 5}
|
|||
|
|
slice := arr[1:4] // 引用 arr[1]=2, arr[2]=3, arr[3]=4
|
|||
|
|
slice[0] = 10 // 等价于 arr[1] = 10
|
|||
|
|
fmt.Println(arr)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
A. `[1 2 3 4 5]`
|
|||
|
|
B. `[1 10 3 4 5]`
|
|||
|
|
C. `[10 2 3 4 5]`
|
|||
|
|
D. 编译错误
|
|||
|
|
|
|||
|
|
**正确答案:**B
|
|||
|
|
**作答答案:**B
|
|||
|
|
|
|||
|
|
> [!important] 切片共享底层数组
|
|||
|
|
> 切片是对底层数组的**引用**。`arr[1:4]` 创建的切片其索引 0 对应 `arr[1]`,因此 `slice[0] = 10` 会直接修改 `arr[1]`。这是理解 Go 切片性能优势的关键——零拷贝传递数据。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant Arr as arr = [1,2,3,4,5]
|
|||
|
|
participant Slc as slice = arr[1:4]
|
|||
|
|
participant Set as slice[0] = 10
|
|||
|
|
Arr->>Slc: 创建引用(ptr→arr[1], len=3, cap=4)
|
|||
|
|
Slc->>Set: 写 slice[0](即 arr[1])
|
|||
|
|
Set-->>Arr: arr[1] 被改为 10
|
|||
|
|
Arr-->>Arr: arr = [1,10,3,4,5]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 5. Map 的 delete 操作
|
|||
|
|
|
|||
|
|
【单选题】观察下面的 Go 代码,判断运行结果是什么?(5分)
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import "fmt"
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
m := map[string]int{"a": 1, "b": 2, "c": 3}
|
|||
|
|
delete(m, "b")
|
|||
|
|
fmt.Println(m)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
A. `map[a:1 b:2 c:3]`
|
|||
|
|
B. `map[a:1 b:0 c:3]`
|
|||
|
|
C. `map[a:1 c:3]`
|
|||
|
|
D. `map[a:1]`
|
|||
|
|
|
|||
|
|
**正确答案:**C
|
|||
|
|
**作答答案:**C
|
|||
|
|
|
|||
|
|
> [!info] delete 的语义
|
|||
|
|
> `delete(map, key)` 直接删除键值对,key 不存在时也是安全的(noop,不 panic)。打印时只保留剩余元素。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 6. map 的 json.Unmarshal
|
|||
|
|
|
|||
|
|
【单选题】关于 map,下面说法正确的是?(5分)
|
|||
|
|
|
|||
|
|
A. `json.Unmarshal()` 的入参必须为 map 的地址
|
|||
|
|
B. 在函数调用中传递 map,则子函数中对 map 元素的增加不会导致父函数中 map 的修改
|
|||
|
|
C. 不能使用内置函数 `delete()` 删除 map 的元素
|
|||
|
|
D. 以上都不对
|
|||
|
|
|
|||
|
|
**正确答案:**A
|
|||
|
|
**作答答案:**A
|
|||
|
|
|
|||
|
|
> [!info] 为什么必须是地址?
|
|||
|
|
> `json.Unmarshal(data, &m)` 需要指针才能修改原 map。而 `json.Marshal(data, m)` 不需要地址(值即可读取)。
|
|||
|
|
>
|
|||
|
|
> **补充:map 作为参数传递**——Go 中 map 是引用类型(内部是指针结构体),所以子函数通过同一底层数据结构增删元素,父函数可见。选项 B 说反了。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 7. 结构体字段访问
|
|||
|
|
|
|||
|
|
【单选题】在 Go 语言中,如果你有一个结构体实例 `myStruct`,你如何访问其字段 `field1` 的值?(5分)
|
|||
|
|
|
|||
|
|
A. `myStruct[field1]`
|
|||
|
|
B. `myStruct->field1`
|
|||
|
|
C. `myStruct.field1`
|
|||
|
|
D. `*myStruct.field1`
|
|||
|
|
|
|||
|
|
**正确答案:**C
|
|||
|
|
**作答答案:**C
|
|||
|
|
|
|||
|
|
> [!note] Go 没有箭头运算符
|
|||
|
|
> Go 不使用 `->`(C/C++ 风格),也不使用 `[key]`(像 map 那样)。统一用点号 `.` 访问字段。当持有指针 `p` 时,也只需 `p.Field`,编译器自动解引用。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 8. 结构体方法的错误认知
|
|||
|
|
|
|||
|
|
【单选题】以下关于结构体方法的说法,错误的是(5分)
|
|||
|
|
|
|||
|
|
A. 值接收者方法不能修改结构体的字段值
|
|||
|
|
B. 指针接收者方法可以修改结构体的字段值
|
|||
|
|
C. 值接收者方法在传递参数时效率更高
|
|||
|
|
D. 指针接收者方法在结构体较大时更节省内存
|
|||
|
|
|
|||
|
|
**正确答案:**C
|
|||
|
|
**作答答案:**C
|
|||
|
|
|
|||
|
|
> [!warning] 值接收者的陷阱
|
|||
|
|
> - A ✅:值接收者是副本,修改不影响原对象
|
|||
|
|
> - B ✅:指针接收者直接操作原对象
|
|||
|
|
> - C ❌:**这是本题答案**。值接收者在传参时需要复制整个结构体,当结构体较大时反而更慢
|
|||
|
|
> - D ✅:指针只传 8 字节(ptr),避免完整复制
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 9. 接口实现的完整性
|
|||
|
|
|
|||
|
|
【单选题】Go 中如何正确实现一个接口?(5分)
|
|||
|
|
|
|||
|
|
A. 只需要声明接口中的所有方法
|
|||
|
|
B. 只需要部分实现接口中的方法
|
|||
|
|
C. 需要声明并实现接口中的所有方法
|
|||
|
|
D. 接口不能被实现,只能被继承
|
|||
|
|
|
|||
|
|
**正确答案:**C
|
|||
|
|
**作答答案:**C
|
|||
|
|
|
|||
|
|
> [!info] Go 接口是隐式实现
|
|||
|
|
> Go 没有 `implements` 关键字。只要一个类型的**所有方法签名**与接口一致,就自动实现了该接口。缺少任一方法都会编译报错。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 10. JSON 处理的标准库
|
|||
|
|
|
|||
|
|
【单选题】在 Go 中处理 JSON 数据的推荐方法是?(5分)
|
|||
|
|
|
|||
|
|
A. 使用 `encoding/json` 标准库
|
|||
|
|
B. 使用外部包如 `jsoniter`
|
|||
|
|
C. 手动解析字符串
|
|||
|
|
D. 使用 `bufio` 读取器
|
|||
|
|
|
|||
|
|
**正确答案:**A
|
|||
|
|
**作答答案:**A
|
|||
|
|
|
|||
|
|
> [!info] encoding/json
|
|||
|
|
> Go 标准库 `encoding/json` 提供 `Marshal` / `Unmarshal` 两个核心函数,通过结构体 tag(`` `json:"name"` ``)控制序列化行为,满足绝大多数场景需求。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 11. 格式化输出的函数选择
|
|||
|
|
|
|||
|
|
【单选题】在 Go 中,如何通过 fmt 包输出格式化的字符串?(5分)
|
|||
|
|
|
|||
|
|
A. `fmt.Print()`
|
|||
|
|
B. `fmt.Println()`
|
|||
|
|
C. `fmt.Printf()`
|
|||
|
|
D. `fmt.Format()`
|
|||
|
|
|
|||
|
|
**正确答案:**C
|
|||
|
|
**作答答案:**C
|
|||
|
|
|
|||
|
|
> [!tip] fmt 包常用函数速记
|
|||
|
|
> - `Print`:无换行,无格式
|
|||
|
|
> - `Println`:自动追加换行
|
|||
|
|
> - `Printf`:**支持占位符格式化**,如 `%d`、`%s`、`%v`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 12. 读取文件的便捷方式
|
|||
|
|
|
|||
|
|
【单选题】选择 Go 语言中正确的方式来读取文件。(5分)
|
|||
|
|
|
|||
|
|
A. `os.ReadFile()`
|
|||
|
|
B. `os.OpenFile()`
|
|||
|
|
C. `ioutil.ReadAll()`
|
|||
|
|
D. `file.Read()`
|
|||
|
|
|
|||
|
|
**正确答案:**A
|
|||
|
|
**作答答案:**A
|
|||
|
|
|
|||
|
|
> [!note] ioutil 已废弃
|
|||
|
|
> `ioutil.ReadAll` 和 `ioutil.ReadFile` 在 Go 1.16+ 中被迁移到 `io` 和 `os` 包中。推荐写法:`os.ReadFile("path")` 一次性读取全部文件内容到 `[]byte`。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 13. 时间日期的标准库
|
|||
|
|
|
|||
|
|
【单选题】在 Go 中,用于处理时间和日期的标准库是哪一个?(5分)
|
|||
|
|
|
|||
|
|
A. `time`
|
|||
|
|
B. `date`
|
|||
|
|
C. `timer`
|
|||
|
|
D. `clock`
|
|||
|
|
|
|||
|
|
**正确答案:**A
|
|||
|
|
**作答答案:**A
|
|||
|
|
|
|||
|
|
> [!info] time 包核心用法
|
|||
|
|
> ```go
|
|||
|
|
> now := time.Now() // 当前时间
|
|||
|
|
> time.Sleep(1 * time.Second) // 休眠
|
|||
|
|
> t.Format("2006-01-02 15:04:05") // 格式化(记忆:参考时间 2006-01-02)
|
|||
|
|
> ```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 14. new 的正确使用场景
|
|||
|
|
|
|||
|
|
【单选题】以下哪种情况应该使用 `new` 来分配内存?(5分)
|
|||
|
|
|
|||
|
|
A. 创建一个切片
|
|||
|
|
B. 创建一个映射
|
|||
|
|
C. 创建一个结构体指针
|
|||
|
|
D. 扩展一个切片的容量
|
|||
|
|
|
|||
|
|
**正确答案:**C
|
|||
|
|
**作答答案:**C
|
|||
|
|
|
|||
|
|
> [!info] new vs make 对比
|
|||
|
|
> | 用途 | new(T) | make(T, ...)|
|
|||
|
|
> |------|--------|-------------|
|
|||
|
|
> | 返回类型 | `*T`(指针) | T 本身 |
|
|||
|
|
> | 适用于 | 所有类型 | slice, map, channel |
|
|||
|
|
> | 初始化 | 零值 | 完整初始化 |
|
|||
|
|
> | 典型场景 | `new(Person)` | `make([]int, 10)` |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 15. 无缓冲 channel 的同步语义
|
|||
|
|
|
|||
|
|
【单选题】无缓冲 channel 的特点是什么?(5分)
|
|||
|
|
|
|||
|
|
A. 发送操作永远不会阻塞
|
|||
|
|
B. 接收操作永远不会阻塞
|
|||
|
|
C. 发送和接收操作必须同时发生
|
|||
|
|
D. 可以存储无限数量的值
|
|||
|
|
|
|||
|
|
**正确答案:**C
|
|||
|
|
**作答答案:**C
|
|||
|
|
|
|||
|
|
> [!important] 无缓冲 Channel = 同步通道
|
|||
|
|
> - 容量为 0,send 和 receive **必须同时就绪**才能完成
|
|||
|
|
> - 生产者 send 后阻塞直到有消费者 receive
|
|||
|
|
> - 消费者 receive 后阻塞直到有生产者 send
|
|||
|
|
> - 这是 goroutine 间最原子的同步机制
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 16. Select 语句的作用
|
|||
|
|
|
|||
|
|
【单选题】Go 语言中的 `select` 语句用于什么目的?(5分)
|
|||
|
|
|
|||
|
|
A. 声明变量
|
|||
|
|
B. 捕获异常
|
|||
|
|
C. 监听多个 Channel 操作
|
|||
|
|
D. 同步 Goroutine
|
|||
|
|
|
|||
|
|
**正确答案:**C
|
|||
|
|
**作答答案:**C
|
|||
|
|
|
|||
|
|
> [!info] select 的行为规则
|
|||
|
|
> - 每个 case 必须是 channel 的 send 或 receive 操作
|
|||
|
|
> - 所有 case 求值后,随机选择一个可用的执行
|
|||
|
|
> - 若无可用 case 且有 `default`,立即执行 default
|
|||
|
|
> - 若无可用 case 且无 default,阻塞直到某个 case 就绪
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 17. Goroutine 同步的原语选择
|
|||
|
|
|
|||
|
|
【单选题】在 Go 语言中,哪个 sync 原语可以用于确保多个 Goroutine 的同步执行?(5分)
|
|||
|
|
|
|||
|
|
A. `WaitGroup`
|
|||
|
|
B. `Mutex`
|
|||
|
|
C. `Atomic`
|
|||
|
|
D. `Context`
|
|||
|
|
|
|||
|
|
**正确答案:**A
|
|||
|
|
**作答答案:**A
|
|||
|
|
|
|||
|
|
> [!info] 各 sync 原语分工
|
|||
|
|
> - `sync.WaitGroup`:**等待一组 goroutine 完成**(Add / Done / Wait)
|
|||
|
|
> - `sync.Mutex`:互斥锁,保护临界区
|
|||
|
|
> - `sync/atomic`:原子操作,无锁并发
|
|||
|
|
> - `context.Context`:取消信号与超时控制
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 18. 取地址运算符
|
|||
|
|
|
|||
|
|
【单选题】在 Go 中获取变量的内存地址应使用什么运算符?(5分)
|
|||
|
|
|
|||
|
|
A. `*`
|
|||
|
|
B. `&`
|
|||
|
|
C. `#`
|
|||
|
|
D. `@`
|
|||
|
|
|
|||
|
|
**正确答案:**B
|
|||
|
|
**作答答案:**B
|
|||
|
|
|
|||
|
|
> [!tip] 指针运算口诀
|
|||
|
|
> - `&x`:取 x 的地址
|
|||
|
|
> - `*p`:解除 p 指向,访问目标值
|
|||
|
|
> - 二者互为逆运算:`*&x == x`,`&*p == p`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 19. RWMutex vs Mutex
|
|||
|
|
|
|||
|
|
【单选题】在 Go 中,`sync.RWMutex` 与 `sync.Mutex` 的主要区别是?(5分)
|
|||
|
|
|
|||
|
|
A. Mutex 只支持读操作
|
|||
|
|
B. RWMutex 允许多个读锁或一个写锁,Mutex 仅支持互斥访问
|
|||
|
|
C. 两者没有区别
|
|||
|
|
D. RWMutex 不能与 channel 同时使用
|
|||
|
|
|
|||
|
|
**正确答案:**B
|
|||
|
|
**作答答案:**B
|
|||
|
|
|
|||
|
|
> [!info] 读写锁的性能意义
|
|||
|
|
> 当读多写少时,`RWMutex` 显著优于 `Mutex`:多个 goroutine 可同时持有多把读锁 (`RLock`),只有写操作 (`Lock`) 时才互斥。这是典型的「读并发、写串行」优化手段。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 20. 使用指针接收者的原因
|
|||
|
|
|
|||
|
|
【单选题】以下结构体方法定义中,使用指针接收者的原因通常是(5分)
|
|||
|
|
|
|||
|
|
A. 方法内部需要修改结构体的字段值
|
|||
|
|
B. 为了提高性能(避免大结构体拷贝)
|
|||
|
|
C. 为了遵循代码规范
|
|||
|
|
D. 以上都是
|
|||
|
|
|
|||
|
|
**正确答案:**D
|
|||
|
|
**作答答案:**D
|
|||
|
|
|
|||
|
|
> [!info] 何时用指针接收者?
|
|||
|
|
> 1. 需要**修改**原对象状态
|
|||
|
|
> 2. 结构体**较大**,减少拷贝开销
|
|||
|
|
> 3. 需要保证**一致性**(同一个对象的方法要么全用指针,要么全用值接收者)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 21. 值接收者方法不会修改原对象
|
|||
|
|
|
|||
|
|
【单选题】对于以下结构体和方法定义,调用 ChangeName 方法后,原始的 Person 结构体实例的 Name 字段会改变吗?(5分)
|
|||
|
|
|
|||
|
|
```language
|
|||
|
|
type Person struct {
|
|||
|
|
Name string
|
|||
|
|
Age int
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (p Person) ChangeName(newName string) {
|
|||
|
|
p.Name = newName
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
A. 会
|
|||
|
|
B. 不会
|
|||
|
|
C. 不确定
|
|||
|
|
D. 以上都不对
|
|||
|
|
|
|||
|
|
**正确答案:**B
|
|||
|
|
**作答答案:**B
|
|||
|
|
|
|||
|
|
> [!warning] 经典面试题
|
|||
|
|
> `ChangeName` 使用**值接收者**,这意味着 `p` 是原对象的**副本**。方法内修改 `p.Name` 只会影响副本,原对象不受影响。若希望修改生效,应将接收者改为指针 `(p *Person)`。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 二、多选题
|
|||
|
|
|
|||
|
|
#### 1. 指针访问成员变量的方式
|
|||
|
|
|
|||
|
|
【多选题】通过指针变量 p 访问其成员变量 name,有哪几种方式?(5分)
|
|||
|
|
|
|||
|
|
A. `p.name`
|
|||
|
|
B. `(&p).name`
|
|||
|
|
C. `(*p).name`
|
|||
|
|
D. `p->name`
|
|||
|
|
|
|||
|
|
**正确答案:**AC
|
|||
|
|
**作答答案:**AC
|
|||
|
|
|
|||
|
|
> [!info] 指针访问字段两种方式
|
|||
|
|
> - `p.name` ✅:Go 语法糖,编译器自动解引用,推荐写法
|
|||
|
|
> - `(*p).name` ✅:先解引用再 `.name`,等价但稍显繁琐
|
|||
|
|
> - `(&p).name` ❌:`&p` 是指针的地址(双重指针 `**P`),不符合题意
|
|||
|
|
> - `p->name` ❌:Go 没有 `->` 运算符
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 2. panic 和 recover 的使用
|
|||
|
|
|
|||
|
|
【多选题】关于 Go 语言中 panic 和 recover 的使用,以下正确的是?(5分)
|
|||
|
|
|
|||
|
|
A. panic 可以在任何地方引发,但 recover 只有在 defer 调用的函数中有效
|
|||
|
|
B. recover 用于终止 panic 的异常传递过程
|
|||
|
|
C. panic 无法被捕获,会直接导致程序崩溃
|
|||
|
|
D. 使用 panic 和 recover 可以替代传统的错误处理方式
|
|||
|
|
|
|||
|
|
**正确答案:**AB
|
|||
|
|
**作答答案:**ABC
|
|||
|
|
|
|||
|
|
> [!warning] 易错辨析
|
|||
|
|
> - A ✅:recover 放在 defer 中能捕获当前 goroutine 的 panic,阻止程序崩溃
|
|||
|
|
> - B ✅:recover 被调用后,panic 链中断,程序恢复正常运行
|
|||
|
|
> - C ❌:panic **可以**被 recover 捕获,并非一定会导致程序崩溃
|
|||
|
|
> - D ❌:Go 设计哲学中,panic 用于**真正的异常情况**(如编程错误、初始化失败),业务错误应用 error 返回
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
#### 3. 数组和切片的综合理解
|
|||
|
|
|
|||
|
|
【多选题】在 Go 语言中,关于数组(array)和切片(slice),以下说法正确的是?(5分)
|
|||
|
|
|
|||
|
|
A. 数组定义时必须指定长度,且长度是数组类型的一部分
|
|||
|
|
B. 切片定义时不需要指定长度,其长度可以在运行时动态变化
|
|||
|
|
C. 数组作为函数参数传递时,传递的是数组的副本,函数内对数组的修改不会影响原始数组
|
|||
|
|
D. 切片作为函数参数传递时,传递的是切片结构体的副本,函数内对切片的修改会反映到原始切片上
|
|||
|
|
|
|||
|
|
**正确答案:**ABCD
|
|||
|
|
**作答答案:**ABCD
|
|||
|
|
|
|||
|
|
> [!important] 四个选项全对的原因
|
|||
|
|
> - A ✅:`[3]int` 和 `[5]int` 是不同长度决定类型
|
|||
|
|
> - B ✅:slice 本质是「指针+长度+容量」描述符
|
|||
|
|
> - C ✅:数组传值是**深拷贝**整个数组内容
|
|||
|
|
> - D ✅:slice 内部含底层数组指针,虽然 slice 自身是副本(ptr/len/cap 三件套被拷贝),但对 `slice[i] = x` 等元素级修改通过 ptr 反映到底层数组,因此源数据可见
|
|||
|
|
|
|||
|
|
## 关联笔记
|
|||
|
|
|
|||
|
|
- [[hhs/EXAM/Week01]]
|
|||
|
|
- [[hhs/EXAM/Week05]]
|
|||
|
|
- [[hhs/EXAM/Week06]]
|