136 lines
4.1 KiB
Markdown
136 lines
4.1 KiB
Markdown
---
|
||
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>
|