Skip to content

值类型

Go 当中的基本数据结构,包括 int, float, complex, string, bool 在内的所有类型,都是值类型。在传递时,它会对值进行拷贝,而不是直接传递内存地址。

int 类型来具体看看是什么情况。

go
func test_data_type1(){
	x:= 1
	y :=x
	z := &x
	x++
	fmt.Printf("%d and %d and %d",x,y,*z)
}

这里让 x 的值一开始为 1,y 拿到 x 的值,z 拿到 x 的地址。然后 x 的值加 1 后,y 仍然是复制时的值 1,而 z 的值是 2,因为在最后打印时,它取的是这个地址上的值,也就等价于 x 的值。

打印结果:

2 and 1 and 2

我们的自定义结构体只要只使用值类型,效果是一样的。

用一个通道来做示例,一个协程发送过去,另一个协程收到后修改,在本地协程做打印。

go
type Person struct{
	Name string
	Age int
}
var pch chan Person
func test_data_type() {
	var wg sync.WaitGroup
	pch = make(chan Person)

	// 第一个协程,用于发送数据
	wg.Add(1)
	go func() {
		defer wg.Done()
		person := Person{
			"huashen",
			21,
		}

		// 向通道发送数据
		fmt.Println("Sender: Sending data...")
		pch <- person
		fmt.Println("Sender: Data sent.")

		// 关闭通道
		close(pch)

		// 等待接收方处理,这里使用短暂停顿来确保接收方有足够时间处理
		time.Sleep(10 * time.Millisecond)

		// 打印原始数据,注意这里是协程内部的 person,不受外部协程影响
		fmt.Printf("Sender: Original person is %s, %d\n", person.Name, person.Age)
	}()

	// 第二个协程,用于接收数据并修改
	wg.Add(1)
	go func() {
		defer wg.Done()
		// 从通道接收数据
		fmt.Println("Receiver: Waiting for data...")
		p := <-pch
		fmt.Println("Receiver: Data received.")

		// 修改接收到的数据
		p.Name = "huashen2"
		p.Age = 22
		fmt.Printf("Receiver: Modified person to %s, %d\n", p.Name, p.Age)
	}()

	// 等待所有协程完成
	wg.Wait()
	fmt.Println("All goroutines finished.")
}

打印结果是:

Receiver: Waiting for data...
Sender: Sending data...
Sender: Data sent.
Receiver: Data received.
Receiver: Modified person to huashen2, 22
Sender: Original person is huashen, 21
All goroutines finished.

本地的 person 并没有被修改。因为发到通道里本质上是值的副本

引用类型

我们用一个切片的例子来做对比:

go
func modifySlice(c chan []int) {
	s := <-c
	fmt.Println("接收方收到 slice:", s)
	
	s[0] = 99
	fmt.Println("接收方修改 slice 后:", s)
	
	time.Sleep(1 * time.Second)
	
	close(c)
}

func slice_test() {
	mySlice := []int{10, 20, 30}
	fmt.Println("发送方原始 slice:", mySlice)

	c := make(chan []int)
	
	go modifySlice(c)

	c <- mySlice
	
	// 等待 channel 关闭
	_, ok := <-c
	for ok {
		_, ok = <-c
	}
	
	fmt.Println("发送方在接收方修改后,本地 slice 变为:", mySlice)
}

得到输出:

发送方原始 slice: [10 20 30]
接收方收到 slice: [10 20 30]
接收方修改 slice 后: [99 20 30]
发送方在接收方修改后,本地 slice 变为: [99 20 30]

可以看到,在接收方修改后,本地的 slice 数据也发生了变化。这是因为 slice, map, channel 都是包含指针的复合类型,它们在传递时会把指针传递过去。所以,修改了接收方的 slice,也就修改了发送方的 slice