package main func main() { var s []int s = append(s, 1)}
运行成功~
补充:golang slice 详解
一、数组切片的使用func main() {//1.基于数组创建数组切片var array [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}var slice = array[1:7] //array[startIndex:endIndex] 不包含endIndex//2.直接创建数组切片slice2 := make([]int, 5, 10)//3.直接创建并初始化数组切片slice3 := []int{1, 2, 3, 4, 5, 6}//4.基于数组切片创建数组切片slice5 := slice3[:4]//5.遍历数组切片for i, v := range slice3 {fmt.Println(i, v)}//6.len()和cap()var len = len(slice2) //数组切片的长度var cap = cap(slice) //数组切片的容量fmt.Println('len(slice2) =', len)fmt.Println('cap(slice) =', cap)//7.append() 会生成新的数组切片slice4 := append(slice2, 6, 7, 8)slice4 = append(slice4, slice3...)fmt.Println(slice4)//8.copy() 如果进行操作的两个数组切片元素个数不一致,将会按照个数较小的数组切片进行复制copy(slice2, slice3) //将slice3的前五个元素复制给slice2fmt.Println(slice2, slice3)}二、数组切片数据结构分析
数组切片slice的数据结构如下,一个指向真实array地址的指针ptr,slice的长度len和容量cap
// slice 数据结构type slice struct {array unsafe.Pointer len int cap int }
当传参时,函数接收到的参数是数组切片的一个复制,虽然两个是不同的变量,但是它们都有一个指向同一个地址空间的array指针,当修改一个数组切片时,另外一个也会改变,所以数组切片看起来是引用传递,其实是值传递。
三、append()方法解析3.1 数组切片不扩容的情况运行以下代码思考一个问题:s1和s2是指向同一个底层数组吗?
func main() {array := [20]int{1, 2, 3, 4, 5, 6, 7, 8, 9}s1 := array[:5]s2 := append(s1, 10)fmt.Println('s1 =', s1)fmt.Println('s2 =', s2)s2[0] = 0fmt.Println('s1 =', s1)fmt.Println('s2 =', s2)}
输出结果:
s1 = [1 2 3 4 5]
s2 = [1 2 3 4 5 10]
s1 = [0 2 3 4 5]
s2 = [0 2 3 4 5 10]
由第一行和第二行结果看来,似乎这是指向两个不同的数组;但是当修改了s2,发现s1也跟着改变了,这又表明二者是指向同一个数组。到底真相是怎样的呢?
运行以下代码:
import ('fmt''unsafe')type Slice struct {ptr unsafe.Pointer // Array pointerlen int // slice lengthcap int // slice capacity}func main() {array := [20]int{1, 2, 3, 4, 5, 6, 7, 8, 9}s1 := array[:5]s2 := append(s1, 10)s2[0] = 0// 把slice转换成自定义的 Slice structslice1 := (*Slice)(unsafe.Pointer(&s1))fmt.Printf('ptr:%v len:%v cap:%v n', slice1.ptr, slice1.len, slice1.cap)slice2 := (*Slice)(unsafe.Pointer(&s2))fmt.Printf('ptr:%v len:%v cap:%v n', slice2.ptr, slice2.len, slice2.cap)}
输出结果:
ptr:0xc04205e0a0 len:5 cap:20
ptr:0xc04205e0a0 len:6 cap:20
由结果可知:ptr指针存储的是数组中的首地址的值,并且这两个值相同,所以s1和s2确实是指向同一个底层数组。
但是,这两个数组切片的元素不同,这个可以根据首地址和数组切片长度len来确定不同的数组切片应该包含哪些元素,因为s1和s2虽然指向同一个底层数组,但是二者的len不同。通过这个demo,也验证了数组切片传参方式也是值传递。
3.2 数组切片扩容的情况:运行以下代码,思考与不扩容情况的不同之处,以及为什么
func main() {s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}s2 := append(s1, 10)fmt.Println('s1 =', s1)fmt.Println('s2 =', s2)s2[0] = 0fmt.Println('s1 =', s1)fmt.Println('s2 =', s2)}
输出结果:
s1 = [1 2 3 4 5 6 7 8 9]
s2 = [1 2 3 4 5 6 7 8 9 10]
s1 = [1 2 3 4 5 6 7 8 9]
s2 = [0 2 3 4 5 6 7 8 9 10]
根据结果我们发现,修改s2后,s1并未改变,这说明当append()后,s1和s2并未指向同一个底层数组,这又是为什么呢?
同样,我们接着运行以下代码:
import ('fmt''unsafe')type Slice struct {ptr unsafe.Pointer // Array pointerlen int // slice lengthcap int // slice capacity}func main() {s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}s2 := append(s1, 10)fmt.Println('s1 =', s1)fmt.Println('s2 =', s2)s2[0] = 0fmt.Println('s1 =', s1)fmt.Println('s2 =', s2)// 把slice转换成自定义的 Slice structslice1 := (*Slice)(unsafe.Pointer(&s1))fmt.Printf('ptr:%v len:%v cap:%v n', slice1.ptr, slice1.len, slice1.cap)slice2 := (*Slice)(unsafe.Pointer(&s2))fmt.Printf('ptr:%v len:%v cap:%v n', slice2.ptr, slice2.len, slice2.cap)}
输出结果:
s1 = [1 2 3 4 5 6 7 8 9]
s2 = [1 2 3 4 5 6 7 8 9 10]
s1 = [1 2 3 4 5 6 7 8 9]
s2 = [0 2 3 4 5 6 7 8 9 10]
ptr:0xc04207a000 len:9 cap:9
ptr:0xc04207c000 len:10 cap:18
由结果可知:append()后,s1和s2确实指向了不同的底层数组,并且二者的数组容量cap也不相同了。
过程是这样的:当append()时,发现数组容量不够用,于是开辟了新的数组空间,cap变为原来的两倍,s2指向了这个新的数组,所以当修改s2时,s1不受影响
以上为个人经验,希望能给大家一个参考,也希望大家多多支持优爱好网。如有错误或未考虑完全的地方,望不吝赐教。
相关文章:
1. 怎么让div+css兼容ie6ie7ie8ie9和FireFoxChrome等浏览器2. requestAnimationFrame使用示例详解3. 基于JavaScript实现图片裁剪功能4. React优雅的封装SvgIcon组件示例5. uniapp自定义验证码输入框并隐藏光标6. 详解JavaScript中原始数据类型Symbol的使用7. JavaScript深拷贝方法structuredClone使用8. uniapp 手机验证码输入框实现代码(随机数、倒计时、隐藏手机号码中间四位)可以直接使用9. 使用Node.js实现Clean Architecture方法示例详解10. Jquery使用原生AJAX方法请求数据