golang 中的切片(slice) 和 python 中的 list 很像,但是又有很多不一样的地方,本文总结一下,当某个函数的参数是切片类型的时候的一些特性。

数组作为函数的参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
	"fmt"
)

func main() {
	a := [2]int{1,2}
	fmt.Println(a)
	arraytest(a)
	fmt.Println(a)
}


// 参数为数组
func arraytest(a [2]int) {
	// 尝试修改数组中的值
	a[0] = a[0]*2
}

上面代码打印结果

[1 2]
[1 2]

说明当以数组作为参数传入函数以后,在函数体内对数组进行修改,并不会影响到原数组,因为在函数体内,实际上操作的是参数的复本

如果想要在函数内对数组进行修改,同时也改变原数组,则参数要改为指针类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
	"fmt"
)

func main() {
	a := [2]int{1,2}
	fmt.Println(a)
	arraytest(&a) //这里要传入数组a的地址
	fmt.Println(a)
}


// 参数为数组指针
func arraytest(a *[2]int) {
	// 尝试修改数组中的值
	a[0] = a[0]*2
}

但是这里要注意,一切函数内都是操作参数的复本,上面的函数其实也是操作 a 的复本

切片作为函数参数

切片的初始化,可以是从一个数组中截取某个片断,也可以使用 make 函数

  1. 通过数组初始化切片
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
	"fmt"
)

func main() {
	a := [2]int{1,2}
	fmt.Println(a)  // 输出为[1,2]
	var s_a = a[:]
	slicetest(s_a)
	fmt.Println(a) // 输出为[2,2]
}


// 参数为切片
func slicetest(a []int) {
	a[0] = a[0]*2
}

在函数中对切片进行修改,这种情况下是会影响到原数组的!

  1. 使用 make 函数创建的切片
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
	"fmt"
)

func main() {
	sam := make([]int, 2)
	sam = []int{1,2}
	fmt.Println(sam)
	slicetest(sam)
	fmt.Println(sam)
}


// 参数为切片
func slicetest(a []int) {
	a[0] = a[0]*2
}

这种情况下对于参数的修改也是会影响到原切片的

  1. 切片指针

go语言中切片名本身就是一个地址,通过切片名加下标的方式访问切片元素原本就是指针访问的一种表现,所在在函数参数中最好不要再次使用指针,如果非要使用的话看起来会比较麻烦

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
	"fmt"
)

func main() {
	sam := make([]int, 2)
	sam = []int{1,2}
	fmt.Println(sam)
	slicetest(&sam)  //这里要传sam的地址
	fmt.Println(sam)
}


// 参数为切片
func slicetest(a *[]int) {
  // 这里一定要将*a 括起来,*a 表示a处的值,也就是切片本身
  (*a)[0] = 100
}
  1. 使用 new 创建切片
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
	"fmt"
)

func main() {
	// 使用new 初始化切片
	san := new([]int)
	san = &[]int{1,2}
	slicetest(san)
	fmt.Println(*san)
}


// 参数为切片
func slicetest(a *[]int) {
  // 这里一定要将*a 括起来,*a 表示a处的值,也就是切片本身
  (*a)[0] = 100
}

使用 new 创建变量,返回的是变量的地址,所以上面代码中 san 的类型为 *[]int ,本身就是一个指针,所以可以直接传入 slicetest 函数中, san := new([]int) 只是声明了变量,但是并没有初始化,是个空切片,如果这时就传入函数中,会由于 (*a)[0] = 100 这行导致索引溢出。