go语言传参都是值传递

请记住,go语言中所有的传参都是值传递,新拷贝了一个副本。

对于非引用类型的传参,是拷贝了一份新数据,修改新数据不会影响旧数据;对于引用类型(slice、map、channel…)来说,传参虽然是新拷贝了一份数据,但自身底层数据结构含有指针指向的是同一个内存地址,若修改新数据,原来的内容也会变。

什么是值传递?

传递的是原数据的副本,两个数据的地址不同

什么是引用传递?

传递的是直接是地址,这就会出现修改新数据,原数据也会改变

下面我们来看一些例子

int类型
传参后的变量地址与原始地址不同,并且不能对变量修改,若使用指针的话就可以

import "fmt"

func update(i int) {
	fmt.Printf("函数内变量i的地址:%p\n", &i)
	i = 55
}

func main() {
	i := 5
	fmt.Printf("原始变量i的地址:%p\n", &i)
	update(i)
	fmt.Printf("改动后的值:%v\n", i)
}

原始变量i的地址:0xc0000140e8
函数内变量i的地址:0xc000014110
改动后的值:5

再来看一下指针类型的例子
可以看出传参前后指针指向地址是同一地址,所以可以修改原数据

package main

import "fmt"

func update(i *int) {
	fmt.Printf("函数内指针的地址:%p\n", &i)
	fmt.Printf("函数内指针指向变量i的地址:%p\n", i)
	*i = 55
}

func main() {
	i := 5
	n := &i
	fmt.Printf("原始指针的地址:%p\n", &n)
	fmt.Printf("原始指针指向变量i的地址:%p\n", n)
	update(n)
	fmt.Printf("改动后的值:%v\n", n)
}
原始指针的地址:0xc000006028
原始指针指向变量i的地址:0xc0000140e8
函数内指针的地址:0xc000006038         
函数内指针指向变量i的地址:0xc0000140e8
改动后的值:55

slice切片类型
切片底层的结构就是一个指向数组的指针,长度len,容量cap,slice变量的地址其实就是内部存储数组元素的地址

package main

import "fmt"

func update(i []int) {
	fmt.Printf("函数内切片的地址:%p\n", i)
	fmt.Printf("函数内切片的第一个元素的地址:%p\n", &i[0])
	i[0] = 55
}

func main() {
	arr := []int{1, 2, 3}
	fmt.Printf("原始切片的地址:%p\n", arr)
	fmt.Printf("原始切片的第一个元素的地址:%p\n", &arr[0])
	update(arr)
	fmt.Printf("改动后的值:%v\n", arr)
}
原始切片的地址:0xc00000c150
原始切片的第一个元素的地址:0xc00000c150
函数内切片的地址:0xc00000c150            
函数内切片的第一个元素的地址:0xc00000c150
改动后的值:[55 2 3]

map类型
使用make初始化map,内部其实传的是*hmap,修改后来的数据也会改变旧数据

package main

import "fmt"

func update(i map[int]string) {
	fmt.Printf("函数内map的地址:%p\n", &i)
	i[0] = "pear"
}

func main() {
	m := make(map[int]string)
	m[0] = "monkey"
	fmt.Printf("原始map的地址:%p\n", &m)
	update(m)
	fmt.Printf("改动后的值:%v\n", m)
}
原始map的地址:0xc000006028
函数内map的地址:0xc000006038
改动后的值:map[0:pear]

参考runtime包中map.go

// makemap implements Go map creation for make(map[k]v, hint).(注意这一句!!!)
// If the compiler has determined that the map or the first bucket
// can be created on the stack, h and/or bucket may be non-nil.
// If h != nil, the map can be created directly in h.
// If h.buckets != nil, bucket pointed to can be used as the first bucket.
func makemap(t *maptype, hint int, h *hmap) *hmap {
	mem, overflow := math.MulUintptr(uintptr(hint), t.bucket.size)
	if overflow || mem > maxAlloc {
		hint = 0
	}

	// initialize Hmap
	if h == nil {
		h = new(hmap)
	}
	h.hash0 = fastrand()

	// Find the size parameter B which will hold the requested # of elements.
	// For hint < 0 overLoadFactor returns false since hint < bucketCnt.
	B := uint8(0)
	for overLoadFactor(hint, B) {
		B++
	}
	h.B = B

	// allocate initial hash table
	// if B == 0, the buckets field is allocated lazily later (in mapassign)
	// If hint is large zeroing this memory could take a while.
	if h.B != 0 {
		var nextOverflow *bmap
		h.buckets, nextOverflow = makeBucketArray(t, h.B, nil)
		if nextOverflow != nil {
			h.extra = new(mapextra)
			h.extra.nextOverflow = nextOverflow
		}
	}

	return h
}
划重点!!!
  1. 在Go语言里,虽然只有值传递,但是我们也可以修改原内容数据,只要参数是引用类型即可
  2. 引用传递跟引用类型是两个概念(若在面视中遇到该问题,一定要听清面试官的问题!!)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值