一.数组和切片的区别(7个问题)
对应问题
1.数组和切片的关系和区别(跟谁学,京东,伴鱼,腾讯+3,小米+3,百度,深信服,知乎,触宝,微步,北京神州,度小满,腾讯音乐,早安科技)
2.如何把数组转换成一个切片(跟谁学)
3.slice用copy和左值进行初始化的区别 (有色)--------copy的用法在下面
4.一个函数传参一个slice,先append在赋值和另外一个函数先赋值在append, 那个会发生变化?(字节)
5.slice分配在堆上还是栈上 (京东)
6.切片的比较,深拷贝的问题(北京合链)
7.make一个slice后返回的是指针吗? map呢?(字节)
1.数组和切片初始化的区别(问题1,2,3)
1.1数组的初始化
数组的初始化一共有两种方式
(1):采用的是 [x]type{a,b,c}
(2):采用的是[…]type{a,b,c}(自动计算数组的长度)
例子如下
//在定义的时候就确定长度 [x]type{a,b,c}
var arr0 = [3]int{1, 2, 3}
fmt.Println(arr0)
//在定义的时候省略长度 [...]type{a,b,c}
var arr1 = [...]int{1, 2, 3}
fmt.Println(arr1)
//在定义的时候制定下标对应的值
var arr2 = [...]int{3: 4, 4: 5}
fmt.Println(arr2)
//自定义结构的数组
var arr3 = [...]struct {
name string
age uint8
}{
{"xiaoming", 8},
{"xiaojin", 16},
}
fmt.Println(arr3)
1.2切片的初始化
切片的初始化方法就多了,一共有四种初始化的方式
(1): var arr1 [] int
(2):make([]type,len)
(3):make([]type,len,cap)
(4):从数组上面
//方法1 直接声明 (直接声明的话将是nil,也被成为nil切片)
var arr1 []int
if arr1 == nil {
fmt.Println("空")
} else {
fmt.Println("非空")
}
fmt.Println(len(arr1))
fmt.Println(cap(arr1))
/* 方法1 打印结果
arr1空
0
0
*/
// 方法2 使用 :=
arr2 := []int{}
if arr2 == nil {
fmt.Println("空")
} else {
fmt.Println("非空")
}
fmt.Println(len(arr2))
fmt.Println(cap(arr2))
/* 方法2 打印结果
arr2非空
0
0
*/
// 方法3 使用 make 创建 make([]type,len,cap)
// 3.1 make([]type,len) 省略cap 此时cap默认等于len
var arr3_1 = make([]int, 5)
if arr3_1 == nil {
fmt.Println("arr3_1空")
} else {
fmt.Println("arr3_1非空")
}
fmt.Println(len(arr3_1))
fmt.Println(cap(arr3_1))
//3.2 make([]type,len,cap)
var arr3_2 = make([]int, 0, 5)
if arr3_2 == nil {
fmt.Println("arr3_2空")
} else {
fmt.Println("arr3_3非空")
}
fmt.Println(len(arr3_2))
fmt.Println(cap(arr3_2))
/* 方法3 打印结果
arr3_1非空
5
5
arr3_3非空
0
5
*/
// 方法4 从数组切片
arrTemp := [5]int{1, 2, 3, 4, 5}
arr4 := arrTemp[:]
if arr4 == nil {
fmt.Println("arr4空")
} else {
fmt.Println("arr4非空")
}
fmt.Println(len(arr4))
fmt.Println(cap(arr4))
/* 方法4 打印结果
arr4非空
5
5
*/
结论:只有第一种方法(var arr []int)定义切片的时候,切片为空,而且如果slice==nil,len,cap的结果都为0
需要注意的是从数组切片的方法中可以有三个参数,详细如下(来自地鼠文档)
2.数组和切片在函数传参中的区别(问题3,4,5,6,7)
对应问题
一个函数传参一个slice,先append在赋值和另外一个函数先赋值在append, 那个会发生变化?(字节)
slice分配在堆上还是栈上 (京东)
切片的比较,深拷贝的问题(北京合链)
make一个slice后返回的是指针吗? map呢?(字节)
Go语言中所有的传参都是值传递(传值),都是一个副本,一个拷贝。
Golang中引用类型:指针,slice,map,channel,接口,函数等。变量存放的是一个内存地址值,这个地址值指向的空间存的才是最终的值。内存通常在堆中分配,当没有任何变量引用这个地址时,改地址对应的数据空间就成为一个垃圾,通过GC回收
1.简单的数据像 int,float,bool,string这样的基本类型都属于值类型,传参到函数后,在函数中做出的修改就是对副本进行了修改,到了函数外面还是原来的值
2.复杂的数据结构一般都是用引用类型保存,虽然传进去的是副本,但是副本指针依然指向的是同一块空间,故对函数中引用类型的修改会修改函数外面的值,到了函数外面,将会发生改变
2.1数组的函数传参以及复制(值传递)
//数组中的复制,以及函数中的传递都是值赋值的
func TestArrayTrans(t *testing.T) {
arrayA := [2]int{100, 200}
var arrayB [2]int
arrayB = arrayA
fmt.Printf("arrayA : %p , %v\n", &arrayA, arrayA)
fmt.Printf("arrayB : %p , %v\n", &arrayB, arrayB)
//在函数中对arrayA进行修改
testArray(arrayA)
//函数中修改后,A还是原来的值
fmt.Printf("arrayA : %p , %v\n", &arrayA, arrayA)
/* 打印结果:
arrayA : 0xc00000a350 , [100 200]
arrayB : 0xc00000a360 , [100 200]
func Array : 0xc00000a390 , [100 200]
三个打印的地址都不同(说明数组的传递是值传递的,即每次都是复制和传参中都是开辟一个新的内存地址,并一一复制),这样如果数组的长度较大的时候将会消耗大量的空间
*/
}
func testArray(x [2]int) {
fmt.Printf("func Array : %p , %v\n", &x, x)
x[0] = 1
}
2.2切片的函数传参以及复制(引用传递)
//数组中的复制,以及函数中的传递都是引用赋值的
func TestSliceTrans(t *testing.T) {
arrayA := []int{100, 200}
var arrayB []int
//浅拷贝,只是拷贝了指针,两个指针指向的却是同一块地址,对arrayA的改变会影响到arrayB
//arrayB = arrayA
//深拷贝,开辟了一个新的空间,两个指针指向的是不同的地址,此时对arrayA的改变不会影响到arrayB
//注意:copy函数取决于两者中最短的那个slice,如果没有下面的两行append,arrayB的长度是0,那么arrayB的打印结果将为[]
//arrayB = append(arrayB, 1)
//arrayB = append(arrayB, 1)
//copy(arrayB, arrayA)
fmt.Printf("arrayA : %p , %v\n", &arrayA, arrayA)
fmt.Printf("arrayB : %p , %v\n", &arrayB, arrayB)
testSlice(arrayA)
//函数中修改后,A还是原来的值
fmt.Printf("arrayA : %p , %v\n", &arrayA, arrayA)
fmt.Printf("arrayA : %p , %v\n", &arrayB, arrayB)
/* 打印结果
arrayA : 0xc0000040d8 , [100 200]
arrayB : 0xc0000040f0 , [100 200]
func Array : 0xc000004138 , [100 200]
arrayA : 0xc0000040d8 , [1 200]
arrayA : 0xc0000040f0 , [1 200]
slice是引用类型的传递,地址虽然不同,但是只是两个不同地址的指针,指针指向的是同一块内存地址,在函数中进行的改变会影响到函数外面的结果,而且拷贝也只是拷贝了一份指针,两个指针指向同一块地址,对arrayA的修改,会影响到arrayB的值
因此引用类型的拷贝一般又特定的函数来进行深拷贝,如slice中使用copy(dst,src)函数
*/
}
2.3数组函数传参的小坑
[4]int和[3]int不被视为同一种类型,编译不通过
func TestArrayFuncParameter(t *testing.T) {
numbersArray := [3]int{1, 2, 3}
//编译不通过
SumForArray(numbersArray)
}
func SumForArray(numbers [4]int) int {
sum := 0
for i := 0; i < 5; i++ {
sum += numbers[i]
}
return sum
}
3.数组和切片的关系
func TestRelation(t *testing.T) {
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:3]
fmt.Println(len(slice), cap(slice))
}
示意图如下
结论:
数组
1.数组固定长度
2.数组需要指定大小,不指定的话可以通过[…]type,将进行自动推算出大小,数组长度是数据类型的一部分,当函数需要传参数[4]int 的时候,如果传入的参数是[3]int 编译不通过
3.数组是通过值传递的
切片
1.切片可以改变长度
2.数组不需要指定大小
3.切片是引用传递
笔记由阅读以下文档做出
地鼠文档https://www.topgoer.cn/docs/golang/chapter03-10
彻底理解Golang Slicehttps://mp.weixin.qq.com/s/Rqi_nSbw6oxEnpxZ8teCCw