Go语言参数

发布于 2025年5月19日

本文内容由AI生成并经过人工精选,不保证100%准确,请酌情参考使用

在 Go 语言中,能否通过函数参数修改原始值,取决于参数的类型和传递方式。以下是具体类型的行为分析及示例:


一、无法直接通过入参修改原始值的类型(需通过指针或返回值)

1. 基本类型

  • 示例int, float, string, bool
  • 问题:按值传递,函数内修改的是副本,原始值不变
  • 错误示例
    func modifyInt(x int) { x = 100 // 修改的是副本 } func main() { a := 10 modifyInt(a) fmt.Println(a) // 输出 10(未改变) }
  • 解决方案:传递指针
    func modifyIntByPtr(x *int) { *x = 100 // 通过指针修改原始值 } func main() { a := 10 modifyIntByPtr(&a) fmt.Println(a) // 输出 100 }

2. 数组(Array)

  • 特点:数组是值类型,传递时复制全部元素
  • 错误示例
    func modifyArray(arr [3]int) { arr[0] = 100 // 修改的是副本 } func main() { arr := [3]int{1, 2, 3} modifyArray(arr) fmt.Println(arr) // 输出 [1 2 3](未改变) }
  • 解决方案:传递指针或改用切片
    // 方法1:指针 func modifyArrayByPtr(arr *[3]int) { arr[0] = 100 // 语法糖,等价于 (*arr)[0] = 100 } // 方法2:改用切片(Slice) func modifySlice(s []int) { s[0] = 100 // 切片底层引用数组 } func main() { arr := [3]int{1, 2, 3} modifySlice(arr[:]) // 传递切片 fmt.Println(arr) // 输出 [100 2 3] }

3. 结构体(Struct)

  • 特点:按值传递时,函数内修改的是副本
  • 错误示例
    type Person struct { Name string } func modifyStruct(p Person) { p.Name = "Bob" // 修改副本 } func main() { p := Person{Name: "Alice"} modifyStruct(p) fmt.Println(p.Name) // 输出 "Alice" }
  • 解决方案:传递指针
    func modifyStructByPtr(p *Person) { p.Name = "Bob" // 修改原始结构体 } func main() { p := &Person{Name: "Alice"} modifyStructByPtr(p) fmt.Println(p.Name) // 输出 "Bob" }

二、可直接通过入参修改原始值的类型(无需额外操作)

1. 切片(Slice)

  • 特点:切片是引用类型,传递时底层数组共享
  • 注意:修改元素可影响原切片,但 append 可能触发扩容(需返回新切片)
  • 正确示例
    func modifySlice(s []int) { s[0] = 100 // 直接修改原切片元素 } func main() { s := []int{1, 2, 3} modifySlice(s) fmt.Println(s) // 输出 [100 2 3] }
  • append 的特殊性
    func appendSlice(s []int) []int { return append(s, 4) // 必须返回新切片 } func main() { s := []int{1, 2, 3} s = appendSlice(s) // 需要重新赋值 fmt.Println(s) // 输出 [1 2 3 4] }

2. 映射(Map)

  • 特点:传递时共享底层哈希表
  • 正确示例
    func modifyMap(m map[string]int) { m["key"] = 100 // 直接修改原映射 } func main() { m := map[string]int{"key": 1} modifyMap(m) fmt.Println(m["key"]) // 输出 100 }

3. 通道(Channel)、指针(Pointer)、函数(Func)

  • 特点:传递的是引用,可直接修改指向的数据
  • 示例
    func modifyPointerContent(p *int) { *p = 100 // 通过指针修改原始值 } func main() { x := 10 modifyPointerContent(&x) fmt.Println(x) // 输出 100 }

三、总结:哪些类型需要特别注意?

类型能否直接通过入参修改原始值?解决方案
int, bool❌ 不能传递指针
string❌ 不能(不可变)无法直接修改,需返回新字符串
数组❌ 不能传递指针或改用切片
结构体❌ 不能传递指针
切片✅ 能(但 append 需返回)直接传递
映射✅ 能直接传递
通道/指针✅ 能直接传递

四、最佳实践

  1. 需要修改原始值时
    • 基本类型、数组、结构体 → 传递指针
    • 切片、映射 → 直接传递,但注意 append 需返回新切片
  2. 避免副作用
    • 如果函数需要返回修改后的值,优先通过返回值而非指针参数(提高可读性)
    // 更清晰的写法 func addOne(x int) int { return x + 1 } // 而不是 func addOneByPtr(x *int) { *x = *x + 1 }