Go语言中的值传递详解及实例演示
编辑:本站更新:2024-12-03 13:41:54人气:7636
在深入探讨 Go 语言的值传递机制前,首先需要明确的是:函数参数是通过“复制”而非引用的方式进行传参。这种特性使得对形参(即函数内部变量)的操作不会影响实参(调用时的实际输入)。接下来将详细解读这一核心概念,并结合具体示例进一步阐述。
### 值类型与指针类型的值传递
Go 中的基本数据类型如整型、浮点数和字符串等都是值类型,在作为函数参数被传递的时候会创建一份新的副本给到接收者函数中使用。例如:
package main
import "fmt"
func changeValue(x int) {
x = 10 // 对x赋新值不影响原始变量a
}
func main() {
a := 5
fmt.Println("Before call:", a)
changeValue(a)
fmt.Println("After call:", a)
}
上述代码运行后,“After call”的输出仍为 `5` 而非修改后的 `10` ,这是因为函数内的操作仅改变了局部作用域内拷贝的数值,原变量并未受影响。
然而对于复合类型比如切片或映射这类底层存储结构是指向堆内存的数据结构的情况,则稍微复杂一些——虽然它们也是按值传递,但这个"值"实际上是包含指向实际元素数组的一个地址。因此当我们改变这些内容时,会影响到原有的对象:
package main
import (
"fmt"
)
func modifySlice(s []int) {
s[0] = 99 // 改变 slice 元素会影响原有slice的内容
}
func main() {
arr := [3]int{1, 2, 3}
var sli = arr[:]
fmt.Printf("Original Slice: %v\n", sli)
modifySlice(sli)
fmt.Printf("Modified Slice: %v\n", sli)
}
此段程序执行结果表明,尽管 slices 是以值方式传递给了函数 `modifySlice()` ,但由于该值实际上是一个指向底层数组的指针,所以当我们在函数体内更改了其元素,外部观察仍然可以看到变化。
### 深入理解 - 使用指针实现可变性
若确实需让一个函数能直接修改某个基础类型的外层变量,可以通过传递对应类型的指针对其实现间接访问并修改:
package main
import "fmt"
func alterViaPointer(ptr *int) {
*ptr = 7 // 直接通过指针修改原始变量b的值
}
func main() {
b := 42
fmt.Println("Initial value of b is ", b)
.AlterViaPointer(&b)
fmt.Println("Altered the original variable through pointer. New Value of b is ", b)
}
在这个例子中,我们把变量 b 的地址当作参数传进去了,这样就可以透过指针直接更新原来的变量 b 。这就是所谓的 “按引用传递”,但在 Go 里实质上还是基于值语义的一种体现,这里的值就是指针本身。
总结来说,无论何时何地,Go 都是在做严格的值传递;即使是看起来像是引用行为的地方也仅仅是由于那些特定类型的设计使然(如切片、map 和通道),而并非因为有真正的引用或者指针引用来违反这一定律。了解这一点有助于编写出更加清晰且符合预期的行为的 Go 程序。
### 值类型与指针类型的值传递
Go 中的基本数据类型如整型、浮点数和字符串等都是值类型,在作为函数参数被传递的时候会创建一份新的副本给到接收者函数中使用。例如:
go
package main
import "fmt"
func changeValue(x int) {
x = 10 // 对x赋新值不影响原始变量a
}
func main() {
a := 5
fmt.Println("Before call:", a)
changeValue(a)
fmt.Println("After call:", a)
}
上述代码运行后,“After call”的输出仍为 `5` 而非修改后的 `10` ,这是因为函数内的操作仅改变了局部作用域内拷贝的数值,原变量并未受影响。
然而对于复合类型比如切片或映射这类底层存储结构是指向堆内存的数据结构的情况,则稍微复杂一些——虽然它们也是按值传递,但这个"值"实际上是包含指向实际元素数组的一个地址。因此当我们改变这些内容时,会影响到原有的对象:
go
package main
import (
"fmt"
)
func modifySlice(s []int) {
s[0] = 99 // 改变 slice 元素会影响原有slice的内容
}
func main() {
arr := [3]int{1, 2, 3}
var sli = arr[:]
fmt.Printf("Original Slice: %v\n", sli)
modifySlice(sli)
fmt.Printf("Modified Slice: %v\n", sli)
}
此段程序执行结果表明,尽管 slices 是以值方式传递给了函数 `modifySlice()` ,但由于该值实际上是一个指向底层数组的指针,所以当我们在函数体内更改了其元素,外部观察仍然可以看到变化。
### 深入理解 - 使用指针实现可变性
若确实需让一个函数能直接修改某个基础类型的外层变量,可以通过传递对应类型的指针对其实现间接访问并修改:
go
package main
import "fmt"
func alterViaPointer(ptr *int) {
*ptr = 7 // 直接通过指针修改原始变量b的值
}
func main() {
b := 42
fmt.Println("Initial value of b is ", b)
.AlterViaPointer(&b)
fmt.Println("Altered the original variable through pointer. New Value of b is ", b)
}
在这个例子中,我们把变量 b 的地址当作参数传进去了,这样就可以透过指针直接更新原来的变量 b 。这就是所谓的 “按引用传递”,但在 Go 里实质上还是基于值语义的一种体现,这里的值就是指针本身。
总结来说,无论何时何地,Go 都是在做严格的值传递;即使是看起来像是引用行为的地方也仅仅是由于那些特定类型的设计使然(如切片、map 和通道),而并非因为有真正的引用或者指针引用来违反这一定律。了解这一点有助于编写出更加清晰且符合预期的行为的 Go 程序。
www.php580.com PHP工作室 - 全面的PHP教程、实例、框架与实战资源
PHP学习网是专注于PHP技术学习的一站式在线平台,提供丰富全面的PHP教程、深入浅出的实例解析、主流PHP框架详解及实战应用,并涵盖PHP面试指南、最新资讯和活跃的PHP开发者社区。无论您是初学者还是进阶者,这里都有助于提升您的PHP编程技能。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。