Cyrus Flag

flag{S0_bangbang_7ha7_u_f1nd_h3r3}

GO传参技巧

在经历和 C/C++ 和 Python 的函数传参坑之后,决定在学 Go 之前先把坑都踩了。

本文主要包括

切片Slice的传参方式,集合Map的传参方式,基本的字符串String和数值Int的传参方式。

代码样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package main

import "fmt"

func f1(arr []int) int {
res := 0
for i:=0; i<len(arr); i++ {
res += arr[i]
arr[i]++
}
return res
}

func f2(arr []int) int {
res := 0
for _,i := range arr {
res += i
i++
}
return res
}

func f3(arr []int) int {
res := 0
for i,_ := range arr {
res += arr[i]
arr[i]++
}
return res
}

func f4(arr []int) int {
tarr := make([]int, len(arr), cap(arr))
copy(tarr, arr)
res := 0
for i:=0; i<len(tarr); i++ {
res += tarr[i]
tarr[i]++
}
return res
}

func f5(arr []int) int {
tarr := make([]int, len(arr), cap(arr))
tarr = arr
res := 0
for i:=0; i<len(tarr); i++ {
res += tarr[i]
tarr[i]++
}
return res
}

func f6(arr map[string]int) int {
res := 0
for _,i := range arr {
res += i
i++
}
return res
}

func f7(arr map[string]int) int {
res := 0
for i,_ := range arr {
res += arr[i]
arr[i]++
}
return res
}

func f8(arr map[string]int) int {
res := 0
tarr := make(map[string]int)
tarr = arr
for i,_ := range tarr {
res += tarr[i]
tarr[i]++
}
return res
}

func f9(x string, y int) (string, int) {
x = "shit"
y += 1
return x, y
}

func main() {
a1 := []int {1,2,3,4,5}
fmt.Printf("%v => ", a1)
b1 := f1(a1)
fmt.Printf("%v / RES = %d\n", a1, b1)

a2 := []int {1,2,3,4,5}
fmt.Printf("%v => ", a2)
b2 := f2(a2)
fmt.Printf("%v / RES = %d\n", a2, b2)

a3 := []int {1,2,3,4,5}
fmt.Printf("%v => ", a3)
b3 := f3(a3)
fmt.Printf("%v / RES = %d\n", a3, b3)

a4 := []int {1,2,3,4,5}
fmt.Printf("%v => ", a4)
b4 := f4(a4)
fmt.Printf("%v / RES = %d\n", a4, b4)

a5 := []int {1,2,3,4,5}
fmt.Printf("%v => ", a5)
b5 := f5(a5)
fmt.Printf("%v / RES = %d\n", a5, b5)

a6 := make(map[string]int)
a6["AAA"] = 1
a6["BBB"] = 2
a6["CCC"] = 3
fmt.Printf("%v => ", a6)
b6 := f6(a6)
fmt.Printf("%v / RES = %d\n", a6, b6)

a7 := make(map[string]int)
a7["AAA"] = 1
a7["BBB"] = 2
a7["CCC"] = 3
fmt.Printf("%v => ", a7)
b7 := f7(a7)
fmt.Printf("%v / RES = %d\n", a7, b7)

a8 := make(map[string]int)
a8["AAA"] = 1
a8["BBB"] = 2
a8["CCC"] = 3
fmt.Printf("%v => ", a8)
b8 := f8(a8)
fmt.Printf("%v / RES = %d\n", a8, b8)

a91 := "fuck"
a92 := 233
fmt.Printf("%s, %d => ", a91, a92)
b91, b92 := f9(a91, a92)
fmt.Printf("%s, %d / RES = %s, %d\n", a91, a92, b91, b92)

a := make(map[string]int)
a["AAA"] = 1
a["BBB"] = 2
a["CCC"] = 3
}

输出结果如下:

1
2
3
4
5
6
7
8
9
[1 2 3 4 5] => [2 3 4 5 6] / RES = 15
[1 2 3 4 5] => [1 2 3 4 5] / RES = 15
[1 2 3 4 5] => [2 3 4 5 6] / RES = 15
[1 2 3 4 5] => [1 2 3 4 5] / RES = 15
[1 2 3 4 5] => [2 3 4 5 6] / RES = 15
map[AAA:1 BBB:2 CCC:3] => map[AAA:1 BBB:2 CCC:3] / RES = 6
map[AAA:1 BBB:2 CCC:3] => map[AAA:2 BBB:3 CCC:4] / RES = 6
map[AAA:1 BBB:2 CCC:3] => map[AAA:2 BBB:3 CCC:4] / RES = 6
fuck, 233 => fuck, 233 / RES = shit, 234

结论

输出结果这 1-9 行对应的是,传入的参数为 arr(对于前8项):

对象 操作方式 传参影响
切片 直接操作arr[i] 改变原值
切片 操作range的value 不改变原值
切片 操作range的arr[key] 改变原值
切片 copy到tarr,操作tarr 不改变原值
切片 赋值tarr,操作tarr 改变原值
集合 操作range的value 不改变原值
集合 操作range的arr[key] 改变原值
集合 赋值tarr,操作tarr 改变原值
字符串和数字 直接操作传入参数 不改变原值

由此可以看出:

  • 字符串和数字,遵循 Go 语言万物传值的规律,任何操作都不改变传入值。
  • 切片和集合应视为指针,直接操作会改变传入值。但是有下面两种方式使其不改变传入值:
    • 对于切片可以做一次 copy,这里要注意需要使用make([]int, len(arr), cap(arr))初始化,而不能简单的使用tarr := []int初始化,后者会使得 copy 无法进行(tarr依旧是空)。操作 copy 之后的值不改变传入值。
    • 对于切片和集合,都可以使用 range 取出其中的值,再进行操作。操作 range 取出的值不改变传入值。