4.1 KiB
4.1 KiB
tags, create time
| tags | create time | |||||
|---|---|---|---|---|---|---|
|
2026-05-16 15:10 |
08 - 单选题 6:参数传递
题目
在 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)——每个参数的值是实参的一份拷贝。但如果拷贝的是"引用类型"(底层数据结构),那么对内容的修改会反映到原始数据上。
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)
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 |
❌ 否 |
易错陷阱题
// 陷阱: 看似修改了切片,实际没有
func badAppend(s []int) {
s = append(s, 4) // 如果触发了扩容, newSlice != originalSlice
}
// 正确做法: return append(s, 4) 或者传 *[]int
[!warning] 面试高频 "Go 到底是传值还是传引用?" → 答案是传值。只不过有些值的类型恰好是指针/引用类型。这和 Java 的说法一致:"Java 只有值传递"。