非零基础自学Golang
第7章 函数
7.8 知识拓展
7.8.1 函数参数传递的本质
在讲述参数传递前,我们首先要了解两个基本概念:值传递和引用传递。
- 值传递:将变量的一个副本传递给函数,函数中不管如何操作该变量副本,都不会改变原变量的值。
- 引用传递:将变量的内存地址传递给函数,函数中操作变量时会找到保存在该地址的变量,对其进行操作,会改变原变量的值。
Go语言函数传入参数时使用的始终是值传递,对于值传递,Go语言主要分为以下两种情况:
- 对于int、string和bool等值类型变量,传递的是原变量的副本,对副本的操作不会影响原变量。
- 对于指针、切片、map和channel(通道)引用类型变量,传递的是原变量指针的一份副本,该副本指向了原变量地址,因此对该副本的操作会影响原变量,从而达到了其他编程语言中类似于引用传递的效果。
【懂了!】
下面我们通过实例来进行讲解。
[ 动手写 7.8.1 ]
package main
import "fmt"
func passByValue(numPara int) {
//值传递参数
fmt.Printf("passByValue 函数中变量的numPara地址为: %p\n", &numPara)
numPara = 100
}
func passByReference(numPara *int) {
// 引用传递函数
fmt.Printf("passByReference 函数中指针变量numPara 地址为: %p\n", &numPara)
fmt.Printf("passByReference 函数中指针变量numPara 指向的地址为: %p\n", numPara)
*numPara = 100
}
func main() {
num := 1
fmt.Printf("main 函数中变量num地址为: %p\n", &num)
passByValue(num)
fmt.Printf("num 变量值为: %d\n", num)
passByReference(&num)
fmt.Printf("num 变量值为: %d\n", num)
}
运行结果
从程序执行结果可以发现,num变量在传递到passByReference函数后,其原本的值发生了变化。
原因是其传递的是num变量的地址,该地址经过拷贝后传递给passByReference函数,但是其指向的值为num变量的地址,因此在passByReference函数中的“*numPara = 100”会影响原变量的值。
很清晰
类似地,使用map变量传递的是原变量的指针:
[ 动手写 7.8.2 ]
package main
import "fmt"
func passByReference(mapNumReference map[int]int) {
fmt.Printf("passByReference 函数中变量mapNumReference地址为: %p\n", mapNumReference)
fmt.Printf("passByReference 函数中变量mapNumReference 所属指针地址为: %p\n", &mapNumReference)
mapNumReference[1] = 100
}
func main() {
mapNum := map[int]int{
1: 10}
fmt.Printf("main 函数中变量mapNum 地址为: %p\n", mapNum)
fmt.Printf("main 函数中变量mapNum 所属指针的地址为: %p\n", &mapNum)
fmt.Printf("mapNum 变量值为: %d\n", mapNum)
passByReference(mapNum)
fmt.Printf("mapNum 变量值为: %d\n", mapNum)
}
运行结果
通过map类型变量参数传递的例子我们可以发现,函数进行值传递后拷贝的是map类型变量指针,但拷贝后的指针地址指向的还是map的地址,从而导致在函数passByReference中对map的操作会影响原变量。
7.8.2 Go内置函数
Go语言中自带了很多功能强大的内置函数,这些内置函数的定义都位于builtin.go文件中,使用时直接调用即可。
这里简要列出了Go的所有内置函数及其作用,如下表所示:
这里以对切片和复数的操作为应用场景,介绍几个Go内置函数及其作用。
我们创建sourceSlice切片,将元素4、5、6追加到该切片中,之后将其复制给targetSlice切片,输出切片的长度和容量。
[ 动手写 7.8.3 ]
package main
import "fmt"
func main() {
// 将4、5、6追加到 sourceSlice 切片中
sourceSlice := []int{
1, 2, 3}
sourceSlice = append(sourceSlice, 4, 5, 6)
fmt.Println("sourceSlice: ", sourceSlice)
// 将sourceSlice 切片中的元素复制到 targetSlice 切片中
targetSlice := make([]int, 3)
num := copy(targetSlice, sourceSlice)
fmt.Println("复制成功的元素个数为: ", num)
fmt.Println("targetSlice : ", targetSlice)
fmt.Println("targetSlice 切片长度为: ", len(targetSlice))
fmt.Println("targetSlice 切片容量为: ", cap(targetSlice))
}
运行结果
使用Go语言内置的complex函数可创建复数,并使用real函数和imag函数提取复数的实部与虚部。
[ 动手写 7.8.4]
package main
import "fmt"
func main() {
c1 := complex(1, 2)
fmt.Println(c1, "实部为: ", real(c1))
fmt.Println(c1, "虚部为: ", imag(c1))
}
运行结果
可以看到,在许多情况下,使用Go语言的内置函数可解决开发中的许多实际问题,避免自己使用代码重复实现相关逻辑。
【Go 66666】