- 切片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等元素。
- 切片唯一合法的比较操作是和nil比较。 一个nil值的切片并没有底层数组。
一个nil值的切片的长度和容量都是0。但是我们不能说一个长度和容量都是0的切片一定是nil
要检查切片是否为空,请始终使用len(s) == 0
来判断,而不应该使用s == nil
来判断。- 对一个切片的修改会影响另一个切片的内容
- 切片的遍历方式和数组是一致的,支持索引遍历和for range遍历。
切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。
上述两张图片引自李文周的博客
可以为切片动态添加元素。
可以一次添加一个元素,可以添加多个元素,也可以添加另一个切片中的元素(后面加…)
通过var声明的零值切片可以在append()函数直接使用,无需初始化。
可以迅速地将一个切片的数据复制到另外一个切片空间中,copy()函数的使用格式如下:
copy(destSlice, srcSlice []T)
Go函数传递默认是值拷贝,将slice变量传入append函数相当于传了原slice变量的一个副本,注意不是拷贝底层数组。
因为slice变量并不是数组,它仅仅是存储了底层数组的一些信息。
len>cap
时,会重新开辟一个2*cap
的新内存空间去存储追加过元素的slice。len<=cap
,则append返回的新生成的slice的内存地址依旧是传入的slice参数的内存地址。package main
import (
"fmt"
"unsafe"
)
func main() {
testSlice := make([]int, 0)
testSlice = append(testSlice, 0)
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice), cap(testSlice), testSlice)
//len: 1, cap: 1, data:[0]
fmt.Printf("testSlice地址:%p \n", &testSlice[0])
//testSlice地址:0xc000014070
fmt.Println("---------------0---------------")
testSlice = append(testSlice, 1)
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice), cap(testSlice), testSlice)
//len: 2, cap: 2, data:[0 1]
//因为cap长度不够,所以重新申请一个2*cap长度的内存空间,将内容拷贝到2倍cap的内存中
fmt.Printf("testSlice地址:%p \n", &testSlice[0])
//testSlice地址:0xc0000140a0 —— testSlice的内存地址变化了
fmt.Println("---------------1---------------")
testSlice = append(testSlice, 2)
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice), cap(testSlice), testSlice)
//len: 3, cap: 4, data:[0 1 2]
fmt.Printf("testSlice地址:%p \n", &testSlice[0])
//testSlice地址:0xc000016140
fmt.Println("----------------2--------------")
testSlice = append(testSlice, 3)
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice), cap(testSlice), testSlice)
//len: 4, cap: 4, data:[0 1 2 3]
fmt.Printf("testSlice地址:%p \n", &testSlice[0])
//testSlice地址:0xc000016140
//因为cap空间足够用,所以继续在原来的内存地址上追加数据
fmt.Println("---------------3---------------")
// ================ 这里开始用新变量接收 ====================
testSlice1 := append(testSlice, 4)
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice), cap(testSlice), testSlice)
//len: 4, cap: 4, data:[0 1 2 3]
fmt.Printf("testSlice地址:%p \n", &testSlice[0])
//testSlice地址:0xc000016140
//testSlice的数据不变
fmt.Println("================ 从这里开始用新变量接收 ====================")
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice1), cap(testSlice1), testSlice1)
//len: 5, cap: 8, data:[0 1 2 3 4]
fmt.Printf("testSlice1地址:%p \n", &testSlice1[0])
//testSlice1地址:0xc00001a0c0
//新生成的testSlice1内存地址变化了
fmt.Println("---------------4---------------")
testSlice2 := append(testSlice1, 5)
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice1), cap(testSlice1), testSlice1)
//len: 5, cap: 8, data:[0 1 2 3 4]
fmt.Printf("testSlice1地址:%p \n", &testSlice1[0])
//testSlice1地址:0xc00001a0c0
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice2), cap(testSlice2), testSlice2)
//len: 6, cap: 8, data:[0 1 2 3 4 5]
fmt.Printf("testSlice2地址:%p \n", &testSlice2[0])
//testSlice2地址:0xc00001a0c0
//在这里可以看到testSlice1和testSlice2的内存地址是一样的
fmt.Println("--------------5----------------")
p := unsafe.Pointer(&testSlice1[4])
q := uintptr(p) + 8
t := (*int)(unsafe.Pointer(q))
fmt.Println("testSlice1可以拿到testSlice2的第五个元素值:", *t)
//testSlice1可以拿到testSlice2的第五个元素值: 5
fmt.Println("================ 这里测试同地址变量 取值 ====================")
testSlice2[0] = 99999
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice1), cap(testSlice1), testSlice1)
//len: 5, cap: 8, data:[99999 1 2 3 4]
fmt.Printf("testSlice1地址:%p \n", &testSlice1[0])
//testSlice1地址:0xc00001a0c0
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice2), cap(testSlice2), testSlice2)
//len: 6, cap: 8, data:[99999 1 2 3 4 5]
fmt.Printf("testSlice2地址:%p \n", &testSlice2[0])
//testSlice2地址:0xc00001a0c0
//修改testSlice2的值同时会改变testSlice1中的值
//当然testSlice1一般不能访问testSlice2中的第6个元素,因为testSlice1的len是5
//但是可以通过上面的方法访问,通过操作内存地址来访问
}
package main
import (
"fmt"
)
func main() {
testMap := make(map[string][]string, 0)
testSlice := make([]string, 0)
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice), cap(testSlice), testSlice)
fmt.Println("===============================")
testSlice = append(testSlice, "hello","go")
fmt.Printf("len: %d, cap: %d, data:%+v \n", len(testSlice), cap(testSlice), testSlice)
fmt.Printf("testSlice地址:%p \n", &testSlice[0])
fmt.Println("===============================")
// 将testSlice的值赋值给 testMap["world"]
testMap["world"] = testSlice
// 这里要对map的value进行操作,即testSlice
testSliceTmp := append(testSlice, "hello2","go2")
// 发现地址变了,因为 append追加元素后,slice动态扩容,可能会生成新的slice(地址变了),但是原来的slice(上述map中slice)没变
fmt.Printf("testSliceTmp地址:%p \n", &testSliceTmp[0])
// 用append操作完以后,并不能改变 testMap["world"]的值, 因为append函数接收的是testSlice的副本
fmt.Printf("false,len: %d, cap: %d, data:%+v \n", len(testMap["world"]), cap(testMap["world"]), testMap["world"])
// append后的结果重新赋值回testSlice,发现testMap["world"]并没有改变
// 因为上边map的赋值操作, =右边 代表 testSlice内存空间存储的数据值,而不是他指向的内存地址
testSlice = append(testSlice, "hello2","go2")
fmt.Printf("true,len: %d, cap: %d, data:%+v \n", len(testMap["world"]), cap(testMap["world"]), testMap["world"])
// 解决办法是:将append()返回的slice重新赋值回 testMap["world"]
// 这里恢复一下testSlice,上边append操作结果赋值testSlice已经修改了
testMap["world"] = append(testSlice[0:2], "hello2","go2")
fmt.Printf("true,len: %d, cap: %d, data:%+v \n", len(testMap["world"]), cap(testMap["world"]), testMap["world"])
}