• Post author:
  • Post category:后端
  • Post comments:0评论
  • Reading time:3 mins read

最近在学习golang,对指针方面的一些概念有些迷惑,下了些功夫把相关的知识学习了一下,这里做个总结。

主要还是关于值传递和引用传递方面的东西,基本可以分为4种,只论结果,其他的网上已经说烂了。

值类型值传递

无论是golang、java、c#,值类型在作为参数传递时,都是深拷贝直接传递值的,因为值类型的内存分配在栈中,内存地址直接保存了值类型的真实值,在作为参数传递的时候,直接拷贝其值,所以在方法体内部修改时,不会对外部产生影响,当用其对另一个变量赋值时,也是直接拷贝其值,修改新的变量不会对旧变量产生影响。

package main

func foo(x int) {
	x = 100
}

func main() {
	x := 1
	foo(x)
	println(x) // 1

	y := x
	y = 200
	println(x) // x is still 1
	println(y) // y is now 200
}

值类型引用传递

值类型的引用传递实际上就是强行搞一个对值类型的引用,像上述那样用等号赋值是不行的,那样会直接拷贝一个新的变量,在c#中,我们可以通过ref关键字来创建并传递值类型的引用,如果需要直接创建值类型的引用,则需要像如下方式进行拆装箱:

int x = 1;
int y = (int)(object)x;

而在golang等有指针的语言下,可以直接通过&关键字获取值类型的指针,在使用指针作为值类型的引用,进行赋值和传参时,需要注意的是,只有修改其内部值有效,而直接将该引用指向新的内存地址是无效的:

package main

type someStruct struct {
	value int
}

func foo1(x *someStruct) {
	x.value = 100
}

func foo2(y *someStruct) {
	y = &someStruct{100}
}

func main() {
	x := someStruct{1}
	foo1(&x)
	println(x.value) // x.value is 100

	y := someStruct{1}
	foo2(&y)
	println(y.value) // y.value is 1
}

这是因为修改时是直接对值的内存进行修改,如果你将引用的指向改变为新的内存,那对于方法外部则是无感的。

引用类型值传递

对于引用类型的值传递,就不得不说一句:“一切传引用本质都是传值”,是不是有点迷惑了,这就要先理解引用类型的变量里面存的是啥了,引用类型的变量的值,实际上存的是实际变量的内存地址,也就是引用,比如object x = new object(),x里面存的实际上是object的内存地址,当作为参数传递的时候,会将x内存储的指,也就是object的引用传递给方法体内部,同时和值类型的引用传递一样,当你改变引用的实际地址时,不会对外部生效,如果仅进行修改,则会对外部生效:

package main


import "fmt"

func foo1(someMap map[string]string) {
	someMap["a"] = "2"
}

func foo2(someMap map[string]string) {
	someMap = make(map[string]string)
}

func main() {
	someMap1 := make(map[string]string)
	someMap1["a"] = "1"
	foo1(someMap1)
	fmt.Println(someMap1["a"]) // "2"

	someMap2 := make(map[string]string)
	someMap2["b"] = "2"
	foo2(someMap2)
	fmt.Println(someMap2["b"]) // "2"
}

可以看到foo2方法内对someMap2重新赋值的操作没有生效,但foo1方法内对someMap1修改value的操作生效了。

引用类型引用传递

引用类型的引用传递实际上就是传递引用的内存地址的真实的值,听起来像绕口令一样,实际上可以理解为就是把真实的内存地址传递到方法体了,简单来说就是引用类型值传递传递的是引用本身的值,而引用类型引用传递传递的则是真实的值,无论是修改还是改变真实的值都会影响到外部,注意这里的改变都是基于原内存地址的改变,实际上改变的是真实内存地址存储的值:

package main

import "fmt"

func foo1(someMap *map[string]string) {
	(*someMap)["a"] = "1"
}

func foo2(someMap *map[string]string) {
	(*someMap) = make(map[string]string)
	fmt.Printf("someMap改变后的实际地址 %p \r\n", someMap)   // 0xc000006030
	fmt.Printf("someMap的引用变量地址  %p  \r\n", *someMap) // 0xc00007c4b0
}

func main() {
	someMap1 := make(map[string]string)
	someMap1["a"] = "1"
	foo1(&someMap1)
	fmt.Println(someMap1["a"]) // 1

	someMap2 := make(map[string]string)
	someMap2["b"] = "2"
	fmt.Printf("someMap改变前的实际地址 %p  \r\n", &someMap2) // 0xc000006030
	foo2(&someMap2)
	fmt.Println(someMap2["b"]) // ""
}

葫芦

葫芦,诞生于1992年8月11日,游戏宅,胶佬,爱好摸鱼,一个干过超市收银,工地里搬过砖,当过广告印刷狗,做过电焊铁艺的现役.Net程序员。

发表回复