Golang数组与Slice,以及append函数的陷阱

Golang中数组的概念与C中相差无几。

1.声明一个数组

var array [5]int //声明一个数组。
var array2 [5]int 
array2=array //两个数组变量赋值,这个操作会导致一次数组的拷贝。
//比如
func test(){
    var array =[5]int{2,3,4,5,6}
    var array2 [5]int
    array2=array
    array[1]=9
    fmt.Printf("array:%v\n",array) //输出:array:[2 9 4 5 6]
    fmt.Printf("array2:%v\n",array2) //输出:array2:[2 3 4 5 6]
}

2.声明一个数组指针

var array [5]*int //声明一个数组指针。
var array2 [5]*int 
array2=array //两个数组指针赋值,不会拷贝底层数组,两个指针都指向同一个数组。
//比如
func test(){
    var array =&[5]int{2,3,4,5,6}
    var array2 *[5]int
    array2=array
    array[1]=9
    fmt.Printf("array:%v\n",array) // array:&[2 9 4 5 6]
    fmt.Printf("array2:%v\n",array2) //array2:&[2 9 4 5 6]
}

3.Slice

slice并不是单纯的一个指向数组的指针,它是一个结构体(包含:指针,长度,容量)

// 创建 nil 整型切片
var slice []int

上面创造了一个”nil切片”,要注意到的就是golang中的nil也不是一个单纯的空指针,与Java中的null有着不同的意义。

Java中若是存在以下代码:

Object object=null;

这表示object这个变量不是任何内存的引用。

Golang中若是存在以下代码:

var slice []int=nil

表示slice被一个nil切片赋值,nil切片就是一个结构体中个变量都为”0值”。
即:nil切片{指针(NULL),长度(0),容量(0)}

4.Slice的赋值

如果讲一个Slice赋值个另外一个Slice了,那么他们是共享底层数组的
比如slice2=slice
那么,slice2中的指针与slice中的指针就指向了底层同一个数组。

还可以通过索引的方式来创建切片:

// 创建一个整型切片
// 其长度和容量都是 5 个元素
slice := []int{10, 20, 30, 40, 50}
// 创建一个新切片
// 其长度为 2 个元素,容量为 4 个元素
newSlice := slice[1:3]
/ 将第三个元素切片,并限制容量
// 其长度为 1 个元素,容量为 2 个元素
newSlice2 := slice[2:3:4]

Slice中长度代表了他能访问的区间,容量代表了append操作的区间。
如果append超过设定的区间,那么Slice底层就会扩容了

5.有陷阱的append操作

这部分是针对上面–>如果append超过设定的区间,那么Slice底层就会扩容了. 这句话做详细说明哈。

具体想说的都在下面的例子里:

func test(){
    var array =[]int{1,2,3,4,5}// len:5,capacity:5
    var newArray=array[1:3]// len:2,capacity:4   (已经使用了两个位置,所以还空两位置可以append)
    fmt.Printf("%p\n",array) //0xc420098000
    fmt.Printf("%p\n",newArray) //0xc420098008 可以看到newArray的地址指向的是array[1]的地址,即他们底层使用的还是一个数组
    fmt.Printf("%v\n",array) //[1 2 3 4 5]
    fmt.Printf("%v\n",newArray) //[1 2 3 4 5]

    newArray[1]=9 //更改后array、newArray都改变了
    fmt.Printf("%v\n",array) // [1 2 9 4 5]
    fmt.Printf("%v\n",newArray) // [2 9]

    newArray=append(newArray,11,12)//append 操作之后,array的len和capacity不变,newArray的len变为4,capacity:4。因为这是对newArray的操作
    fmt.Printf("%v\n",array) //[1 2 9 11 12] //注意对newArray做append操作之后,array[3],array[4]的值也发生了改变
    fmt.Printf("%v\n",newArray) //[2 9 11 12]

    newArray=append(newArray,13,14) // 因为newArray的len已经等于capacity,所以再次append就会超过capacity值,
    // 此时,append函数内部会创建一个新的底层数组(是一个扩容过的数组),并将array指向的底层数组拷贝过去,然后在追加新的值。
    fmt.Printf("%p\n",array) //0xc420098000
    fmt.Printf("%p\n",newArray) //0xc4200a0000
    fmt.Printf("%v\n",array) //[1 2 9 11 12]
    fmt.Printf("%v\n",newArray) //[2 9 11 12 13 14]  他两已经不再是指向同一个底层数组y了
}

所以知道我为啥说append有陷阱了吧?
append可能会导致原本使用同一个底层数组的两个Slice变量变为使用不同的底层数组。

关于这部分内容《go语言实战》里第四章讲的很清楚。

猜你喜欢

转载自blog.csdn.net/zhaominpro/article/details/80765241
今日推荐