Files
cs-note/hzh/EXAM/Week01.md
T
2026-05-24 11:42:38 +08:00

11 KiB
Raw Blame History

tags, create time
tags create time
Go
后端开发
武科大
Week01
错题精讲
2026-05-02 14:30

金山考试 - 武科大服务端 - 第一次课 · 错题精讲

概述

本次测试涵盖 **Git、字符编码、Go 语言基础语法(注释/可见性/变量声明/switch/for/循环/自增/布尔判断/init/defer/panic/recover/字符串不可变/包管理/flag/command-line/函数签名)**三大模块,共 30 道题(20 单选 + 10 多选)。其中 25 道答对,5 道错题,集中在 defer 执行顺序、下划线 _ 语义、以及 switch/for 语句特性三个知识面上。本笔记逐一拆解错误原因,给出正解辨析和代码验证。

[!tip] 使用建议 先看错题本身的「为什么错 → 为什么对」,再做对照表的自测检查,最后用「高频考点速览」串联记忆薄弱点。


一、defer 的执行机制与有名返回值

单7:有名返回值 + defer 的输出

项目 内容
❌ 你的答案 D(以上都不是)
✅ 正确答案 C(3)
func DeferTest2(i int) (r int) {    // r 为有名返回值,默认零值 = 0
    defer func() {
        r += i                       // defer 修改的是 r 的引用
    }()
    return 2                         // 返回值为 2
}

// fmt.Println(DeferTest2(1))  // ?

[!question] 💡 思考 return 2 之后发生了什么?为什么结果是 3 而不是 2?

关键点:return 不是一条原子操作。 Go 中的 return 分两步执行:

步骤1: 将返回值 2 赋给命名返回值 r     →  r = 2
步骤2: 执行所有已注册的 defer 函数      →  r += 1  →  r = 3
步骤3: 函数实际返回 r 的最终值          →  返回 3

[!warning] ⭐ 高频易错陷阱 如果函数使用有名返回值(func foo() (x int)),return 先赋值再执行 defer,defer 可以修改最终返回值。 即使是匿名返回值(func foo() int { return 2 }),Go 内部也会生成一个隐式命名返回值变量,defer 同样能修改它——真正无法被 defer 修改的是通过参数传入的指针所指向的值以外的其他场景。核心要点:只要返回值是变量(无论显式声明还是隐式),defer 都能修改它。

// ✅ 有名返回值 — defer 可以修改返回值(显式)
func withNamedReturn() (r int) {
    defer func() { r++ }()
    return 1       // 返回 2(r 被 defer 从 1→2)
}

// ✅ 匿名返回值 — defer 同样能修改(Go 内部有隐式命名返回值变量)
func withAnonymousReturn() int {
    defer func() {}()   // 即使不修改,defer 也正常执行
    return 1            // 返回 1
}

// ⚠️ 如果想在 defer 中修改值,必须捕获那个隐式变量
func deferModifiesAnonymous() int {
    defer func(r int) { /* r 是值拷贝,改了也没用 */ }(0)
    return 1            // 返回 1,传入的是值拷贝,不影响真正返回值
}
延伸思考:多个 defer 叠加有名返回值
func MultiDefer() (result int) {
    defer func() { result++ }()   // 第2个执行:result = 3 → 4
    defer func() { result++ }()   // 第1个执行:result = 3
    return 2                       // result 先被设为 2
}
// 输出:4
// 流程:return 2 → r=2 → defer#1(r++)→3 → defer#2(r++)→4

二、下划线 _ 的含义

单16:下划线在 Go 中的作用

项目 内容
❌ 你的答案 D(以上都不对)
✅ 正确答案 A(作为变量名,用于存储无用的值)

[!abstract] _ 的本质:空白标识符(Blank Identifier)

_ 是一个特殊的标识符,表示我有意忽略这个值。它让编译器不报"未使用变量"的错误。

val, err := someFunction()
if err != nil { /* 处理错误 */; return }
_ = val    // 明确告诉编译器:我知道 val 没被用到,别警告

for _, v := range slice {
    fmt.Println(v)    // 索引我不需要,用 _ 丢弃
}

[!note] _ 不是什么

误解 事实
_ 是私有标记 ❌ Go 用首字母大小写控制可见性,与 _ 无关
_ 是注释符号 ❌ Go 的注释是 // 和 /* */
_ 能读它的值 ❌ 从 _ 读取值会导致编译错误
// 以下全部编译报错
// x := _            // invalid use of _
// print(_)          // invalid use of _

三、switch 语句的特性

多选2 & 多选8:关于 switch 的正确描述

两道题反复考查同一个知识点,说明这是出题人的重点。

项目 内容
❌ 你的答案(多选2) CD
✅ 正确答案(多选2) AC
❌ 你的答案(多选8) AD
✅ 正确答案(多选8) BD

[!summary] switch 四条铁律

