乔拓云智能建站系统,视频怎么转wordpress,建立网站需要分几部进行,网络推广方案pptGo复合类型之数组 文章目录 Go复合类型之数组一、数组(Array)介绍1.1 基本介绍1.2 数组的特点 二、数组的声明与初始化2.1 数组声明2.2 常见的数据类型声明方法2.3 数组的初始化方式一#xff1a;使用初始值列表初始化数组方法二#xff1a;根据初始值个数自动推断数组长度方…Go复合类型之数组 文章目录 Go复合类型之数组一、数组(Array)介绍1.1 基本介绍1.2 数组的特点 二、数组的声明与初始化2.1 数组声明2.2 常见的数据类型声明方法2.3 数组的初始化方式一使用初始值列表初始化数组方法二根据初始值个数自动推断数组长度方法三通过指定索引值初始化数组 三、数组的常用操作3.1 数组的遍历方法1使用 for 循环遍历方法2使用 for range 遍历 3.2 获取数组长度3.3 访问数组元素3.4 修改数组元素3.5 数组的切片3.6 数组的比较3.7 数组作为函数参数 四、数组类型在内存中的实际表示五、数组是值类型(数组拷贝和传参)六、多维数组6.1 二维数组6.2.1 二维数组的定义6.2.2 二维数组的遍历 6.3 多维数组介绍6.4 多维数组声明与初始化 七、Go 数组和以往认知的数组的区别 一、数组(Array)介绍
1.1 基本介绍
Go语言中数组是一个值类型value type。数组就是指一系列同一类型数据的集合。数组是一个长度固定的、由同构类型元素组成的连续序列。数组类型包含两个重要属性元素的类型和数组长度元素的个数。数组长度在定义时确定,不可变更。数组类型表示为:[大小]T,比如[5]int表示拥有5个int元素的数组。如果将数组作为函数的参数类型则在函数调用时该参数将发生数据复制。因此在函数体中无法修改传入的数组的内容因为函数内操作的只是所传入数组的一个副本。
1.2 数组的特点
长度固定一旦声明和初始化数组的长度就不能更改。类型一致所有数组元素必须是相同类型。连续的内存分配数组的所有元素在内存中是连续分配的这有助于快速访问元素。值类型数组是值类型它们在传递给函数时会被复制而不是引用。
二、数组的声明与初始化
2.1 数组声明
定义方式如下
var arr [N]T
// 或者使用短变量申明
arr : [N]T{}这里我们声明了一个数组变量 arr其中
arr为数组变量名N表示数组长度T表示数组存储类型
**如果两个数组类型的元素类型 T 与数组长度 N 都是一样的那么这两个数组类型是等价的如果有一个属性不同它们就是两个不同的数组类型。**下面这个示例很好地诠释了这一点
func foo(arr [5]int) {}
func main() {var arr1 [5]intvar arr2 [6]intvar arr3 [5]stringfoo(arr1) // okfoo(arr2) // 错误[6]int与函数foo参数的类型[5]int不是同一数组类型foo(arr3) // 错误[5]string与函数foo参数的类型[5]int不是同一数组类型
} 在这段代码里arr2 与 arr3 两个变量的类型分别为[6]int 和 [5]string前者的长度属性与[5]int 不一致后者的元素类型属性与[5]int 不一致因此这两个变量都不能作为调用函数 foo 时的实际参数。
2.2 常见的数据类型声明方法
var a [5]byte //长度为5的数组每个元素为一个字节
var b [2*N] struct { x, y int5 } //复杂类型数组
var c [5]*int // 指针数组
var d [2][3]int //二维数组
var e [2][3][4]int //等同于[2]([3]([4]int))2.3 数组的初始化
方式一使用初始值列表初始化数组
这种方式在声明数组的同时通过提供初始值列表来初始化数组元素。如果没有为数组的每个元素提供初始值剩余的元素将会使用默认值。对于数值类型如int默认值为0对于字符串类型如string默认值为空字符串。 var testArray [3]int //数组会初始化为int类型的零值var numArray [3]int{1, 2} //使用指定的初始值完成初始化var strArray [3]string{}fmt.Println(testArray) //[0 0 0]fmt.Println(numArray) //[1 2 0]fmt.Println(strArray) //[ ] 默认值空字符串方法二根据初始值个数自动推断数组长度
在这种方式下你可以在声明数组时省略长度并使用...操作符编译器会根据提供的初始值的个数自动推断数组的长度。这使得代码更加简洁不需要显式指定数组的长度。 arr : [...]int{1, 2, 3} // [1 2 3]fmt.Println(arr) // [1 2]fmt.Printf(type of numArray:%T\n, arr) // type of numArray:[3]int方法三通过指定索引值初始化数组
这种方式允许你在数组的指定索引位置提供初始值其他位置会被初始化为默认值。在示例中a[1]被初始化为1a[3]被初始化为5其他位置默认为0。
func main() {a : [...]int{1: 1, 3: 5}fmt.Println(a) // [0 1 0 5]fmt.Printf(type of a:%T\n, a) //type of a:[4]int
}三、数组的常用操作
3.1 数组的遍历
遍历数组有两种方法使用for循环和使用for range语句
方法1使用 for 循环遍历
var a [...]string{贾, 维, 斯}
for i : 0; i len(a); i {fmt.Println(a[i])
}这是传统的for循环遍历数组的方式它使用一个循环变量i来迭代数组的索引然后使用a[i]来访问数组的元素。这种方式适用于需要访问数组索引或按照索引进行操作的情况。
方法2使用 for range 遍历 var a [...]string{贾, 维, 斯}for index, value : range a {fmt.Println(index, value)}for range语句更加简洁和直观。它会返回数组的索引和对应的值这使得遍历数组变得非常方便。通常情况下使用for range遍历数组更加推荐特别是当你只需要访问数组的值而不需要索引时。
需要注意的是for range遍历数组会创建一个值的拷贝而不是原始数组的引用。如果你需要在循环内修改数组元素的值并且希望这些修改在循环结束后对原始数组生效那么你应该使用for循环因为它允许你直接访问数组的元素。
3.2 获取数组长度
在Go语言中数组长度在定义后就不可更改在声明时长度可以为一个常量或者一个常量表达式常量表达式是指在编译期即可计算结果的表达式。数组的长度是该数组类型的一个内置常量可以用Go语言的内置函数len()来获取。
arrLength : len(arr)举个例子
arr : [5]int{10, 20, 30, 40, 50}
length : len(arr) // 获取数组的长度length的值为53.3 访问数组元素 数组的下标值是从 0 开始的 使用数组变量名加索引下标的方式就可以访问数组对应位置的元素。
var arr [6]int{11, 12, 13, 14, 15, 16}
fmt.Println(arr[0], arr[5]) // 11 16
fmt.Println(arr[-1]) // 错误下标值不能为负数
fmt.Println(arr[8]) // 错误小标值超出了arr的长度范围3.4 修改数组元素
同样是通过数组变量名加索引下标的方式就可以修改数组对应位置的元素。
arr : [5]int{1, 2, 3, 4, 5}arr[0] 100 // 修改数组第一个元素
arr[1] 200 // 修改数组第二个元素fmt.Println(arr) // 输出:[100 200 3 4 5]3.5 数组的切片
使用切片来从数组中创建一个动态长度的子集。切片是对数组的引用因此它们与原始数组共享底层数据。
arr : [5]int{10, 20, 30, 40, 50}
slice : arr[1:4] // 创建一个包含arr的索引1到3的切片slice的值为{20, 30, 40}3.6 数组的比较
你可以使用运算符来比较两个数组是否相等。两个数组相等的条件是它们的长度和元素都相同。 arr1 : [3]int{1, 2, 3}arr2 : [3]int{1, 2, 3}isEqual : arr1 arr2 fmt.Println(isEqual) // isEqual为true3.7 数组作为函数参数
数组是值类型当它作为函数参数传递时会复制整个数组。这意味着在函数内对数组的修改不会影响原始数组。
func modify(arr [3]int) {arr[0] 100
}func main() {a : [3]int{1, 2, 3}modify(a)fmt.Println(a) // 输出[1, 2, 3]
}
// 在modify函数中,我们把数组arr的第一个元素修改为了100。但是回到main函数后,打印数组a时,它的第一个元素仍然是1。如果需要在函数内修改数组,需要传入数组指针:
func modify(arr *[3]int) {(*arr)[0] 100
}func main() {a : [3]int{1, 2, 3}modify(a)fmt.Println(a) // 输出[100 2 3]
}四、数组类型在内存中的实际表示
了解了数组类型的定义和操作后我们再来看看数组类型在内存中的实际表示是怎样的这是数组区别于其他类型也是我们区分不同数组类型的根本依据。
**数组类型不仅是逻辑上的连续序列而且在实际内存分配时也占据着一整块内存。**Go 编译器在为数组类型的变量实际分配内存时会为 Go 数组分配一整块、可以容纳它所有元素的连续内存如下图所示 我们从这个数组类型的内存表示中可以看出来这块内存全部空间都被用来表示数组元素所以说这块内存的大小就等于各个数组元素的大小之和。如果两个数组所分配的内存大小不同那么它们肯定是不同的数组类型。Go 提供了预定义函数 len 可以用于获取一个数组类型变量的长度通过 unsafe 包提供的 Sizeof 函数我们可以获得一个数组变量的总大小如下面代码
var arr [6]int{1, 2, 3, 4, 5, 6}
fmt.Println(数组长度, len(arr)) // 6
fmt.Println(数组大小, unsafe.Sizeof(arr)) // 48数组大小就是所有元素的大小之和这里数组元素的类型为 int。在 64 位平台上int 类型的大小为 8数组 arr 一共有 6 个元素因此它的总大小为 6x848 个字节。
五、数组是值类型(数组拷贝和传参)
数组是值类型赋值和传参会复制整个数组。因此改变副本的值不会改变本身的值。
func modifyArray(x [3]int) {x[0] 100
}func modifyArray2(x [3][2]int) {x[2][0] 100
}
func main() {a : [3]int{10, 20, 30}modifyArray(a) //在modify中修改的是a的副本xfmt.Println(a) //[10 20 30]b : [3][2]int{{1, 1},{1, 1},{1, 1},}modifyArray2(b) //在modify中修改的是b的副本xfmt.Println(b) //[[1 1] [1 1] [1 1]]
}注意
数组支持 ““、”!” 操作符因为内存总是被初始化过的。[n]*T表示指针数组*[n]T表示数组指针 。
六、多维数组
6.1 二维数组
二维数组本质就是数组中又嵌套数组
6.2.1 二维数组的定义
组是最简单的多维数组二维数组本质上是由一维数组组成的。二维数组定义方式如下
var arrayName [ x ][ y ] variable_typevariable_type 为 Go 语言的数据类型arrayName 为数组名二维数组可认为是一个表格x 为行y 为列下图演示了一个二维数组 a 为三行四列 举个栗子二维数组定义并初始化
func main() {a : [3][2]string{{北京, 上海},{广州, 深圳},{成都, 重庆},}fmt.Println(a) //[[北京 上海] [广州 深圳] [成都 重庆]]fmt.Println(a[2][1]) //支持索引取值:重庆
}6.2.2 二维数组的遍历
func main() {a : [3][2]string{{北京, 上海},{广州, 深圳},{成都, 重庆},}for _, v1 : range a {for _, v2 : range v1 {fmt.Printf(%s\t, v2)}fmt.Println()}
}输出
北京 上海
广州 深圳
成都 重庆 注意 多维数组只有第一层可以使用...来让编译器推导数组长度。例如
//支持的写法
a : [...][2]string{{北京, 上海},{广州, 深圳},{成都, 重庆},
}
//不支持多维数组的内层使用...
b : [3][...]string{{北京, 上海},{广州, 深圳},{成都, 重庆},
}6.3 多维数组介绍
多维数组是一种数组的扩展它允许在一个数组中存储多个维度的数据。在许多编程语言中通常可以创建二维数组、三维数组甚至更高维度的数组。多维数组在处理具有多个维度的数据集时非常有用比如矩阵、图像等。
多维数组的基本思想是使用多个索引来引用数组中的元素。例如二维数组可以看作是一个表格需要两个索引来定位某个元素第一个索引表示行号第二个索引表示列号。三维数组则需要三个索引依此类推。以下是多维数组的一些基本概念
数组类型自身也可以作为数组元素的类型这样就会产生多维数组。多维数组在Go语言中不太常用,大多数情况下使用切片(slice)就可以实现多维数据结构。但是在某些需要明确数组大小的情况下,多维数组也会用到。
6.4 多维数组声明与初始化
Go 语言支持多维数组以下为常用的多维数组声明方式
var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type比如下面的变量 mArr 的类型就是一个多维数组[2][3][4]int
var mArr [2][3][4]int多维数组也不难理解我们以上面示例中的多维数组类型为例我们从左向右逐维地去看这样我们就可以将一个多维数组分层拆解成这样
我们从上向下看首先我们将 mArr 这个数组看成是一个拥有两个元素且元素类型都为[3] [4]int 的数组就像图中最上层画的那样。这样mArr 的两个元素分别为 mArr[0]和 mArr [1]它们的类型均为[3] [4]int也就是说它们都是二维数组。
而以 mArr[0]为例我们可以将其看成一个拥有 3 个元素且元素类型为[4]int 的数组也就是图中中间层画的那样。这样 mArr[0]的三个元素分别为 mArr[0][0]、mArr[0][1]以及 mArr[0][2]它们的类型均为[4]int也就是说它们都是一维数组。
图中的最后一层就是 mArr[0]的三个元素以及 mArr[1]的三个元素的各自展开形式。以此类推你会发现无论多维数组究竟有多少维我们都可以将它从左到右逐一展开最终化为我们熟悉的一维数组。
不过虽然数组类型是 Go 语言中最基础的复合数据类型但是在使用中它也会有一些问题。数组类型变量是一个整体这就意味着一个数组变量表示的是整个数组。这点与 C 语言完全不同在 C 语言中数组变量可视为指向数组第一个元素的指针。这样一来无论是参与迭代还是作为实际参数传给一个函数 / 方法Go 传递数组的方式都是纯粹的值拷贝这会带来较大的内存拷贝开销。
这时你可能会想到我们可以使用指针的方式来向函数传递数组。没错这样做的确可以避免性能损耗。其实Go 语言为我们提供了一种更为灵活、更为地道的方式 切片来解决这个问题。
七、Go 数组和以往认知的数组的区别
在Go语言中数组和一般认知中的数组如C、C等语言中的数组有一些重要区别和特点。下面是关于Go语言中数组的一些特点和区别
固定长度的序列 与一般认知中的数组类似Go中的数组也是一种同一种数据类型的固定长度的序列。这意味着一旦数组被定义其长度不能更改。数组定义 在Go中数组的定义形式为var a [len]Type其中len表示数组的长度Type表示数组元素的类型。例如var a [5]int定义了一个包含5个整数的数组。长度是类型的一部分 数组的长度是数组类型的一部分。因此[5]int和[10]int是不同的类型。这意味着不能将一个长度为5的数组赋值给一个长度为10的数组它们是不兼容的。下标访问 类似于其他语言的数组Go中的数组也可以通过下标进行访问下标从0开始最后一个元素的下标是len-1。可以使用for循环或range来遍历数组。访问越界 如果尝试访问数组中的索引超出合法范围Go将会引发运行时错误称为越界访问而不会继续执行程序。这是一种保护机制以防止访问无效的内存。数组是值类型 在Go中数组是值类型这意味着当你将一个数组赋值给另一个数组时实际上是将整个数组的副本复制给了目标数组而不是引用。因此在对副本进行更改时不会影响原始数组。支持比较操作 Go中的数组支持相等和不等!操作符因为数组在定义后会被初始化所以它们是可比较的。指针数组和数组指针 Go支持指针数组和数组指针的概念。指针数组是一个包含指向某种类型的指针的数组而数组指针是指向数组的指针。