Close

Go语言在参数传递中深拷贝浅拷贝的问题

Go语言深拷贝浅拷贝

最近在写代码的时候发现有一个bug,是跟深拷贝浅拷贝有关系的

之前看了
Go 语言设计与实现 – 面向信仰编程

已经讲的非常详细了,分为两种

传递过程中参数是int类型和数组

func myFunction(i int, arr [2]int) {
    i = 29
    arr[1] = 88
    fmt.Printf("in my_funciton - i=(%d, %p) arr=(%v, %p)\\n", i, &i, arr, &arr)
}

$ go run main.go
before calling - i=(30, 0xc000072008) arr=([66 77], 0xc000072010)
in my_funciton - i=(29, 0xc000072028) arr=([66 88], 0xc000072040)
after  calling - i=(30, 0xc000072008) arr=([66 77], 0xc000072010)

首先是在传递过程中参数是int类型和数组

可以发现前后没有任何变化

所以go语言在整型和数组类型传递过程都是值传递的

传递过程中参数是结构体和结构体指针

type MyStruct struct {
    i int
}

func myFunction(a MyStruct, b *MyStruct) {
    a.i = 31
    b.i = 41
    fmt.Printf("in my_function - a=(%d, %p) b=(%v, %p)\\n", a, &a, b, &b)
}

func main() {
    a := MyStruct{i: 30}
    b := &MyStruct{i: 40}
    fmt.Printf("before calling - a=(%d, %p) b=(%v, %p)\\n", a, &a, b, &b)
    myFunction(a, b)
    fmt.Printf("after calling  - a=(%d, %p) b=(%v, %p)\\n", a, &a, b, &b)
}

$ go run main.go
before calling - a=({30}, 0xc000018178) b=(&{40}, 0xc00000c028)
in my_function - a=({31}, 0xc000018198) b=(&{41}, 0xc00000c038)
after calling  - a=({30}, 0xc000018178) b=(&{41}, 0xc00000c028)

当在参数中传递的是结构体和结构体指针的时候

可以看到a.i在进入参数的前后都没有发生变化

但是b.i在进入参数的前后发生了变化

从这里我们可以看出

  • 传递结构体时:会拷贝结构体中的全部内容;
  • 传递结构体指针时:会拷贝结构体指针;

这里就引出了我碰到的问题

遇到的bug

func (sqli *SqlInjection) GenerateTimePayloads(payloadTemplate string, OriginDict interface{}) [][]fakehttp.Request {

先看我在代码中的函数调用,一个是 string这个是值传递肯定没有问题

但是这个interface{}在参数中是怎么传递的呢

根据上面的结论不管传进去的是结构体还是别的,因为不是指针然后肯定会拷贝结构体中的内容在传递

所以我就这样写了newDict := OriginDict.(url.Values)

结果发现里面的值不断在增加,debug的时候一直不明白为什么,后来想到可能interface{}传递的时候是指针,这里我们就来测试一下

先用interface来传递结构体指针和结构体看看有没有问题

因为不用想在传递数组int肯定是值传递的

type MyStruct struct {
    i int
}

func myFunction(t interface{}, OriginDict interface{}) {
    a := t.(MyStruct)
    a.i = 31
    b := OriginDict.(*MyStruct)
    b.i = 41
    fmt.Printf("in my_function - a=(%s, %p) b=(%v, %p)\\n", a, &a, b, &b)
}

func main() {
    a := MyStruct{i: 40}
    b := &MyStruct{i: 40}
    fmt.Printf("before calling - a=(%s, %p) b=(%v, %p)\\n", a, &a, b, &b)
    myFunction(a, b)
    fmt.Printf("after calling  - a=(%s, %p) b=(%v, %p)\\n", a, &a, b, &b)
}

[Running] go run "/Users/apple/Desktop/github/扫描器/Gondar/scanner/test/tesst.go"
before calling - a=({%!s(int=40)}, 0x140000180f8) b=(&{40}, 0x1400000e028)
in my_function - a=({%!s(int=31)}, 0x14000018110) b=(&{41}, 0x1400000e038)
after calling  - a=({%!s(int=40)}, 0x140000180f8) b=(&{41}, 0x1400000e028)

诶发现产生的结果和之前的一模一样说明传递的时候还是按照是否结构体指针来传递的

难道我的想法有问题吗

看一下发现我传递的是个字典,所以我也传递一个字典试一下

https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/52768c17fa7b4a2ebcbca1535963d93b~tplv-k3u1fbpfcp-watermark.image

type MyStruct struct {
    i int
}

type test map[string][]string

func myFunction(t interface{}, OriginDict interface{}) {
    a := t.(test)
    a["1"] = []string{"123"}
    b := OriginDict.(MyStruct)
    b.i = 41
    fmt.Printf("in my_function - a=(%s, %p) b=(%v, %p)\\n", a, &a, b, &b)
}

func main() {
    var a test
    a = test{
        "0": []string{"123"},
    }
    b := MyStruct{i: 40}
    fmt.Printf("before calling - a=(%v, %p) b=(%v, %p)\\n", a, &a, b, &b)
    myFunction(a, b)
    fmt.Printf("after calling  - a=(%v, %p) b=(%v, %p)\\n", a, &a, b, &b)
}

[Running] go run "/Users/apple/Desktop/github/扫描器/Gondar/scanner/test/tesst.go"
before calling - a=(map[0:[123]], 0x1400000e028) b=({40}, 0x140000180f8)
in my_function - a=(map[0:[123] 1:[123]], 0x1400000e038) b=({41}, 0x14000018100)
after calling  - a=(map[0:[123] 1:[123]], 0x1400000e028) b=({40}, 0x140000180f8)

问题复原了,所以在用interface{}传参过程中吐过是map的时候是拷贝了指针进行传递的,导致原来的值产生了变化

那么如果不是interface而是map直接在参数中传递的时候呢

type MyStruct struct {
    i int
}

func myFunction(a map[string][]string, OriginDict interface{}) {
    a["1"] = []string{"123"}
    b := OriginDict.(MyStruct)
    b.i = 41
    fmt.Printf("in my_function - a=(%s, %p) b=(%v, %p)\\n", a, &a, b, &b)
}

func main() {
    a := map[string][]string{
        "0": []string{"123"},
    }
    b := MyStruct{i: 40}
    fmt.Printf("before calling - a=(%v, %p) b=(%v, %p)\\n", a, &a, b, &b)
    myFunction(a, b)
    fmt.Printf("after calling  - a=(%v, %p) b=(%v, %p)\\n", a, &a, b, &b)
}

[Running] go run "/Users/apple/Desktop/github/扫描器/Gondar/scanner/test/tesst.go"
before calling - a=(map[0:[123]], 0x1400000e028) b=({40}, 0x140000180f8)
in my_function - a=(map[0:[123] 1:[123]], 0x1400000e038) b=({41}, 0x14000018100)
after calling  - a=(map[0:[123] 1:[123]], 0x1400000e028) b=({40}, 0x140000180f8)

结果是跟上一个一模一样的,说明参数interface{}在传递过程中,传递的过程还是和本身类型有关一样

说明有问题的是map类型,他在传递的过程中使用的是指针传递,是浅拷贝如果改变了值会对原来的值产生变化

总结一下

  • 传递结构体、int、string、数组时:深拷贝;
  • 传递结构体指针、map时:浅拷贝;

参考链接

https://draveness.me/golang/docs/part2-foundation/ch04-basic/golang-function-call/#412-参数传递

Leave a Reply

Your email address will not be published. Required fields are marked *