--- tags: [笔试, 微派, Go, 函数, 传参] create time: 2026-05-16 15:10 --- # 08 - 单选题 6:参数传递 ## 题目 在 Go 语言中,考虑以下代码: ```go func main() { // 第 1 组 a := []int{1, 2, 3} f1(a) fmt.Println(a) // ? // 第 2 组 b := map[string]int{"x": 10} f2(b) fmt.Println(b) // ? // 第 3 组 c := &struct{ v int }{v: 100} f3(c) fmt.Println(*c) // ? // 第 4 组 d := 10 f4(d) fmt.Println(d) // ? } func f1(s []int) { s[0] = 99 } func f2(m map[string]int) { m["x"] = 20 } func f3(p *struct{ v int }) { p.v = 300 } func f4(n int) { n = 400 } ``` 以上 `fmt.Println` 的输出结果分别是: | 选项 | 第1组 (切片a) | 第2组 (映射b) | 第3组 (结构体指针c) | 第4组 (整数d) | |------|-------------|-------------|-------------------|-------------| | A | `[99 2 3]` | `map[x:20]` | `{300}` | `10` | | B | `[1 2 3]` | `map[x:10]` | `{100}` | `400` | | C | `[99 2 3]` | `map[x:10]` | `{300}` | `10` | | D | `[99 2 3]` | `map[x:20]` | `{100}` | `10` |
点击查看答案与解析 ### ✅ 正确答案:**A** --- ### 详细解析 #### Go 的参数传递原则 ⭐ > [!info] 核心规则 > **Go 永远都是值传递(pass by value)**——每个参数的值是实参的一份拷贝。但如果拷贝的是"引用类型"(底层数据结构),那么对内容的修改会反映到原始数据上。 ```mermaid flowchart TD subgraph "值传递的本质" A["main: a → header{s,p,h}"] -->|"复制header"| B["f1: s → header{s,p,h}"] end subgraph "但底层数组 h 是共享的" H["📦 堆上的数组"] A -.->|p指向| H B -.->|p指向同一地址| H end B -->|"s[0] = 99 → 写H[0]"| H H -->|"main看到变化"| A ``` #### 逐项分析 **第 1 组:切片 (`[]int`)** ```go func f1(s []int) { s[0] = 99 } // ❌ 等等,这个能改吗? ``` - 切片的底层是一个**结构体**(slice header): `struct{ Data *int; Len int; Cap int }` - 传入函数时,**这个结构体被值拷贝了** - 但 `Data` 指针指向的是**同一个底层数组**! - 所以 `s[0] = 99` 通过共享的底层数组修改了原数据 → **输出 `[99 2 3]`** ✓ > [!tip] 但是... 如果是在函数里 `s = append(s, 4)` 呢? > 那就要分情况看: 如果 cap 足够大(追加不触发扩容),`Data` 共享 → 影响原切片; 如果触发扩容 → 分配新数组 → **不影响**原切片。 **第 2 组:映射 (`map[string]int`)** - map 也是引用类型,它的值是 `*hmap`(一个内部结构的指针) - 传入函数后拷贝的是指针本身,但指向同一个 `hmap` - `m["x"] = 20` 修改了共享的 map → **输出 `map[x:20]`** ✓ **第 3 组:结构体指针 (`*struct{ v int }`)** - 值传递拷贝的是指针的值(即地址) - `p.v = 300` 通过这个地址修改了原始结构体 → **输出 `{300}`** ✓ **第 4 组:基本类型 (`int`)** - int 是值类型,传入时拷贝了数值 `10` - `n = 400` 只改了局部副本,不影响原始变量 → **输出 `10`** ✓ #### 汇总表 | 类型 | 本质 | 拷贝内容 | 函数内修改内容 | 是否影响外部 | |------|------|---------|--------------|-----------| | `[]int` 切片 | 引用类型 | slice header(含指针) | `s[i] = x` | ✅ 是 | | `map[K]V` | 引用类型 | 指针 `*hmap` | `m[k] = v` | ✅ 是 | | `*T` 指针 | 引用类型 | 地址值 | `p.field = v` | ✅ 是 | | `int` 等基础类型 | 值类型 | 实际数值 | `n = v` | ❌ 否 | | `struct{T}` 结构体 | 值类型 | 所有字段 | `s.f = v` | ❌ 否 | #### 易错陷阱题 ```go // 陷阱: 看似修改了切片,实际没有 func badAppend(s []int) { s = append(s, 4) // 如果触发了扩容, newSlice != originalSlice } // 正确做法: return append(s, 4) 或者传 *[]int ``` > [!warning] 面试高频 > "Go 到底是传值还是传引用?" → **答案是传值**。只不过有些值的类型恰好是指针/引用类型。这和 Java 的说法一致:"Java 只有值传递"。