Menu
CoddyTech

值传递与引用传递

本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 21 课,共 107 课。

Go 是一种值传递语言。当你将变量传递给函数时,Go 会创建该值的副本。这意味着函数内部的更改不会影响原始变量。

func double(n int) {
    n = n * 2
}

func main() {
    x := 10
    double(x)
    fmt.Println(x)  // 10(未改变)
}

结构体也是如此。将结构体传递给函数时,会复制它的所有字段,因此函数内部所做的修改会丢失。

type Person struct {
    Name string
    Age  int
}

func birthday(p Person) {
    p.Age++
}

func main() {
    alice := Person{Name: "Alice", Age: 30}
    birthday(alice)
    fmt.Println(alice.Age)  // 30(未改变)
}

要修改原始值,请改为传递一个指针。指针本身会被复制,但它仍指向同一个内存地址,因此你可以修改原始数据。

func birthday(p *Person) {
    p.Age++
}

func main() {
    alice := Person{Name: "Alice", Age: 30}
    birthday(&alice)
    fmt.Println(alice.Age)  // 31(已修改!)
}

需要修改原始值,或传递较大的结构体以避免高开销的复制时,使用指针。想要保护原始数据不被意外修改时,使用值。

challenge icon

挑战

简单

让我们构建一个购物车系统,演示按值传递结构体和按指针传递结构体之间的区别。你将亲自看到,只有指针参数才能修改原始数据。

你需要将代码分布在两个文件中:

  • cart.go:定义一个 CartItem 结构体,包含 Name(string)、Price(float64)和 Quantity(int)字段。创建两个尝试更新数量的函数:
    • UpdateQuantityByValue 接收一个 CartItem(按值传递)和一个新数量,然后将商品的数量设为新值
    • UpdateQuantityByPointer 接收一个 *CartItem(指针)和一个新数量,然后将商品的数量设为新值
    还要为 CartItem 创建一个 Total 方法,返回价格乘以数量的结果。
  • main.go:从输入中读取商品信息,创建一个 CartItem,然后演示这两种更新方式。先尝试使用按值传递的函数更新并显示结果,再使用按指针传递的函数更新并再次显示结果。

输入内容如下:

  • 第 1 行:商品名称
  • 第 2 行:价格(浮点数)
  • 第 3 行:初始数量(整数)
  • 第 4 行:要设置的新数量(整数)

输出格式:

Initial: [Name] x[Quantity] = $[Total]
After value update: [Name] x[Quantity] = $[Total]
After pointer update: [Name] x[Quantity] = $[Total]

例如,输入 Laptop、999.99、1 和 3 时,输出应为:

Initial: Laptop x1 = $999.99
After value update: Laptop x1 = $999.99
After pointer update: Laptop x3 = $2999.97

注意,按值传递的函数更新后,数量仍为 1(该函数修改的是副本);而按指针传递的函数更新后,数量变为 3(该函数修改的是原始数据)。总价保留两位小数。

亲手试试

package main

import (
	"fmt"
)

func main() {
	// 读取输入
	var name string
	var price float64
	var initialQty, newQty int

	fmt.Scanln(&name)
	fmt.Scanln(&price)
	fmt.Scanln(&initialQty)
	fmt.Scanln(&newQty)

	// TODO: 用输入的值创建一个 CartItem

	// TODO: 按以下格式打印初始状态:
	// fmt.Printf("Initial: %s x%d = $%.2f\n", ...)

	// TODO: 尝试用 UpdateQuantityByValue 更新数量
	// 然后打印结果(注意数量不会改变)

	// TODO: 用 UpdateQuantityByPointer 更新数量
	// 然后打印结果(注意数量会改变)
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Go 在线编译器