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

4.1 KiB
Raw Blame History

tags, create time
tags create time
笔试
微派
Go
函数
传参
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 只有值传递"。