24、Go 复合数据类型 - 数组

作者: 温新

分类: 【Go基础】

阅读: 819

时间: 2023-08-29 15:58:43

hi,我是温新

Go 语言中,复合类型有:array(数组)、slice(切片)、哈希表(map)、解构体(struct)。

数组的概念

**数组是相同类型的一组数据构成的长度固定的序列。**其数据类型包含基本数据类型、复合数据类型和自定义类型。数组中的每一项是数组的元素。数组名是数组的唯一标识,数组的每一个元素都是没有名字的,只能通过索引下标进行访问。

数组声明的语法

Go 语言中,数组声明需要指定元素类型及元素个数。语法如下:

// 格式
var 变量名 [数组长度] 数据类型

定义数组

1、数组一旦声明,其存储的数据类型和长度便不能修改;

2、数组初始化时,其每个元素都被初始化为对应类型的零值;

3、数组字面量初始化数组时,数组元素个数不能大于指定的数组长度;

声明数组

package main

import "fmt"

func main() {
    // 声明数组,不赋值,默认用对应类型的零值
    var array [5]int
    // 因此数组的值都是 0
    fmt.Println(array)

    // 数组赋值
    array[0] = 10
    fmt.Println(array)
}

输出结果为

[0 0 0 0 0]

数组字面量创建数组

使用数组字面量可以快速创建并初始化数组,数组字面量可以声明数组中元素的数量,并指定每个元素的值。

指定数组长度

package main

import "fmt"

func main() {
    /**
     * 声明一个包含 5 个元素的整型数组
     * 使用具体值初始化每个元素
     */
    arr := [5]int{1, 2, 3, 4, 5}
    fmt.Println(arr)
}

不指定数组长度,使用 ...

package main

import "fmt"

func main() {
    /**
     * 声明一个字符串型数组
     * 使用具体值初始化数组的每个元素
     * 数组容量由初始化值的数据决定
     */
    arr := [...]string{"王美丽", "郝帅"}
    fmt.Println(arr)
}

为数组下标指定值

package main

import "fmt"

func main() {
    // 为数组下标指定值
    arr := [5]int{0: 10, 2: 30}
    fmt.Println(arr)
}

输出结果

// 结果
[10 0 30 0 0]

数组的访问与修改

Go 语言中,数组通过下标(索引位置)来读取或修改数组元素。数组下标从 0 开始,第一个元素索引为 0,第二个为 1,以此类推。

访问数组元素

package main

import "fmt"

func main() {
    arr := [3]string{"王美丽", "大漂亮", "王小丽"}
    // 通过下标取出数组元素
    fmt.Println(arr[1])

    // 遍历数组元素
    for i := 0; i < len(arr); i++ {
        fmt.Printf("数组下标 %d 的值是 %s \n", i, arr[i])
    }
}

输出结果

大漂亮
数组下标 0 的值是 王美丽
数组下标 1 的值是 大漂亮
数组下标 2 的值是 王小丽

通过 Go 的内置函数 len() 可以获取数组的长度。

修改数组元素

1、访问数组元素时,使用[下标] 的方式,修改数组元素的值时,也使用这种方式。

package main

import "fmt"

func main() {
    // 声明一个包含 5 个元素的整型数组,并初始化值
    arr := [5]int{10, 20, 30, 40, 50}
    // 通过索引修改数组元素的值
    arr[0] = 100

    fmt.Println(arr) // 输出结果 [100 20 30 40 50]
}

2、数组的值可以是指针,使用 * 访问元素指针所指向的值

package main

import "fmt"

func main() {
    /**
     * 声明包含 5 个元素的整型数组
     * 使用指针初始化化索引为 0 和 1 的数组元素
     **/
    arr := [5]*int{0: new(int), 1: new(int)}
    *arr[0] = 10
    *arr[1] = 20
    fmt.Println(*arr[0])
    fmt.Println(*arr[1])
}

这里需要需要注意,声明指针类型数组时使用了 *int,初始化数组元素值时使用了 new 函数。关于 new 函数会在后续的文章中学习。

3、数组是值类型

在 Go 语言中,数组是一个值类型,而不是引用类型。当数组被分配给一个新变量时,会将原数组复制出一份分配给新变量。因此,对新变量进行修改时,原数组不受影响。

package main

import "fmt"

