Files
leetcode-go/笔试/微派 Test1/08-单选题6-参数传递.md
T

136 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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` |
<details>
<summary>点击查看答案与解析</summary>
### ✅ 正确答案:**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 只有值传递"。
</details>