切片传递与指针传递的区别

疑问:切片底层不就是指针指向底层数组数据吗,为何不直接传递切片,两者有什么区别?

在源码log包中,Logger对象上绑定了formatHeader方法,它的入参对象buf,其类型是*[]byte,而非[]byte

 

func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {}

 

func modifySlice(innerSlice []string) {

        innerSlice[0] = "b"

        innerSlice[1] = "b"

        fmt.Println(innerSlice)

}

 

func main() {

        outerSlice := []string{"a", "a"}

        modifySlice(outerSlice)

        fmt.Print(outerSlice)

}

 

输出结果如下:

[b b]

[b b]

将modifySlice函数的入参类型改为指向切片的指针

 

func modifySlice(innerSlice *[]string) {

        (*innerSlice)[0] = "b"

        (*innerSlice)[1] = "b"

        fmt.Println(innerSlice)

}

 

func main() {

        outerSlice := []string{"a", "a"}

        modifySlice(&outerSlice)

        fmt.Print(outerSlice)

}

 

输出结果:

&[b b]

[b b]

    在上面的例子中,两种函数传参类型得到的结果都一样,似乎没发现有什么区别。通过指针传递它看起来毫无用处,而且无论如何切片都是通过引用传递的,在两种情况下切片内容都得到了修改。

 

这个印证了我们

一贯的认知:函数内对切片的修改,将会影响到函数外的切片。但,真的是如此吗?

 

type slice struct {
    array unsafe.Pointer
    len  
int
    cap   int
}

 

array是底层数组的指针,len表示长度,cap表示容量。

func modifySlice(innerSlice []string) {

        innerSlice = append(innerSlice, "a")

        innerSlice[0] = "b"

        innerSlice[1] = "b"

        fmt.Println(innerSlice)

}

 

func main() {

        outerSlice := []string{"a", "a"}

        modifySlice(outerSlice)

        fmt.Print(outerSlice)

}

 

输出:

[b b a]

[a a]

神奇的事情发生了,函数内对切片的修改竟然没能对外部切片造成影响?为了清晰地明白发生了什么,将打印添加更多细节

 

func modifySlice(innerSlice []string) {

        fmt.Printf("%p %v %p\n", &innerSlice, innerSlice, &innerSlice[0])

        innerSlice = append(innerSlice, "a")

        innerSlice[0] = "b"

        innerSlice[1] = "b"

        fmt.Printf("%p %v %p \n", &innerSlice, innerSlice, &innerSlice[0])

}

 

func main() {

        outerSlice := []string{"a", "a"}

        fmt.Printf("%p %v %p \n", &outerSlice, outerSlice, &outerSlice[0])

        modifySlice(outerSlice)

        fmt.Printf("%p %v %p \n", &outerSlice, outerSlice, &outerSlice[0])

}

 

输出结果如下:

0xc000004480 [a a] 0xc0000044a0

0xc000004500 [a a] 0xc0000044a0

0xc000004500 [b b a] 0xc000020080

0xc000004480 [a a] 0xc0000044a0

       在Go函数中,函数的参数传递均是值传递。那么,将切片通过参数传递给函数,其实质是复制了slice结构体对象,两个slice结构体的字段值均相等。正常情况下,由于函数内slice结构体的array和函数外slice结构体的array指向的是同一底层数组,所以当对底层数组中的数据做修改时,两者均会受到影响。

但是存在这样的问题:如果指向底层数组的指针被覆盖或者修改(copy、重分配、append触发扩容),此时函数内部对数据的修改将不再影响到外部的切片,代表长度的len和容量cap也均不会被修改。

 

func modifySlice(innerSlice *[]string) {

        //fmt.Printf("%p %v %p\n", &innerSlice, innerSlice, &innerSlice[0])

        *innerSlice = append(*innerSlice, "a")

        (*innerSlice)[0] = "b"

        (*innerSlice)[1] = "b"

        //fmt.Printf("%p %v %p \n", &innerSlice, innerSlice, &innerSlice[0])

        fmt.Println(*innerSlice)

}

 

func main() {

        outerSlice := []string{"a", "a"}

        //fmt.Printf("%p %v %p \n", &outerSlice, outerSlice, &outerSlice[0])

        modifySlice(&outerSlice)

        //fmt.Printf("%p %v %p \n", &outerSlice, outerSlice, &outerSlice[0])

        fmt.Print(outerSlice)

}

 

输出结果

[b b a]

[b b a]

 

func modifySlice(innerSlice []string) {

        innerSlice = append(innerSlice, "a")

        innerSlice[0] = "b"

        innerSlice[1] = "b"

        fmt.Println(innerSlice)

}

 

func main() {

        outerSlice := make([]string, 0, 3)

        outerSlice = append(outerSlice, "a", "a")

        modifySlice(outerSlice)

        fmt.Println(outerSlice)

}

输出结果:

[b b a]

[b b]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值