func main() {
    // 声明并初始化一个数组
    names := [2]string{"王美丽", "王小丽"}
    // 数组数组值给新变量
    copyNames := names
    // 修改新变量的数组元素值
    copyNames[0] = "郝帅"

    fmt.Println("原数组的值", names)
    fmt.Println("新数组的值", copyNames)
}

输出结果

原数组的值 [王美丽 王小丽]
新数组的值 [郝帅 王小丽]

从输出结果中可以看出,修改新变量数组元素的值不会影响原始数组。

数组作为一个变量类型,它包括数组长度每个元素的类型两个部分。只有这个两个部分全部相同的数组才是类型相同的数组,才可以进行相互赋值,不然会报错。

如果是指针数组进行复制,那么只会复制指针的值,而不会复制指针所指向的值,而此时无论是修改哪个数组的值,彼此都会受到影响,案例如下:

package main

import "fmt"

func main() {
    // 指针赋值
    arr := [2]*int{0: new(int), 1: new(int)}
    c := arr
    *c[0] = 10
    fmt.Println("原数组的值", *arr[0])
    fmt.Println("新数组的值", *c[0])
}

输出结果如下

原数组的值 10
新数组的值 10

多维数组

数组本身只有一个维度,但可以组合多个数组创建多维数组。多维数组用于管理具有依赖关系的数据。

多维数组声明语法

// 格式
var 变量名 [组度][长度]...[长度N] 变量类型

二维数组

二维数组是最简单的数组,其本质也是一个一维数组,只是数组成员由基本数据类型变成了构造数据类型。

二维数组定义方式:

// 格式
var 变量名 [长度][长度] 变量类型

二维数组案例

声明二维数组

package main

import "fmt"

func main() {
    /**
     * 方式一 先声明后赋值
     **/
    // 声明一个二维整型数组,两个维度分别存储 4 个元素和 2 个元素
    var arr [2][2]int
    // 赋值
    arr = [2][2]int{
        {10, 11},
        {20, 21},
    }
    fmt.Println(arr)

    /**
     * 方式二:数组字面量声明并初始化数组
     **/
    arr1 := [2][2]int{
        {10, 10},
        {20, 20},
    }
    fmt.Println(arr1)
}

多维数组的访问还是使用 []

二维数组的操作

package main

import "fmt"

func main() {
    arr := [2][2]int{
        {10, 10},
        {20, 20},
    }

    // 访问数组元素
    fmt.Println(arr[0][1])
    // 修改数组元素
    arr[0][0] = 100
    fmt.Println(arr)
    // 遍历二维数组
    for i := 0; i < len(arr); i++ {
        for j := 0; j < len(arr[0]); j++ {
            fmt.Printf("arr[%d][%d] = %d\n", i, j, arr[i][j])
        }
    }
}

三维数组

三维数组本质上还是一个数组,只是数组成员由基本数据类型变成了构造数据类型(二维数组),其语法如下:

var 变量名 [长度][长度][长度]

将数组传递给函数

在 Go 语言中,数组是一个值类型,所有值类型变量在赋值和作为参数传递时都会产生一次复制动作。若将数组作为函数的参数,那么函数在调用时数组会复制一份作为函数参数。因此,在函数体中无法修改传入的数组的内容,因为函数体内操作的数组只是一个副本。

从内存和性能角度看,在函数间传递数组是一个开销很大的操作。尤其很大的数组。这时可以传递数组的指针。案例如下:

package main

import "fmt"

func main() {
    arr := [5]int{1, 2, 3, 4, 5}
    // 数组值传递
    foo(arr)
    // 传递指针
    boo(&arr)
    fmt.Println("原数组:", arr)
}

func foo(array [5]int) {
    fmt.Println(array)
}

func boo(array *[5]int) {
    fmt.Println("boo 函数传递指针后的数组的值", *array)
    //// 修改数组的值
    (*array)[0] = 100
    fmt.Println("boo 函数内通过指针修改数组元素值", *array)
}

输出结果

[1 2 3 4 5]
boo 函数传递指针后的数组的值 [1 2 3 4 5]
boo 函数内通过指针修改数组元素值 [100 2 3 4 5]
原数组: [100 2 3 4 5]

传递指针可以有效地利用内存,性能也更好。由于传递的是指针,若改变指针指针的向,也会改变共享内存中的值。使用切片可以处理这类共享问题。

请登录后再评论