内容简介:数组 的类型名是 [n ]elemetType ,其中 n 是数组长度, elementType 是数组元素类型 。 数组一般在创建时通过字面量初始化, 单独声明一个数组类型变量而不进行初始化是没有意义的。编译运行结果
1. 内部实现
数组是一种非常有用给的数据结构,因为其占用的内存是连续分配的。 由于内存连续,CPU能把正在使用的数据缓存更久的时间。而且内存连续很容易计算索引,可以快速访问数组里的任意数据。
在Go语言中,数组是一种类似于整形,浮点型,字符串的 基本数据类型 ,区别于C++语言的是, Go语言的数组在函数传参时是值传递的,因此要想通过某个函数修改数组的值,就必须通过传入数组的指针来实现 ,实际上Go语言中的所有函数传参都是值传递。
2. 声明和初始化
数组 的类型名是 [n ]elemetType ,其中 n 是数组长度, elementType 是数组元素类型 。 数组一般在创建时通过字面量初始化, 单独声明一个数组类型变量而不进行初始化是没有意义的。
// 声明 var arr0 [3] int // 声明一个有两个类型的数组,但元素默认值都是0 arr1 := [...] float64{7.0, 8.1, 9.2} // [...]后面跟字面量初始化列表 // 初始化 arr2 := [3]int {1, 2, 3} // 指定长度和初始化字面量 arr3 := [...]int {1, 2, 3} // 不指定长度,但由后面的初始化列表 arr4 := [3]int {1:1, 2:3} // 指定总长度,并通过索引值进行初始化,没有初始化的元素使用默认值 arr5 := [...]int {2:2, 5:4} // 不指定总长度,通过索引值进行初始化,没有初始化的元素使用默认值 // 数据长度由最后一个索引的元素值确定 fmt.Println("arr0:", arr0) fmt.Println("arr1:", arr1) fmt.Println("arr2:", arr2) fmt.Println("arr3:", arr3) fmt.Println("arr4:", arr4, "len=", len(arr4)) fmt.Println("arr5:", arr5, "len=", len(arr5))
编译运行结果
arr0: [0 0 0] arr1: [7 8.1 9.2] arr2: [1 2 3] arr3: [1 2 3] arr4: [0 1 3] len= 3 arr5: [0 0 2 0 0 4] len= 6 Process finished with exit code 0
3. 数组的特点
- 数组创建完长度就固定了,不能再追加元素
- 数组是值类型的,数组赋值或作为函数参数都是值拷贝
- 数组长度是数组类型的组成部分,[10]int和[20]int表示不同的类型
- 可以根据数组创建切片(切片后面文章会介绍)
- 在函数间传参是值传递的
4. 数组元素的访问
arr := [...]int{1, 2, 3} // 方式一 for i:=0; i<len(arr); i++ { fmt.Printf("arr[%d] = %d\n", i, arr[i]) } // 方式二 for k, v := range arr { fmt.Printf("arr[%d] = %d\n", k, v) }
5. 指针数组
// 声明包含5个元素的指向整数的数组 // 用整形指针初始化索引为0和1的数组元素 arr := [2]*int{0: new(int), 1: new(int)} // 为索引0和1的元素赋值 *arr[0] = 1; *arr[1] = 2; fmt.Println(arr) for _, v := range arr { // 对每个地址解引用,得到其指向的值 fmt.Println(\*v) }
代码编译运行结果如下:
[0xc00000a0c8 0xc00000a0e0] 1 2
指针数组的内存分布如下图所示:
6. 数组之间的赋值
在Go语言里,数组是一个值。这意味着数组可以用在赋值操作中。 变量名代表整个数组(和C++不同) ,同一类型的数组可以赋值给另一个数组,代码如下:
// 声明第一个包含5个元素的字符串数组 var arr1 [5] string // 声明第二个包含5个元素的字符串数组并并初始化 arr2 := [...]string{"Red", "Blue", "Green", "Yellow", "Pink"} // 把arr2复制到arr1 arr1 = arr2
复制之后,两个数组完全一样,如下图所示。
前面已经说过,数组变量的类型包括数组长度和每个元素的类型。只有这两部分都相同的数组,才是类型相同的数组,才能互相赋值,否则会报编译错误。
7. 指针数组的赋值
package main import "fmt" func main() { // 声明第一个包含3个元素的指向字符串的指针数组 var arr1 [3] *string // 声明第二个包含3个元素的指向字符串的指针数组 arr2 := [...]*string{new(string), new(string), new(string)} // 为每个元素赋值 *arr2[0] = "Red" *arr2[1] = "Blue" *arr2[2] = "Green" // 把arr2复制到arr1 arr1 = arr2 fmt.Println(arr1) // 打印每个指针元素指向的值 for k, v := range arr1 { fmt.Printf("arr[%d] = %s\n", k, *v) } }
编译运行结果如下。
arr1: [0xc0000441f0 0xc000044200 0xc000044210] arr[0] = Red arr[1] = Blue arr[2] = Green Process finished with exit code 0
赋值之后,两个数组指向同一个字符串,如下图所示。
8.多维数组
数组本身只有一个维度,但可以组合多个维度创建多维数组,代码如下所示。
package main import "fmt" func main() { // 声明一个二维整形数组,两个维度分别存储4个元素和2个元素 var arr1 [4][2]int // 使用数组字面量来声明并初始化一个二维数组 arr2 := [4][2] int{{1, 2}, {3, 4}, {5, 6}, {7, 8}} // 声明并初始化外层数组中索引为1和3的元素 arr3 := [4][2] int{1: {30, 40}, 3: {50, 60}} // 声明并初始化外层数组和内层数组的单个元素 arr4 := [4][2] int{1: {0: 10}, 2: {1: 40}} fmt.Println("arr1:", arr1) fmt.Println("arr2:", arr2) fmt.Println("arr3:", arr3) fmt.Println("arr4:", arr4) }
编译运行结果如下
arr1: [[0 0] [0 0] [0 0] [0 0]] arr2: [[1 2] [3 4] [5 6] [7 8]] arr3: [[0 0] [30 40] [0 0] [50 60]] arr4: [[0 0] [10 0] [0 40] [0 0]] Process finished with exit code 0
9. 在函数间传递数组
根据内存和性能来看,在函数间传递数组是一个开销很大的操作。在函数之间传递变量时,总是以值的方式传递的。如果这个变量是一个数组,意味着整个数组,不管有多长,都会完整复制后传递给函数。
package main // 函数foo接收一个指向100万个整形值的数组的指针 func foo(arr *[1e6] int) { // ... } func main() { // 分配一个8MB的数组 var arr [1e6]int // 将数组的地址传递给函数foo foo(&arr) }
现在将数组的地址传入函数,在栈空间分配上只需销毁8字节的内存空间。这个操作会更有效第利用内存,性能也更好。不过要意识到,因为现在传递的是指针,如果改变指针指向的值,会改变共享的内存,如果调用者不期望这种改变,就可能出现问题。
关于go语言数组,就啰嗦这么多了~~
我是lioney,年轻的后端攻城狮一枚,爱钻研,爱技术,爱分享。 个人笔记,整理不易,感谢阅读、点赞和收藏。 文章有任何问题欢迎大家指出,也欢迎大家一起交流后端各种问题!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。