GoLang学习笔记(十八)函数的传值和传引用

版权声明:如需转载、粘贴内容,必须在转载和粘贴文中标明文章内容出至 https://blog.csdn.net/ynzcxx/article/details/86497273

值传递:是指在调用函数时将实际参数复制一份传递到函数中,在函数中如果对值进行修改,不会影响到实际参数
引用传递:是指在调用函数时将实际参数的地址传递一份到函数中,那么在函数中对参数所进行的修改,将影响到实际参数
严格来说Go语言只有值传递一种传参方式,Go语言是没有引用传递的。
Go语言中可以借助传指针来实现引用传递的效果,函数参数使用指针参数,传参时其实是在拷贝一份指针参数,也就是拷贝了一份变量地址
Go语言中,string、int、bool、数组、stuct都属于非引用数据类型。
Go语言中,指针、Slice切片、map、chan都是引用数据类型,引用的时候也是类似指针地址。
传引用和引用类型是两个概念。

先来测试基本的string和int,写两个函数:

func changeIntVal(a int) {
	fmt.Printf("--------变量a的内存地址:%p,值为%v  \n", &a, a)
	a = 90
}

func changeIntPtr(a *int) {
	fmt.Printf("--------变量a的内存地址:%p,值为%v  \n", &a, a)
	*a = 60
}

func changeStringVal(str string) {
	fmt.Printf("--------变量str的内存地址:%p,值为%v  \n", &str, str)
	str = strings.ToUpper(str)
}

func changeStringPtr(str *string) {
	fmt.Printf("--------变量str的内存地址:%p,值为%v  \n", &str, str)
	*str = strings.ToUpper(*str)
}

然后在main()函数里调用:

a := 10
fmt.Printf("1、变量a的内存地址:%p,值为%v  \n", &a, a)
//传值
changeIntVal(a)
fmt.Printf("2、changeIntVal函数调用后,变量a的内存地址:%p,值为%v  \n", &a, a)
//传引用
changeIntPtr(&a)
fmt.Printf("3、changeIntPtr函数调用后,变量a的内存地址:%p,值为%v  ", &a, a)

str := "hello world"
fmt.Printf("1、变量str的内存地址:%p,值为%v  \n", &str, str)
//传值
changeStringVal(str)
fmt.Printf("2、changeStringVal函数调用后,变量str的内存地址:%p,值为%v  \n", &str, str)
//传引用
changeStringPtr(&str)
fmt.Printf("3、changeStringPtr函数调用后,变量str的内存地址:%p,值为%v  ", &str, str)

运行结果如下:

1、变量a的内存地址:0xc00004c080,值为10  
--------变量a的内存地址:0xc00004c0a0,值为10  
2、changeIntVal函数调用后,变量a的内存地址:0xc00004c080,值为10  
--------变量a的内存地址:0xc000074020,值为0xc00004c080  
3、changeIntPtr函数调用后,变量a的内存地址:0xc00004c080,值为60  

1、变量str的内存地址:0xc0000401c0,值为hello world  
--------变量str的内存地址:0xc0000401e0,值为hello world  
2、changeStringVal函数调用后,变量str的内存地址:0xc0000401c0,值为hello world  
--------变量a的内存地址:0xc000074028,值为0xc0000401c0  
3、changeStringPtr函数调用后,变量str的内存地址:0xc0000401c0,值为HELLO WORLD

可以看到在非引用类型数据,只能通过指针这种引用类型,才能改变原值的数据。

也可以看到值传递的时候,仅仅是将原值复制了一份,进行拷贝后,放在了一个新的内存地址中。

再比较一下两个非常相似的数据类型:数组(非引用数据类型)与切片(引用数据类型)

还是先写两个函数:

func changeArrayVal(ints [4]int) {
	fmt.Printf("--------变量ints的内存地址:%p,值为%v  \n", &ints, ints)
	ints[0] = 200
}

func changeArrayPtr(ints *[4]int) {
	fmt.Printf("--------变量ints的内存地址:%p,值为%v  \n", &ints, ints)
	(*ints)[0] = 300
}

func changeSliceVal(ints []int) {
	fmt.Printf("--------变量ints的内存地址:%p,值为%v  \n", &ints, ints)
	ints[0] = 99
}

func changeSlicePtr(ints *[]int) {
	fmt.Printf("--------变量ints的内存地址:%p,值为%v  \n", &ints, ints)
	(*ints)[1] = 199
}

还是在main()函数里进行调用:

ints := [4]int{1, 2, 3, 4}
fmt.Printf("1、变量a的内存地址:%p,值为%v  \n", &ints, ints)
//传值
changeArrayVal(ints)
fmt.Printf("2、changeArrayVal函数调用后,变量ints的内存地址:%p,值为%v  \n", &ints, ints)
//传引用
changeArrayPtr(&ints)
fmt.Printf("3、changeArrayPtr函数调用后,变量ints的内存地址:%p,值为%v  \n", &ints, ints)

ints1 := []int{1, 2, 3, 4}
fmt.Printf("1、变量a的内存地址:%p,值为%v  \n", &ints1, ints1)
//传值
changeSliceVal(ints1)
fmt.Printf("2、changeSliceVal函数调用后,变量ints1的内存地址:%p,值为%v  \n", &ints1, ints1)
//传引用
changeSlicePtr(&ints1)
fmt.Printf("3、changeSlicePtr函数调用后,变量ints1的内存地址:%p,值为%v  \n", &ints1, ints1)

运行结果如下:

1、变量ints的内存地址:0xc00004a0c0,值为[1 2 3 4]  
--------变量ints的内存地址:0xc00004a100,值为[1 2 3 4]  
2、changeArrayVal函数调用后,变量ints的内存地址:0xc00004a0c0,值为[1 2 3 4]  
--------变量ints的内存地址:0xc000074030,值为&[1 2 3 4]  
3、changeArrayPtr函数调用后,变量ints的内存地址:0xc00004a0c0,值为[300 2 3 4]
  
1、变量ints的内存地址:0xc000046420,值为[1 2 3 4]  
--------变量ints的内存地址:0xc000046460,值为[1 2 3 4]  
2、changeSliceVal函数调用后,变量ints的内存地址:0xc000046420,值为[99 2 3 4]  
--------变量ints的内存地址:0xc000074038,值为&[99 2 3 4]  
3、changeSlicePtr函数调用后,变量ints的内存地址:0xc000046420,值为[99 199 3 4] 

可以看到数组这种非引用数据类型和前面两个非引用数据类型是一样的,不能通过函数传值的方式进行修改。

而切片则不同,切片本身就是引用类型,所以直接就能修改。

猜你喜欢

转载自blog.csdn.net/ynzcxx/article/details/86497273
今日推荐