Golang-复合数据类型
复合数据类型——数组和结构体——是通过组合简单类型,来表达更加复杂的数据结构。
1.数组(array)
1.简介
- 数组是一个具有相同数据类型的元素组成的固定长度的有序序列
- 数组是值类型,长度是类型的组成部分,意味着[10]int和[20]int是完全不同的两种数组类型。
- 数组元素通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推,最后一个元素为【数组元素个数-1】。
- 同类型的两个数组支持”==”和”!=”比较,但是不能比较大小。
- 数组作为参数时,函数内部不改变数组内部的值,除非是传入数组的指针。
- 数组长度最大为 2Gb。
- 注意:
1.指针数组:
[3]*int
2.数组指针:
*[3]int
2.定义
Go 语言数组声明需要指定元素类型及元素个数:
var array_name [size]data_type
释义:
array_name:数组名称
size:数组长度/大小,数组的长度/大小只能是整型【int】数,且必须是数值类型
data_type:数据类型
当定义完成数组后,就在内存中开辟了size个连续的存储空间,每个数据都存储在相应的空间内,数组中包含的每个数据被称为数组元素(element),一个数组包含的元素个数被称为数组的长度,未赋值的数组元素的值为数组类型的零值,如果是整型是0,字符串是空字符,浮点型数据是0,而不是0.0,interface{}类型的是nil。
3.初始化
定义数组的时候初始化数据,没有初始化的元素为类型零值
var array_name [size]data_type = [size]data_type{data1,data2,data3}
或者:
var array_name = [size]data_type{data1,data2,data3}
亦或者:
array_name := [size]data_type{data1,data2,data3}
或者使用自动计算长度:
array_name := [...]data_type{data1,data2,data3}
或者先定义,后赋值
var array_name = [size]data_type
array_name[0] = 1
array_name[1] = 2
array_name[2] = 3
4.访问
数组可以通过下标进行访问,下标是从0开始,最后一个元素是len-1
访问数组元素:
array_name[index]
5.遍历
5.1.索引遍历
索引范围为:0 到 len(array)-1
for index := 0; index < len(array_name); index++{
......
}
5.2.range遍历
for index,value:=range array_name{
......
}
6.越界
如果下标在数组合法范围之外,就会触发越界,访问越界不会编辑通过,不会打印;访问越界会引发panic异常
6.1.正向越界
1.访问/修改正向越界
array_name[size]
2.遍历正向越界
for index := 0;index <= len(array_name); index++{
......
}
6.2.反向越界
1.访问/修改反向越界
array_name[-1]
2.遍历反向越界
for index := -1;index < len(array_name); index++{
......
}
7.数组指针
*[size]data_type
数组指针,本质是一个指针
数组指针是指向数组地址的指针
8.指针数组
[size]*data_type
指针数组,本质是一个数组
指针数组存放的是指针的数组
9.函数参数传递
数组作为函数参数传递时,是进行的值拷贝,不是引用传递
Go语言中本质上并没有引用传递,做的是值传递,如果是普通数组,则是copy了一份数组进行操作,如果是指针类型数组,则copy的是地址值
func main() {
var array = [10]int{1, 2, 3, 4, 5}
test1(array)
fmt.Println(array)
}
// 数组作为函数参数
func test1(arr [10]int) {
arr[0] = 100
}
数组作为函数参数,是值传递,不会改变数组元素的值
把一个大数组传递给函数会消耗很多内存,可以通过:
- 传递数组的指针
- 使用数组的切片
避免消耗过多的内存
如果需要实现引用传递,就需要进行数组指针作为函数参数
Go语言中本质上并没有引用传递,做的是值传递,如果是普通数组,则是copy了一份数组进行操作,如果是指针类型数组,则copy的是地址值
数组是值类型,因此改变副本的值,不会改变本身的值
10.比较[==或!=]
如果一个数组的元素类型是可以相互比较的,那么数组类型也是可以相互比较的,可以直接通过 == 比较运算符来比较两个数组,只有当两个数组的所有元素都是相等的时候数组才是相等的。不相等比较运算符 != 遵循同样的规则。
func main() {
a := [2]int{1, 2}
b := [...]int{1, 2}
c := [2]int{1, 3}
fmt.Println(a == b, a == c, b == c) // "true false false"
//d := [3]int{1, 2}
//fmt.Println(a == d) // 编译错误:无法比较 [2]int == [3]int
}
11.斐波那契数列
使用数组实现斐波那契数列
//菲波那切数列,非递归方式实现,打印前50个数
func fab() {
var a [50]uint64
a[0] = 1
a[1] = 1
for i := 2; i < 50; i++ {
a[i] = a[i-1] + a[i-2]
}
for _, v := range a {
fmt.Println(v)
}
}
func main() {
fab()
}
12.底层实现
数组的底层是一个连续的内存地址空间,内存的最小单元是字节
13.len( )
len(array_name)用来计算数组的长度/大小
14.cap( )
cap(array_name)可以获得 和len( array_name)同样的效果,获取数组的长度
15.多维数组
Go 语言支持多维数组,常用的多维数组声明方式:
var array_name[SIZE1][SIZE2]...[SIZEN] data_type
二维数组是最简单的多维数组,二维数组本质上是由一维数组组成的。二维数组定义方式:
var array_name [ x ][ y ] data_type
释义:
data_type: Go 语言的数据类型,array_name为数组名,二维数组可认为是一个表格,x 为行,y 为列
func main() {
var array = [2][5]int{
{1,2,3,4,5},
{6,7,8,9,10},
}
for index, _ := range array {
for key, _ := range array[index] {
fmt.Printf("&array[%d][%d] = %p\n",index,key,&array[index][key])
}
}
}
16.元素反转/逆置
func main() {
var array = [...]int{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,
}
fmt.Println("逆序前:",array)
// 使用数组指针进行参数传递
reverse(&array)
fmt.Println("逆序后:",array)
}
func reverse(arr *[11]int) {
// 对半进行交换
for i := 0; i < len(arr)/2; i++ {
arr[i],arr[len(arr)-1-i] = arr[len(arr)-1-i],arr[i]
}
}
17.注意事项
- 数组是多个相同类型数据的序列,一个数组一旦声明/定义了,其长度是固定的,不能动态变化。
- 数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用。
- 数组创建后,如果没有赋值,有默认值
数值类型数组:默认值为0
字符串类型数组:默认值为""
bool数组: 默认值为false
指针数组: 默认值nil - 使用数组的步骤:
1.声明数组并开辟空间
2.给数组各个元素赋值
3.使用数组 - 数组的下标从0开始
数组下标必须在指定范围内使用,否则报panic:数组越界 - Go的数组属于值类型 ,在默认情况下是值传递,因此会进行值拷贝。数组间不会相互影响
- 如果想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)
- 长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度
- new([size]data_type)方式生成的是数组指针不是数组
- 不可以通过make方式来创建数组
- 特殊:
// 这样定义数组也是合法的
var array = [3]int{1,2,3,}// 可以在最后一个元素后面跟逗号
2.结构体
结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。
1.每个字段称为结构体的成员。通常也被称为属性。
2.结构体对应绑定的方法,通常被称为结构体的行为。