Go语言中两种指针变量交换写法为何运行结果不同
Go语言两种指针交换实现的输出差异原理
问题复现
两段交换逻辑的代码运行结果存在明显偏差,完整可运行代码如下:
func main() { var a int = 10 var b int = 20 swap(&a, &b) fmt.Println(a, b) // 实际输出 20 20 var c int = 10 var d int = 20 swap2(&c, &d) fmt.Println(c, d) // 实际输出 20 10 } // 分步赋值版本 func swap(x, y *int) { *x = *y *y = *x } // 多重赋值版本 func swap2(x, y *int) { *x, *y = *y, *x }
差异核心原因
差异完全来自两种赋值逻辑的执行顺序规则不同:
分步赋值的执行逻辑
分步赋值是严格串行、按行顺序即时生效的:
- 执行
*x = *y时,立刻将y指向的内存值(初始为20)写入x指向的内存,此时x指向的变量值已经从原10被覆盖为20,原10的值不再保留在x指向的地址中 - 后续执行
*y = *x时,读取的*x已经是覆盖后的20,将这个值写入y指向的内存后,两个指针指向的地址最终存储的值都是20,因此输出20 20
多重赋值的执行逻辑
根据Go语言的语法规范,多重赋值的执行严格分为两个独立阶段,和分步赋值的即时生效逻辑完全不同:
- 第一阶段:对赋值语句左右两侧的所有操作数统一完成求值——包括计算左侧指针的指向地址、计算右侧所有指针解引用的值,所有求值结果都会被临时暂存,这个阶段不会对任何变量的内存值做修改
- 第二阶段:严格按照左侧操作数的排列顺序,将暂存的右侧值依次写入对应左侧操作数对应的内存地址
对应到swap2的执行流程:
- 第一阶段求值:拿到x的地址、y的地址;同时取出y的值20、x的值10,所有暂存信息为
写入地址:[x的地址, y的地址],待写入值:[20, 10],全程不会改动x、y指向的原内存值 - 第二阶段赋值:按顺序把20写入x的地址,把10写入y的地址
最终c的值为20,d的值为10,完成正确交换,因此输出20 10
补充说明
这种分阶段执行的多重赋值规则,是Go语言不引入显式临时变量即可完成值交换的核心设计基础,和Python、Rust等支持多重赋值的语言遵循相同的语义逻辑。
内容的提问来源于stack exchange,提问作者白觐瑄
相关产品推荐
相关产品推荐

