数组
定义
数组是用来存储相同唯一类型的,一组已编号且长度固定的序列
声明 && 初始化数组
数组声明需要指定元素类型及元素个数,语法格式如下:
var arr_name [num] arr_type
var name [5]string{"xiaoming","xiaofang"}
声明长度为5 类型为string的数组
var arr1 [5]string
声明并初始化,不写长度用…代替
声明并初始化,写长度
package main
import "fmt"
func main() {
var arr1 [5]string
fmt.Println(arr1)
arr1[1] = "golang"
fmt.Println(arr1)
var arr2 = [...]int{1, 2, 3}
fmt.Println(arr2)
}
访问数组元素
package main
import "fmt"
func main() {
var arr1 [10]int
// 根据索引赋值
for i := 0; i < 10; i++ {
arr1[i] = i
}
// 根据索引查询数据
for i := 0; i < 10; i++ {
fmt.Println(arr1[i])
}
}
- 索引越界 编译检查报错
invalid array index 20 (out of bounds for 10-element array)
- fmt.Println(arr1[20])
指针数组
- 数组的元素除了是某个类型外,还可以是某个类型的指针
- new函数返回一个TYPE 类型的数据结构划分内存并执行默认的初始化操作,然后返回这个数据对象的指针
package main
import "fmt"
func main() {
var arr1 [5]*int
// 根据索引赋值
arr1[0] = new(int)
arr1[1] = new(int)
arr1[2] = new(int)
arr1[3] = new(int)
arr1[4] = new(int)
fmt.Println(arr1)
*arr1[0] = 10
*arr1[1] = 2
fmt.Println(arr1)
for i := 0; i < len(arr1); i++ {
fmt.Printf("[索引:%d 值是: %d]\n", i, *arr1[i])
}
/*
[0xc00001a098 0xc00001a0b0 0xc00001a0b8 0xc00001a0c0 0xc00001a0c8]
[0xc00001a098 0xc00001a0b0 0xc00001a0b8 0xc00001a0c0 0xc00001a0c8]
[索引:0 值是: 10]
[索引:1 值是: 2]
[索引:2 值是: 0]
[索引:3 值是: 0]
[索引:4 值是: 0]
*/
}
只声明不初始化 ,必须用new,空值 panic: runtime error: invalid memory address or nil pointer dereference
普通数据深拷贝的例子
- 判定依据就是 新老对象的指针%p &var是一致的,说明是浅拷贝,否则是深拷贝
package main
import "fmt"
func main() {
arr1 := [2]int{1, 2}
var arr2 [2]int
arr2 = arr1
fmt.Printf("[%v %p]\n", arr1, &arr1)
fmt.Printf("[%v %p]\n", arr2, &arr2)
arr2[1] = 20
fmt.Printf("[%v %p]\n", arr1, &arr1)
fmt.Printf("[%v %p]\n", arr2, &arr2)
}
- 两个数组指针直接复制
- 原因是内部存放的是指针,指向同一块地址,直接赋值,内容都一样,看起来是浅拷贝
- 但是其实数据copy是深拷贝
package main
import "fmt"
func main() {
var arr1 [3]*string
arr2 := [3]*string{new(string), new(string), new(string)}
*arr2[0] = "k1"
*arr2[1] = "k2"
*arr2[2] = "k3"
arr1 = arr2
fmt.Println(arr1)
fmt.Println(arr2)
for i := 0; i < 3; i++ {
fmt.Printf("[arr1 :%d :%v %v]\n", i, *arr1[i], arr1[i])
fmt.Printf("[arr2 :%d :%v %v]\n", i, *arr2[i], arr2[i])
}
fmt.Printf("[%v %p]\n", arr1, &arr1)
fmt.Printf("[%v %p]\n", arr2, &arr2)
/*
[0xc00004c240 0xc00004c250 0xc00004c260]
[0xc00004c240 0xc00004c250 0xc00004c260]
[arr1 :0 :k1 0xc00004c240]
[arr2 :0 :k1 0xc00004c240]
[arr1 :1 :k2 0xc00004c250]
[arr2 :1 :k2 0xc00004c250]
[arr1 :2 :k3 0xc00004c260]
[arr2 :2 :k3 0xc00004c260]
*/
}
数组的特点
- 固定长度:这意味着数组不可增长、不可缩减。想要扩展数组,只能创建新数组,将原数组的元素复制到新数组。
- 内存连续:这意味可以在缓存中保留的时间更长,搜索速度更快,是一种非常高效的数据结构,同时还意味着可以通过数值的方式(arr[index])索引数组中的元素。
- 固定类型:固定类型意味着限制了每个数组元素可以存放什么样的数据,以及每个元素可以存放多少字节的数据。
数组是值类型,就是深拷贝
- 举例
package main
import "fmt"
func main() {
arr1 := [2]int{1, 2}
var arr2 [2]int
arr2 = arr1
fmt.Printf("[%v %p]\n", arr1, &arr1)
fmt.Printf("[%v %p]\n", arr2, &arr2)
arr2[1] = 20
fmt.Printf("[%v %p]\n", arr1, &arr1)
fmt.Printf("[%v %p]\n", arr2, &arr2)
}
所有的值类型变量在赋值和作为参数传递时都将产生一次复制
把数组传递给函数 数组指针
- 数组是一个值类型,所有的值类型变量在赋值和作为参数传递时都将产生一次复制操作
- 从内存和性能上来看,在函数间传递数组是一个开销很大的操作。因为无论这个数组有多长,都会完整复制,并传递给函数
- 数组指针只需要很小传递
package main
import (
"fmt"
"unsafe"
)
func bigArrPoint(arr *[1e6]int64) {
fmt.Printf("[数组指针复制:大小:%d字节]\n", unsafe.Sizeof(arr))
}
func bigArr(arr [1e6]int64) {
fmt.Printf("[数组复制:大小:%d字节]\n", unsafe.Sizeof(arr))
}
func main() {
var arr [1e6]int64
bigArr(arr)
bigArrPoint(&arr)
}
多维数组
- 多维数组的典型用例是平面坐标(二维数组)和三维坐标(三维数组)
- Golang 的数组本身只有一个维度,但是我们可以组合多个数组从而创建出多维数组
package main
import (
"fmt"
)
func main() {
arr1 := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
fmt.Println(arr1)
fmt.Println(arr1[3][1])
}