【golang学习之旅】复杂数据类型——数组

系列文章

【golang学习之旅】使用VScode安装配置Go开发环境
【golang学习之旅】报错:a declared but not used
【golang学习之旅】Go 的基本数据类型
【golang学习之旅】深入理解字符串string数据类型
【golang学习之旅】go mod tidy
【golang学习之旅】记录一次 panic case : reflect: reflect.Value.SetInt using unaddressable value
【golang学习之旅】记录一次 error case : full error output: cc1: error: unrecognized command line option
【golang学习之旅】Go程序快速开始 & Go程序开发的基本注意事项
【golang学习之旅】Go语言常用转义字符
【golang学习之旅】Go中的变量(1)
【golang学习之旅】Go中的变量------基本数据类型(1)
【golang学习之旅】Go中的变量------基本数据类型(2)
【golang学习之旅】复杂数据类型------指针 & 函数
【golang学习之旅】延迟调用------defer


  • 系列文章
  • [1. 概念](#1. 概念)
  • [2. 数组的声明与初始化](#2. 数组的声明与初始化)
    • [2.1 声明数组](#2.1 声明数组)
    • [2.2 其他类型数组](#2.2 其他类型数组)
    • [2.3 空数组](#2.3 空数组)
  • [3. 访问与操作数组](#3. 访问与操作数组)
    • [3.1 访问数组](#3.1 访问数组)
    • [3.2 遍历数组](#3.2 遍历数组)

1. 概念

有其他编程语言基础的同学应该对数组不陌生。数组是一种固定长度、同类型元素的有序集合

注意事项:如果我们想让数组元素类型为任意类型的话可以使用空接口(后面的文章会提到)作为类型

与许多其他编程语言中的数组类似,Go语言中的数组也通过索引 来访问其元素,索引从0开始,直到数组长度减1。(数组以 0 开始在所有类 C 语言中是相似的)。数组的类型由其元素类型和长度共同决定 ,因此[5]int和[10]int被视为两种完全不同的类型。因此在 Go 语言中很少直接使用数组(不同长度的数组因为类型不同无法直接赋值)。和数组对应的类型是切片,切片是可以动态增长和收缩的序列,切片的功能也更加灵活,但是要理解切片的工作原理还是要先理解数组。

下面介绍在Go语言中数组是如何声明和使用的。


2. 数组的声明与初始化

2.1 声明数组

Go语言中声明数组的语法非常直观:

go 复制代码
var arrayName [length]elementType

其中,arrayName是数组的名称,length是数组的长度,elementType是数组元素的类型。例如,声明一个包含5个整数的数组:

go 复制代码
var numbers [5]int // 定义长度为 5 的 int 型数组, 元素全部为 0

在声明数组时,也可以同时初始化其元素:

go 复制代码
var numbers = [5]int{1, 2, 3, 4, 5}

或者,使用更简洁的语法:

go 复制代码
numbers := [5]int{1, 2, 3, 4, 5}

还有其他的定义方法,比如:

go 复制代码
var b = [...]int{1, 2, 3}       // 定义长度为 3 的 int 型数组, 元素为 1, 2, 3
var c = [...]int{2: 3, 1: 2}    // 定义长度为 3 的 int 型数组, 元素为 0, 2, 3
var d = [...]int{1, 2, 4: 5, 6} // 定义长度为 6 的 int 型数组, 元素为 1, 2, 0, 0, 5, 6
  • 第一种方式定义数组,可以在定义的时候顺序指定全部元素的初始化值,数组的长度根据初始化元素的数目自动计算。
  • 第二种方式是以索引的方式来初始化数组的元素,因此元素的初始化值出现顺序比较随意。这种初始化方式和 map[int]Type 类型的初始化语法类似。数组的长度以出现的最大的索引为准,没有明确初始化的元素依然用零值初始化。
  • 第三种方式是混合了第二种和第三种的初始化方式,前面两个元素采用顺序初始化,第三第四个元素零值初始化,第五个元素通过索引初始化,最后一个元素跟在前面的第五个元素之后采用顺序初始化。

2.2 其他类型数组

数组不仅仅可以用于数值类型,还可以定义字符串数组、结构体数组、函数数组、接口数组、管道数组等等:

go 复制代码
// 字符串数组
var s1 = [2]string{"hello", "world"} // 显式指定长度为2
var s2 = [...]string{"你好", "世界"} // 使用...自动推导长度
var s3 = [...]string{1: "世界", 0: "你好", }// 使用...自动推导长度,并且显式地指定了元素的索引和值,这种方式允许我们以非顺序的方式初始化数组元素

// 结构体数组
var line1 [2]image.Point
var line2 = [...]image.Point{image.Point{X: 0, Y: 0}, image.Point{X: 1, Y: 1}}
var line3 = [...]image.Point{{0, 0}, {1, 1}}

// 图像解码器数组
var decoder1 [2]func(io.Reader) (image.Image, error)
var decoder2 = [...]func(io.Reader) (image.Image, error){
    png.Decode,
    jpeg.Decode,
}

// 接口数组
var unknown1 [2]interface{}
var unknown2 = [...]interface{}{123, "你好"}

// 管道数组
var chanList = [2]chan int{}

2.3 空数组

我们还可以定义一个空的数组:

go 复制代码
var d [0]int       // 定义一个长度为 0 的数组
var e = [0]int{}   // 定义一个长度为 0 的数组
var f = [...]int{} // 定义一个长度为 0 的数组

长度为 0 的数组在内存中并不占用空间。空数组虽然很少直接使用,但是可以用于强调某种特有类型的操作时避免分配额外的内存空间,比如用于管道的同步操作:


3. 访问与操作数组

3.1 访问数组

这里要区分一下Go语言中的数组和C语言中的数组的区别。

Go 语言中数组是值类型。一个数组变量即表示整个数组,它并不是隐式的指向第一个元素的指针(比如 C 语言的数组),而是一个完整的值。这意味着当你将数组赋值给另一个变量或将其作为函数参数传递时,实际上是在复制整个数组。这可能导致在处理大型数组时性能下降。为了避免复制数组带来的开销,可以传递一个指向数组的指针,但是数组指针并不是数组。

比如:

go 复制代码
var a = [...]int{1, 2, 3} // a 是一个数组
var b = &a                // b 是指向数组的指针

fmt.Println(a[0], a[1])   // 打印数组的前 2 个元素
fmt.Println(b[0], b[1])   // 通过数组指针访问数组元素的方式和数组类似

for i, v := range b {     // 通过数组指针迭代数组的元素
    fmt.Println(i, v)
}
  • 其中 b 是指向 a 数组的指针,但是通过 b 访问数组中元素的写法和 a 类似的。其实数组指针类型除了类型和数组不同之外,通过数组指针操作数组的方式和通过数组本身的操作类似,而且数组指针赋值时只会拷贝一个指针。但是数组指针类型依然不够灵活,因为数组的长度是数组类型的组成部分,指向不同长度数组的数组指针类型也是完全不同的

3.2 遍历数组

我们可以用 for 循环来迭代数组。下面常见的几种方式都可以用来遍历数组:

go 复制代码
    for i := range a {
        fmt.Printf("a[%d]: %d\n", i, a[i])
    }
    for i, v := range b {
        fmt.Printf("b[%d]: %d\n", i, v)
    }
    for i := 0; i < len(c); i++ {
        fmt.Printf("c[%d]: %d\n", i, c[i])
    }

for range 方式迭代的性能可能会更好一些,因为这种迭代可以保证不会出现数组越界的情形,每轮迭代对数组元素的访问时可以省去对下标越界的判断。

相关推荐
攸攸太上42 分钟前
Java面试题·解释题·单例模式、工厂模式、代理模式部分
java·学习·单例模式·代理模式·简单工厂模式
youzjuer1 小时前
triton之ttir学习
学习
Chambor_mak1 小时前
stm32单片机个人学习笔记1(简单介绍)
stm32·单片机·学习
学会沉淀。1 小时前
大二上学期详细学习计划
学习
Petal9909122 小时前
UEFI学习笔记(四):inf、dec和dsc
笔记·学习·uefi
一条晒干的咸魚2 小时前
“CSS 定位”如何工作?(补充)——WEB开发系列34
前端·css·学习·html·css3
u0112900642 小时前
Go语言变量的声明
开发语言·后端·golang
逗豆逗2 小时前
perl的学习记录——仿真regression
开发语言·学习·perl
m0_519523102 小时前
C++——入门基础
开发语言·c++·学习