GO:数组、切片

这篇博客详细介绍了Go语言中的数组和切片。数组是具有固定长度和相同类型的数据集合,可以通过不同方式定义,并提供了赋值和遍历操作。多维数组和指针数组的概念也被提及,指针数组允许在函数中改变原始数组的值。切片则是一种更灵活的数据类型,长度可变,可以追加元素并自动扩容。切片操作包括获取子切片、拼接和复制,它们与底层数组的关系是引用关系,需要注意内存管理和效率问题。

@cible 学习笔记

数组、切片

数组

定义数组

数组是具有相同类型且长度固定的一组连续数据。在go语言中我们可以使用如下几种方式来定义数组:
//方式一

var arr1 = [5]int{}
//方式二
var arr2 = [5]int{1,2,3,4,5}
//方式三
var arr3 = [5]int{3:10}

结果:

arr1 [0 0 0 0 0]
arr2 [1 2 3 4 5]
arr3 [0 0 0 10 0]
  1. 方法一在声明时没有为其指定初值,所以数组内的值被初始化为类型的零值。
  2. 方法二使用显示的方式为数组定义初值。
  3. 方法三通过下标的方式为下标为3的位置赋上了初值10。
    在数组的定义是包含其长度的,也就是说[3]int与[4]int是两种不同的数据类型。

操作数据

  1. 赋值
for i := 0; i < len(arr1); i++ {
		arr1[i] = i * 10
	}    //[0 10 20 30 40]

2.遍历数组的值

for index, value := range arr1 {
		fmt.Printf("index: %d, value: %d\n", index, value)
	}

多维数组定义

var arr4 = [5][5]int{
		{1, 2, 3, 4, 5},
		{6, 7, 8, 9, 10},
	}

结果:

[[1 2 3 4 5] [6 7 8 9 10] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]

指针数组与数组指针

  1. 指针数组

对于指针数组来说,就是:一个数组里面装的都是指针,在go语言中数组默认是值传递的,所以如果我们在函数中修改传递过来的数组对原来的数组是没有影响的。

func main() {
	var a [5]int
	fmt.Println(a)
	test(a)
	fmt.Println(a)
}

func test(a [5]int) {
	a[1] = 2
	fmt.Println(a)
}

结果:

[0 0 0 0 0]
[0 2 0 0 0]
[0 0 0 0 0]

但是如果我们一个函数传递的是指针数组。

func main() {
	var a [5]*int
	fmt.Println(a)
	for i := 0; i < 5; i++ {
		temp := i
		a[i] = &temp
	}
	for i := 0; i < 5; i++ {
		fmt.Print(" ", *a[i])
	}
	fmt.Println()
	test1(a)
	for i := 0; i < 5; i++ {
		fmt.Print(" ", *a[i])
	}
}

func test1(a [5]*int) {
	*a[1] = 2
	for i := 0; i < 5; i++ {
		fmt.Print(" ", *a[i])
	}
	fmt.Println()
}

结果:

[<nil> <nil> <nil> <nil> <nil>]
 0 1 2 3 4
 0 2 2 3 4
 0 2 2 3 4

可以看到初始化值全是nil,也就验证了指针数组内部全都是一个一个指针,之后我们将其初始化,内部的每个指针指向一块内存空间。

  1. 数组指针
    了解了指针数组之后,再来看一下数组指针,数组指针的全称为指向数组的指针,在go语言中我们可以如下操作。
func main() {
	var a [5]int
	var aPtr *[5]int
	aPtr = &a
	//这样简短定义也可以aPtr := &a
	fmt.Println(aPtr)
	test(aPtr)
	fmt.Println(aPtr)
}


func test(aPtr *[5]int) {
	aPtr[1] = 5
	fmt.Println(aPtr)
}

我们先定义了一个数组a,然后定一个指向数组a的指针aPtr,然后将这个指针传入一个函数,在函数中我们改变了具体的值。
结果:

&[0 0 0 0 0]
&[0 5 0 0 0]
&[0 5 0 0 0]

无论在main函数还是在test函数中对数组的操作都会直接改变数组。

切片

因为数组是固定长度的,所以在一些场合下就显得不够灵活,所以go语言提供了一种更为便捷的数据类型叫做切片。切片操作与数组类似,但是它的长度是不固定的,可以追加元素,如果以达到当前切片容量的上限会再自动扩容。

  1. 定义切片
//方法一
var s1 = []int{}
//方法二
var s2 = []int{1, 2, 3}
//方法三
var s3 = make([]int, 5)
//方法四
var s4 = make([]int, 5, 10)

方法一声明了一个空切片,方法二声明了一个长度为3的切片,方法三声明了一个长度为5的空切片,方法四声明了一个长度为5容量为10的切片。可以看到切片的定义与数组类似,但是定义切片不需要为其指定长度。

我们可以通过len()和cap()这两个函数来获取切片的长度和容量,下面就来依次看下上面各切片的长度以及容量。

s1 [] 0 0
s2 [1 2 3] 3 3
s3 [0 0 0 0 0] 5 5
s4 [0 0 0 0 0] 5 10

如果我们通过这种方式定义一个切片,那么他会被赋予切片的空值nil。

var s5 []int
  1. 切片操作
    我们可以通过如下的方式在数组和切片上继续获取切片。
func main() {
	arr := [5]int{1, 2, 3, 4, 5}
	s := []int{6, 7, 8, 9, 10}

	s1 := arr[2:4]
	s2 := arr[:3]
	s3 := arr[2:]
	s4 := s[1:3]

	fmt.Println("s1:", s1)
	fmt.Println("s2:", s2)
	fmt.Println("s3:", s3)
	fmt.Println("s4:", s4)
}

程序的输出结果如下:

s1: [3 4]
s2: [1 2 3]
s3: [3 4 5]
s4: [7 8]

切片操作是“包左不包右”.

  1. 切片的扩充与拼接

对切片进行扩充操作。

func main() {
	a := []int{1, 2, 3}
	b := a[1:3]

	b = append(b, 4)
	b = append(b, 5)
	b = append(b, 6)
	b = append(b, 7)
	fmt.Println(a)
	fmt.Println(b)
}

程序输出结果如下:

[1 2 3]
[2 3 4 5 6 7]

将两个切片进行拼接:

func main() {
	a := []int{1, 2, 3}
	b := a[1:3]

	fmt.Println(b)

	a = append(a, b...)
	fmt.Println(a)
}

如果我们想要将一个切片的值复制给另一个切片,go语言也提供了非常简便的方式。

func main() {
	a := []int{1, 2, 3}
	b := make([]int, 3)
	copy(b, a)
	fmt.Println(a)
	fmt.Println(b)
}  
  1. 切片与数组的关系

对于任何一个切片来说,其都有一个底层数组与之对应,我们可以将切片看作是一个窗口,透过这个窗口可以看到底层数组的一部分元素。
切片是引用底层数组的,需要注意的就是小切片引用大数组的问题,如果底层的大数组一直有切片进行引用,那么垃圾回收机制就不会将其收回,造成内存的浪费,最有效的做法是copy需要的数据后再进行操作。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值