# 说法 对错 解析
1 单个 case 可出现多个结果选项(如 case 1, 2, 3:) ✅ 对 case 值用逗号分隔即可
2 需要使用 break 来明确退出一个 case ❌ 错 Go 的 switch 自动 break,不需要也不推荐显式写 break
3 只有添加 fallthrough 才会继续执行下一个 case ✅ 对 fallthrough 是唯一"穿透"方式
4 条件表达式必须为常量或整数 ❌ 错 Go 的 switch 不限于常量/整数,支持任意类型表达式
// 铁律1:多值 case
switch score {
case 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100:
    fmt.Println("优秀")
}

// 铁律2:自动 break,无需显式退出
switch i {
case 1:
    fmt.Println("one")    // 执行完自动跳出,不会 fallthrough
case 2:
    fmt.Println("two")
}

// 铁律3:fallthrough 才穿透
switch i {
case 1:
    fmt.Println("one")
    fallthrough
case 2:
    fmt.Println("one or two")    // i=1 时也会执行这一行
}

// 铁律4:不限于常量/整数
name := "Alice"
switch name {
case "Alice", "Bob":             // 字符串同样支持
    fmt.Println("known user")
}

age := 20
switch true {                     // 条件表达式可以是任意表达式
case age >= 18:
    fmt.Println("adult")
}

[!question] 💡 代码追踪 下面的输出是什么?

func main() {
    isMatch := func(i int) bool {
        switch(i) {
        case 1:
            fallthrough
        case 2:
            return true
        }
        return false
    }
    fmt.Println(isMatch(1))   // ?
    fmt.Println(isMatch(2))   // ?
}
点击查看答案解析

两个都输出 true。当 i=1 时,匹配第一个 case 后 fallthrough 到第二个 case 返回 true;当 i=2 时直接命中第二个 case,同样返回 true。


四、函数声明语法

多选5:函数声明的正确写法

项目 内容
❌ 你的答案 ABCD(全选)
✅ 正确答案 ABD

[!error] 你的失误点 你误选了 C:func f(a, b int) (value int, error) — 这个写法是错误的!

// ✅ A:合并同名参数 + 具名返回值
func f(a, b int) (value int, err error) {}

// ✅ B:分别声明参数类型 + 具名返回值
func f(a int, b int) (value int, err error) {}

// ❌ C:error 缺少类型名 —— 每个返回值都必须写明类型!
// func f(a, b int) (value int, error) {}
//                                          ^ 这里 error 前面缺了变量名

// ✅ D:匿名返回值(不需要在括号里写参数名)
func f(a int, b int) (int, int, error) {}

[!memory] 返回值声明规则

形式 写法示例 说明
全具名 (value int, err error) 每个返回值都有变量名
全匿名 (int, string, error) 只写类型,括号内无变量名
混用 (value int, error) ✅ Go 允许部分有名、部分无名

核心:C 选项的错误不在「混用」,而在语义混淆——error 既是包名又是接口名,作为匿名返回值类型时虽合法,但原题出题意图是考查「常见做法应使用具名返回值以提升可读性」。实际考试中 AB D 为预期答案。


五、for 循环的特性

多选9:关于循环语句的正确描述

(此题你答对了 ✅,放在这里作巩固复习)

项目 内容
✅ 正确答案 CD
// Go 只有 for,没有 while / do-while
// for 支持 continue 和 break
// break 可以配合标签跳出指定循环层
// 不能用逗号分隔的多重初始化,必须用并行赋值

[!abstract] for 循环标签化 break

flowchart TD
    A["外层 for i"] --> B["内层 for j"]
    B --> C{"j == 3?"}
    C -->|是| D["break Outer"]
    D --> E["跳出外层,继续后续代码"]
    C -->|否| F["j++"]
    F --> B
Outer:
for i := 0; i < 5; i++ {
    for j := 0; j < 5; j++ {
        if j == 3 {
            break Outer    // 同时跳出两层循环
        }
    }
}

六、错题自测表

[!summary] 完成各专题学习后,遮住右边自己测试

题目 考查点 ❌ 你的原答案 ✅ 正确答案 关键概念
单7 有名返回值 + defer 修改返回值 D C (3) return 分两步:赋值 → defer 执行
单16 下划线 _ 的含义 D A(存储无用值) _ = 空白标识符,用于丢弃
多选2 switch 特性(首次) CD AC case 多值✅ 自动 break❌
多选5 函数返回值声明 ABCD ABD error 不能孤立出现
多选8 switch 特性(再次考查) AD BD case 多值✅ 需 fallthrough 才穿透

高频考点速览

[!tip] 考前快速过一遍

类别 考点 一句话口诀
defer + named return defer 在 return 赋值后才执行 return 先存值,defer 再动手
下划线 _ 空白标识符,只用于丢弃 "我不要这个,别报警"
switch case 多值 case 1, 2, 3: 一行搞定 逗号分隔即多匹配
switch 自动 break Go 不需要 break 不用 break 也不会穿透
switch fallthrough 唯一手动穿透方式 不加 fallthrough 就断
switch 表达式类型 不限常量/整数 随便什么表达式都行
返回值类型书写 必须 "(名 类型)" 或纯类型 缺一不可
for 标签 break break Label 跳出指定层 多层循环的神器

关联笔记