Go中的赋值:值语义还是引用语义?

本文深入探讨了Go语言中的赋值机制,分析了基本类型、复合类型如数组、切片、map等的赋值行为,并通过实例说明了这些类型在函数间传递时的具体表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 问题的提出

赋值时是值语义还是引用语义对程序的行为有很大影响,如果我们想要某个函数对一个变量的修改对外部不可见,我们希望值语义的赋值,反之,若我们想要某个函数对一个变量的修改是全局可见时,我们希望引用语义的赋值。那么Go中的赋值究竟是值语义还是引用语义?

2. Go中的赋值

在Go中大部分类型的赋值是值类型,包括:

  1. 基本类型,如byte、int、bool、float32、float64和string等;
  2. 复合类型,如数组(array)、结构体(struct)和指针(pointer)等。

可以看到,数组也是值语义的赋值,因此Go中的值语义赋值表现得非常彻底。

2.1 基本类型的赋值

这个很好理解,传入函数的变量与外部变量的地址不同,这与C中保持一致。

2.2 复合类型的赋值

对于从C转向Go的小伙伴来说,这一部分需要详细叙述。

2.2.1 数组的赋值

Go中的数组是很存粹的值类型,例如:

var a = [3]int{1, 2, 3} 
var b = a 
b[1]++ 
fmt.Println(a, b)

程序的运行结果是:

[1 2 3] [1 3 3]

这表明赋值时发送了完整的数组赋值。

2.2.2 map和slice的赋值

这两个数据类型在C中并不存在,它们的赋值同样是值语义,但是有时又会表现出类似引用语义的行为。例如:

var a = make([]int, 3)
a[0] = 0
a[1] = 1
a[2] = 2
var b = a 
b[1]++ 
fmt.Println(a, b)

程序的结果为

[0 2 2] [0 2 2]

这个表现与数组的赋值表现并不一样,实际上可以把切片类型看成下面的结构体:

type slice struct { 
 first *T 
 len int 
 cap int 
}

由于ab有值相同值的内部指针T,因此二者对任一元素的修改都对对方可见。
map类型可以看成是字典指针,因此对字典添加、删除、修改对二者可见:

type Map_K_V struct { 
 // ... 
} 
type map[K]V struct { 
 impl *Map_K_V 
}

3. 一个复杂的例子

看下面的程序,提前想想地址是否有变化

package main

// Tx 
type Tx struct {
	a int
	ns []string
}

// Graph 
type Graph struct {
	ii int
	nums []int
	tos map[string] string
	tx Tx
}

func foo(gra Graph) {
	println(&gra.ii)
	println(&gra.nums[0], &gra.nums[1], &gra.nums[2])
	println(&gra.tos)
	println(&gra.tx)
	println(&gra.tx.a)
	println(&gra.tx.ns[0], &gra.tx.ns[1])
}
  
func main() {
	gra := Graph{1, make([]int, 3), make(map[string]string), Tx{1, make([]string, 2)}}
	gra.tos["1"] = "1"
	println(&gra.ii)
	println(&gra.nums[0], &gra.nums[1], &gra.nums[2])
	println(&gra.tos)
	println(&gra.tx)
	println(&gra.tx.a)
	println(&gra.tx.ns[0], &gra.tx.ns[1])
	foo(gra)
}

该程序的执行结果为:
各类型变量地址变化

  1. int类型变量的地址改变
  2. 切片类型的元素地址不变
  3. map类型变量本身的地址改变
  4. 结构体变量本身地址改变
  5. 属于内部结构体的变量可以直接看成是外部结构体的变量,因此地址变化仍然满足部分2的规则:int类型变量的地址改变,切片类型的元素地址不变。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值