码迷,mamicode.com
首页 > 编程语言 > 详细

go 切片和数组

时间:2020-10-05 21:55:17      阅读:25      评论:0      收藏:0      [点我收藏+]

标签:值传递   unsafe   中比   结构体   ntp   网络   nil   str   部分   

一、区别

  数组的长度是固定的,初始化后就不能修改长度,大家平时代码中比较少用。

  slice是对数组的一个封装,可以动态扩容,slice是一个结构体,包含三个字段:底层数组、长度、容量

 二、初始化方式

  数组

var a = [4]int{1,2,3,4}
var b = [...]int{1,2,3,4,5}

  切片

var a []int //当前值为nil,未分配内存空间
var b = []int{1, 2, 3}
var c = make([]int, 5) //初始化5个元素,默认值为0,容量为5,在后面追加元素就会触发扩容
var d = make([]int, 0, 5) //初始化0个元素,容量为5

注意:slice在追加元素时如果容量不够会触发扩容,原slice拷贝到新slice中,扩容规则网上的说法:原slice小于1024,新的slice容量变成原slice的2倍,大于1024时,新slice容量变成原slice的1.25倍。这里的说法只是一个大概不是很准确,感兴趣的朋友可以查看网络上相关文章或源码,其实这对于我们平时开发影响不是很大,想要最求性能可以在初始化时预估容量,减少扩容和拷贝的次数。

三、拷贝

  第一种

var a = []int{1,2,3,4,5}
var b = a[2:4]  //拷贝a中部分数据到b中

b[0] = 12 //改变b第一个元素的值
log.Println(a,b) //输出:[1 2 12 4 5] [12 4] 两个数组的值都被改变了,说明数据是地址拷贝

b = append(b,6,7,8,9) //追加元素,容量不够引发扩容
b[0] = 13 //改变b第一个元素的值
log.Println(a,b) //输出:[1 2 12 4 5] [13 4 6 7 8 9] b中的数据改变了,a中的值并没有改变,扩容以后b和a中的元素已经没有了关系

  第二种

var a = []int{1,2,3,4,5}
var b = make([]int,5)

copy(b,a) //值拷贝
b[0] = 12
log.Println(a,b) //输出:[1 2 3 4 5] [12 2 3 4 5] 

四、平时使用需要注意的地方

  1、作为函数参数传递(go语言的函数参数传递,只有值传递,没有引用传递),但有时候我们会产生一些误区,看看下面这个例子

func f(a []int){
    a[0] = 12
    a = append(a,6)
}

func main(){
    var a = []int{1,2,3,4,5}
    f(a)
    log.Println(a) //输出:[12 2 3 4 5] 第一个元素改变了,但值6并没有追加到a中
}

   2、range 中改变值

var a = []int{1, 2, 3, 4, 5}

for _, v := range a { //不会改变a中元素的值(v只是一个临时变量)
    v = v + 2
}
log.Println(a) //输出:[1 2 3 4 5]

for i := range a { //会改变a中的值
    a[i] = i + 2
}
log.Println(a) //输出:[2 3 4 5 6]

五、扩展

  1、slice的结构

func main(){
    var a = []int{1, 2, 3, 4, 5}
    s := (*SliceHeader)(unsafe.Pointer(&a))

    log.Println(s.Len,s.Cap) //输出:5 5
}

type SliceHeader struct {
    Data uintptr    //底层数组地址
    Len  int        //长度
    Cap  int        //容量
}

  2、append方法必须要有接收者

var a = []int{1, 2, 3, 4, 5}
append(a,6) //编译失败

  3、数组的元素在内存中地址是连续的

var a = []int{1, 2, 3, 4, 5}
s := (*reflect.SliceHeader)(unsafe.Pointer(&a))

//根据数组索引获取元素值
arrGet := func(n int)int{
    if n >= s.Len{
        panic("索引越界")
    }
    v := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(s.Data))+unsafe.Sizeof(int(0))*uintptr(n)))
    return *v
}
log.Println(arrGet(0)) //输出:1
log.Println(arrGet(1)) //输出:2
log.Println(arrGet(5)) //输出:panic: 索引越界

 

以上内容为个人理解,如有问题欢迎指出。

go 切片和数组

标签:值传递   unsafe   中比   结构体   ntp   网络   nil   str   部分   

原文地址:https://www.cnblogs.com/fanxp/p/13769350.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!