Go
stringslicemap
GoGoGo
1. 函数传参为数组
package main
import "fmt"
func main() {
srcArray := [3]string{"a", "b", "c"}
fmt.Printf("srcArray address is %p\n", &srcArray) // srcArray address is 0xc00005a150
modify(srcArray)
fmt.Printf("srcArray is %v\n", srcArray) // srcArray is [a b c]
}
func modify(modifyArr [3]string) [3]string {
fmt.Printf("modifyArr address is %p\n", &modifyArr) // modifyArr address is 0xc00005a180
modifyArr[1] = "x"
fmt.Printf("modifyArr is %v\n", modifyArr) // modifyArr is [a x c]
return modifyArr
}
0xc00005a1500xc00005a180
2. 函数传参为切片所有传给函数的参数值都会被复制,函数在其内部使用的并不是参数值的原值,而是它的副本。由于数组是值类型,所以每一次复制都会拷贝它,以及它的所有元素值
package main
import "fmt"
func main() {
srcSlice := []string{"a", "b", "c"}
fmt.Printf("srcSlice address is %p\n", srcSlice) // srcSlice address is 0xc00005a150
modify(srcSlice)
fmt.Printf("srcSlice is %v\n", srcSlice) // modifySlice is [a x c]
}
func modify(modifySlice []string) []string {
fmt.Printf("modifySlice address is %p\n", modifySlice) // modifySlice address is 0xc00005a150
modifySlice[1] = "x"
fmt.Printf("modifySlice is %v\n", modifySlice) // srcSlice is [a x c]
return modifySlice
}
0xc00005a150
srcSlice&&
3. 函数传参为字典map对于引用类型,比如:切片、字典、通道,像上面那样复制它们的值,只会拷贝它们本身而已,并不会拷贝它们引用的底层数据。也就是说,这时只是浅表复制,而不是深层复制。以切片值为例,如此复制的时候,只是拷贝了它指向底层数组中某一个元素的指针,以及它的长度值和容量值,而它的底层数组并不会被拷贝。
Gomapmakeruntime.makemap
makemapmakemapGo
makemap*hmapmap*hmap
src/runtime/map.go
// makemap implements Go map creation for make(map[k]v, hint).
func makemap(t *maptype, hint int, h *hmap) *hmap{
//省略无关代码
}
map
package main
import "fmt"
func main() {
srcMap := map[string]int{"a": 1, "b": 2, "c": 3}
fmt.Printf("srcMap address is %p\n", srcMap) // srcMap address is 0xc00005a150
modify(srcMap)
fmt.Printf("srcMap is %#v\n", srcMap) // srcMap is map[string]int{"a":1, "b":2, "c":100}
}
func modify(modifyMap map[string]int) map[string]int {
fmt.Printf("modifyMap address is %p\n", modifyMap) // modifyMap address is 0xc00005a150
modifyMap["c"] = 100
fmt.Printf("modifyMap is %#v\n", modifyMap) // modifyMap is map[string]int{"a":1, "b":2, "c":100}
return modifyMap
}
srcMapmodifyMap&&
map
4. 函数传参为 channel
channel
chan*hchanmap
func makechan(t *chantype, size int64) *hchan {
//省略无关代码
}
GomapchanmapchanGoslice
5. 函数传参为
package main
import "fmt"
type Student struct {
name string
age int
}
func main() {
s := Student{name: "wohu", age: 20}
fmt.Printf("s address is %p\n", &s) // s address is 0xc00000c060
modify(s)
fmt.Printf("s is %v\n", s) // s is {wohu 20}
}
func modify(stu Student) Student {
fmt.Printf("stu address is %p\n", &stu) // stu address is 0xc00000c080
stu.age = 30
fmt.Printf("stu is %v\n", stu) // stu is {wohu 30}
return stu
}
modifystumainstumodifystumain
Go
modifymodifystuGostupnameage
struct
指针类型的变量保存的值就是数据对应的内存地址,所以在函数参数传递是传值的原则下,拷贝的值也是内存地址。现在对以上示例稍做修改,修改后的代码如下:
package main
import "fmt"
type Student struct {
name string
age int
}
func main() {
s := Student{name: "wohu", age: 20}
fmt.Printf("s address is %p\n", &s) // s address is 0xc00000c060
modify(&s)
fmt.Printf("s is %v\n", s) // s is {wohu 30}
}
func modify(stu *Student) *Student {
fmt.Printf("stu address is %p\n", stu) // stu address is 0xc00000c060
stu.age = 30
fmt.Printf("stu is %v\n", *stu) // stu is &{wohu 30}
return stu
}
所以指针类型的参数是永远可以修改原数据的,因为在参数传递时,传递的是内存地址。
注意:值传递的是指针,也是内存地址。通过内存地址可以找到原数据的那块内存,所以修改它也就等于修改了原数据。
stustudentGostudent&stu*student
Gomapchan
6. 其它示例
直接上代码
package main
import "fmt"
func main() {
// 示例1。
array1 := [3]string{"a", "b", "c"}
fmt.Printf("The array: %v\n", array1)
array2 := modifyArray(array1)
fmt.Printf("The modified array: %v\n", array2)
fmt.Printf("The original array: %v\n", array1)
fmt.Println()
// 示例2。
slice1 := []string{"x", "y", "z"}
fmt.Printf("The slice: %v\n", slice1)
slice2 := modifySlice(slice1)
fmt.Printf("The modified slice: %v\n", slice2)
fmt.Printf("The original slice: %v\n", slice1)
fmt.Println()
// 示例3。
complexArray1 := [3][]string{
[]string{"d", "e", "f"},
[]string{"g", "h", "i"},
[]string{"j", "k", "l"},
}
fmt.Printf("The complex array: %v\n", complexArray1)
complexArray2 := modifyComplexArray(complexArray1)
fmt.Printf("The modified complex array: %v\n", complexArray2)
fmt.Printf("The original complex array: %v\n", complexArray1)
}
// 示例1。
func modifyArray(a [3]string) [3]string {
a[1] = "x"
return a
}
// 示例2。
func modifySlice(a []string) []string {
a[1] = "i"
return a
}
// 示例3。
func modifyComplexArray(a [3][]string) [3][]string {
a[1][1] = "s"
a[2] = []string{"o", "p", "q"}
return a
}
- 如果是进行一层修改,即数组的某个完整元素进行修改(指针变化),那么原有数组不变;
- 如果是进行二层修改,即数组中某个元素切片内的某个元素再进行修改(指针未改变),那么原有数据也会跟着改变,传参可以理解是浅copy,参数本身的指针是不同,但是元素指针相同,对元素指针所指向目的的操作会影响传参过程中的原始数据;
当变量被当做参数传入调用函数时,是值传递,也称变量的一个拷贝传递。如果传递过来的值是指针,就相当于把变量的地址作为参数传递到函数内,那么在函数内对这个指针所指向的内容进行修改,将会改变这个变量的值。如下边示例代码:
package main
import (
"fmt"
)
func demo(str *string) {
*str = "world"
}
func main() {
var str = "hello"
demo(&str)
fmt.Println("str value is:", str)
}
输出结果:
str value is: world
str str
package main
import (
"fmt"
)
var world = "hello wolrd"
func demo(str *string) {
str = &world
fmt.Println("str in demo func is:", *str)
}
func main() {
var str = "hello"
demo(&str)
fmt.Println("str in main func is:", str)
}
输出结果:
str in demo func is: hello wolrd
str in main func is: hello
strdemoworldstr
所以,在函数调用中,变量被拷贝了一份传入函数,函数调用结束后,拷贝的值被丢弃。
strdemodemostr
8. 综合示例
package main
import "fmt"
// 用于测试值传递效果的结构体,结构体是拥有多个字段的复杂结构。
type Data struct {
complax []int // complax 为整型切片类型,切片是一种动态类型,内部以指针存在。
instance InnerData // instance 成员以 InnerData 类型作为 Data 的成员 。
ptr *InnerData // 将 ptr 声明为 InnerData 的指针类型
}
// 代表各种结构体字段
type InnerData struct {
a int
}
// 值传递测试函数,该函数的参数和返回值都是 Data 类型。
// 在调用中, Data 的内存会被复制后传入函数,当函数返回时,又会将返回值复制一次,
// 赋给函数返回值的接收变量。
func passByValue(inFunc Data) Data {
// 输出参数的成员情况
fmt.Printf("inFunc value: %+v\n", inFunc)
// 打印inFunc的指针
fmt.Printf("inFunc ptr: %p\n", &inFunc)
// 将传入的变量作为返回值返回,返回的过程将发生值复制。
return inFunc
}
func main() {
// 准备传入函数的结构
in := Data{
complax: []int{1, 2, 3},
instance: InnerData{
5,
},
ptr: &InnerData{1},
}
// 输入结构的成员情况
fmt.Printf("in value: %+v\n", in)
// 输入结构的指针地址
fmt.Printf("in ptr: %p\n", &in)
// 传入结构体,返回同类型的结构体
out := passByValue(in)
// 输出结构的成员情况
fmt.Printf("out value: %+v\n", out)
// 输出结构的指针地址
fmt.Printf("out ptr: %p\n", &out)
}
输出结果:
in value: {complax:[1 2 3] instance:{a:5} ptr:0xc0000180e8}
in ptr: 0xc000078150
inFunc value: {complax:[1 2 3] instance:{a:5} ptr:0xc0000180e8}
inFunc ptr: 0xc0000781e0
out value: {complax:[1 2 3] instance:{a:5} ptr:0xc0000180e8}
out ptr: 0xc0000781b0
从运行结果中发现:
DataDataDataDataDataptr
参考:
https://juejin.cn/post/6844903618890432520
https://www.zhihu.com/question/312356800/answer/739572672
https://segmentfault.com/q/1010000019965306/a-1020000019996800
https://www.flysnow.org/2018/02/24/golang-function-parameters-passed-by-value.html
https://blog.csdn.net/wohu1104/article/details/109661126