深入浅出 Go 语言:数组与切片

news2025/1/19 7:57:37

深入浅出 Go 语言:数组与切片

引言

在 Go 语言中,数组和切片是两种非常重要的数据结构,用于存储和操作一组相同类型的元素。虽然它们看起来相似,但在使用上有很大的区别。理解数组和切片的区别以及如何正确使用它们,对于编写高效、可维护的 Go 程序至关重要。


1. 数组简介

1.1 什么是数组?

数组(Array)是 Go 语言中最基本的集合类型之一,用于存储固定数量的相同类型元素。数组的长度是固定的,一旦定义后不能改变。每个元素在数组中都有一个索引,索引从 0 开始,依次递增。

1.1.1 定义数组

在 Go 语言中,数组的定义方式如下:

  • 指定长度和类型var arr [N]Type,其中 N 是数组的长度,Type 是数组中元素的类型。
  • 初始化数组:可以使用花括号 {} 初始化数组,并为每个元素赋值。

以下是一个简单的例子,展示了如何定义和初始化一个整数数组:

package main

import "fmt"

func main() {
    // 定义一个长度为 5 的整数数组
    var arr [5]int

    // 初始化数组
    arr[0] = 1
    arr[1] = 2
    arr[2] = 3
    arr[3] = 4
    arr[4] = 5

    // 打印数组
    fmt.Println("数组:", arr)
}

在这个例子中,我们定义了一个长度为 5 的整数数组 arr,并为其每个元素赋值。最后,我们使用 fmt.Println 打印整个数组。

1.1.2 数组的长度

数组的长度是固定的,可以通过内置的 len() 函数获取数组的长度。例如:

package main

import "fmt"

func main() {
    arr := [5]int{1, 2, 3, 4, 5}
    length := len(arr)
    fmt.Println("数组长度:", length)
}

在这个例子中,len(arr) 返回数组的长度 5。

1.1.3 多维数组

Go 语言支持多维数组,最常见的是二维数组。你可以通过嵌套方括号来定义多维数组。例如,定义一个 3x3 的二维整数数组:

package main

import "fmt"

func main() {
    // 定义一个 3x3 的二维数组
    matrix := [3][3]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }

    // 打印二维数组
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            fmt.Printf("%d ", matrix[i][j])
        }
        fmt.Println()
    }
}

在这个例子中,我们定义了一个 3x3 的二维数组 matrix,并通过嵌套的 for 循环遍历并打印每个元素。

1.2 数组的特点

  • 固定长度:数组的长度是固定的,一旦定义后不能改变。
  • 连续内存分配:数组中的所有元素在内存中是连续存储的,这使得数组的访问速度非常快。
  • 索引访问:可以通过索引直接访问数组中的任意元素,索引从 0 开始。
  • 占用空间大:由于数组的长度是固定的,即使某些元素为空,也会占用相应的内存空间。

2. 切片简介

2.1 什么是切片?

切片(Slice)是 Go 语言中对数组的动态扩展。与数组不同,切片的长度是可以变化的,可以根据需要动态增加或减少元素。切片是对数组的一个引用,它包含三个部分:指向数组的指针、切片的长度(len)和切片的容量(cap)。

2.1.1 定义切片

在 Go 语言中,切片的定义方式如下:

  • 空切片var slice []Type,其中 Type 是切片中元素的类型。
  • 初始化切片:可以使用 make() 函数创建一个指定长度和容量的切片,也可以通过对数组进行切片操作来创建。

以下是一个简单的例子,展示了如何定义和初始化一个切片:

package main

import "fmt"

func main() {
    // 创建一个空切片
    var slice []int

    // 使用 make() 函数创建一个长度为 5、容量为 10 的切片
    slice = make([]int, 5, 10)

    // 初始化切片
    slice[0] = 1
    slice[1] = 2
    slice[2] = 3
    slice[3] = 4
    slice[4] = 5

    // 打印切片
    fmt.Println("切片:", slice)
}

在这个例子中,我们使用 make() 函数创建了一个长度为 5、容量为 10 的切片 slice,并为其每个元素赋值。最后,我们使用 fmt.Println 打印整个切片。

2.1.2 切片的长度和容量

切片的长度(len)表示切片中当前包含的元素个数,而容量(cap)表示切片背后数组的最大容量。你可以通过内置的 len()cap() 函数分别获取切片的长度和容量。例如:

package main

import "fmt"

func main() {
    slice := make([]int, 5, 10)
    length := len(slice)
    capacity := cap(slice)
    fmt.Println("切片长度:", length)
    fmt.Println("切片容量:", capacity)
}

在这个例子中,len(slice) 返回切片的长度 5,cap(slice) 返回切片的容量 10。

2.1.3 动态扩展切片

切片的一个重要特性是它可以动态扩展。当你向切片中添加元素时,如果超过了切片的容量,Go 会自动分配更大的底层数组,并将原有元素复制到新数组中。你可以使用 append() 函数向切片中添加元素。例如:

package main

import "fmt"

func main() {
    slice := []int{1, 2, 3}

    // 向切片中添加元素
    slice = append(slice, 4)
    slice = append(slice, 5)

    // 打印切片
    fmt.Println("切片:", slice)
}

在这个例子中,我们使用 append() 函数向切片 slice 中添加了两个元素 4 和 5。最后,我们使用 fmt.Println 打印整个切片。

2.1.4 切片的切片操作

你可以通过对数组或另一个切片进行切片操作来创建新的切片。切片操作的语法如下:slice[start:end],其中 start 表示起始索引,end 表示结束索引(不包括 end 位置的元素)。例如:

package main

import "fmt"

func main() {
    arr := [5]int{1, 2, 3, 4, 5}

    // 从数组中创建切片
    slice := arr[1:4]

    // 打印切片
    fmt.Println("切片:", slice)
}

在这个例子中,我们从数组 arr 中创建了一个切片 slice,包含了索引 1 到 3 的元素(即 [2, 3, 4])。

2.2 切片的特点

  • 动态长度:切片的长度是动态的,可以根据需要增加或减少元素。
  • 引用数组:切片是对数组的引用,修改切片中的元素会影响底层数组中的相应元素。
  • 内存共享:多个切片可以共享同一个底层数组,因此在操作切片时需要注意避免意外的副作用。
  • 节省空间:与数组不同,切片只占用实际使用的内存空间,不会浪费多余的内存。

3. 数组与切片的区别

虽然数组和切片都可以用于存储一组相同类型的元素,但它们在使用上有很大的区别。以下是数组和切片的主要区别:

特性数组切片
长度固定动态
内存分配连续内存引用数组,可能分散在内存中
初始化必须指定长度可以动态创建
扩展性不能扩展可以动态扩展
内存占用占用固定大小的内存只占用实际使用的内存
传递方式按值传递(复制整个数组)按引用传递(传递指针)

3.1 数组 vs 切片:按值传递 vs 按引用传递

数组是按值传递的,这意味着当你将一个数组作为参数传递给函数时,实际上传递的是数组的副本。因此,修改函数内部的数组不会影响外部的数组。例如:

package main

import "fmt"

func modifyArray(arr [3]int) {
    arr[0] = 100
}

func main() {
    arr := [3]int{1, 2, 3}
    modifyArray(arr)
    fmt.Println("数组:", arr) // 输出: [1 2 3]
}

在这个例子中,modifyArray 函数修改了传入的数组,但由于数组是按值传递的,修改不会影响外部的数组。

相比之下,切片是按引用传递的,这意味着当你将一个切片作为参数传递给函数时,实际上传递的是指向底层数组的指针。因此,修改函数内部的切片会直接影响外部的切片。例如:

package main

import "fmt"

func modifySlice(slice []int) {
    slice[0] = 100
}

func main() {
    slice := []int{1, 2, 3}
    modifySlice(slice)
    fmt.Println("切片:", slice) // 输出: [100 2 3]
}

在这个例子中,modifySlice 函数修改了传入的切片,由于切片是按引用传递的,修改会影响到外部的切片。

3.2 数组 vs 切片:内存管理

数组的内存是连续分配的,因此访问数组中的元素非常高效。然而,数组的长度是固定的,如果你需要频繁地添加或删除元素,数组可能会变得不够灵活。

切片的内存是动态分配的,它可以在需要时自动扩展。虽然切片的访问速度略低于数组,但它提供了更好的灵活性,尤其是在处理大量数据时。


4. 实际案例:实现一个简单的命令行工具

为了更好地理解数组和切片的使用方法,我们可以通过一个实际案例来加深印象。我们将实现一个简单的命令行工具,读取用户输入的一系列数字,并计算它们的平均值。

4.1 项目结构

首先,创建一个名为 avg-cli 的项目目录,并在其中初始化 Go 模块:

mkdir avg-cli
cd avg-cli
go mod init avg-cli

这将生成一个 go.mod 文件,内容如下:

module avg-cli

go 1.16

接下来,在项目根目录下创建 main.go 文件,编写命令行工具的代码。

4.2 编写代码

main.go 中编写以下代码,实现一个简单的命令行工具,读取用户输入的数字并计算平均值:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
    "strings"
)

func main() {
    reader := bufio.NewReader(os.Stdin)
    fmt.Print("请输入一系列数字(用空格分隔): ")
    input, _ := reader.ReadString('\n')
    input = strings.TrimSpace(input)

    // 将输入字符串分割成数字切片
    numbers := strings.Split(input, " ")

    // 定义一个切片来存储转换后的数字
    var nums []float64

    // 遍历输入的数字并将其转换为 float64 类型
    for _, numStr := range numbers {
        num, err := strconv.ParseFloat(numStr, 64)
        if err != nil {
            fmt.Println("无效的输入,请输入有效的数字")
            return
        }
        nums = append(nums, num)
    }

    // 计算平均值
    sum := 0.0
    for _, num := range nums {
        sum += num
    }
    average := sum / float64(len(nums))

    // 打印结果
    fmt.Printf("平均值: %.2f\n", average)
}

4.3 运行程序

编译并运行这个程序,你可以通过命令行输入一系列数字来计算它们的平均值:

go build -o avg-cli
./avg-cli

你应该会看到提示信息,输入一系列数字(用空格分隔),程序将输出这些数字的平均值。


5. 总结

通过本文的学习,你已经掌握了 Go 语言中的数组和切片的基本概念、使用方法以及它们之间的区别。数组适用于存储固定数量的元素,而切片则提供了更灵活的动态扩展能力。无论是处理静态数据还是动态数据,数组和切片都能为你提供强大的支持。


参考资料

  1. Go 官方文档
  2. Go 语言实战
  3. Go 语言官方博客

业精于勤,荒于嬉;行成于思,毁于随。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2256043.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于超级电容和电池的新能源汽车能量管理系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 超级电容特性 4.2 电池特性 5.完整工程文件 1.课题概述 基于超级电容和电池的新能源汽车能量管理系统simulink建模与仿真。分析不同车速对应的电池&#xff0c;超级电容充放电变化情况。 2.系统仿…

y3编辑器文档3:物体编辑器

文章目录 一、物体编辑器简介1.1 界面介绍1.2 复用(导入导出)1.3 收藏夹(项目资源管理)1.4 对象池二、单位2.1 数据设置2.2 表现设置2.3 单位势力和掉率设置2.4 技能添加和技能参数修改2.5 商店2.5.1 商店属性设置2.5.2 商店物品设置三、装饰物3.1 属性编辑3.2 碰撞体积四、…

「嵌入式系统设计与实现」书评:学习一个STM32的案例

本文最早发表于电子发烧友论坛&#xff1a;【新提醒】【「嵌入式系统设计与实现」阅读体验】 学习一个STM32的案例 - 发烧友官方/活动 - 电子技术论坛 - 广受欢迎的专业电子论坛!https://bbs.elecfans.com/jishu_2467617_1_1.html 感谢电子发烧友论坛和电子工业出版社的赠书。 …

Qt Designer Ui设计 功能增加

效果展示 输入密码&#xff0c;密码错误&#xff0c;弹出提示 密码正确&#xff0c;弹出提示并且关闭原窗口 代码&#xff08;只提供重要关键主代码&#xff09;lxh_log.py代码&#xff1a; import sysfrom PySide6.QtWidgets import QApplication, QWidget, QPushButtonfrom …

RT Thread Studio新建STM32F407IG工程文件编译提示错误

编译提示错误 原因: RT 源码使用4.0.3的话&#xff0c;请用STM32F4支持包的0.2.2版本&#xff0c;就不会出错了。 如果支持包用0.2.3版本的话&#xff0c;需要用RT内核4.1.0版本。0.2.3 版本更新了一些针对内核4.1.0的驱动代码&#xff0c;这几个定义都是4.1.0里的。

智能制造标准体系建设指南

一、智能制造系统架构总览 智能制造作为当今制造业转型升级的核心&#xff0c;深度整合了新一代信息技术与传统制造工艺&#xff0c;催生出一个横跨产品全生命周期、纵贯多层级组织架构&#xff0c;并彰显多元智能特性的复杂系统。这一架构从生命周期、系统层级、智能特征三个…

DApp开发与APP开发的五大区别

随着比特币与区块链技术的不断发展&#xff0c;DApp应用会逐渐成为主流。与APPAPP相比&#xff0c;DApp有许多不同之处&#xff0c;尤其是在架构、数据存储、用户隐私等方面。本文将通过五大关键点&#xff0c;深入探讨DApp开发与APP开发之间的主要区别。 1. 后端架构&#xff…

XSS(DOM)-HIGH错误总结

HIGH就不从简单的开始。 我们直接闭合HTML标签绕过 ></option></select><img srcx:alert(alt) οnerrοreval(src) altxss> 没有变化 这里应该是后端的问题&#xff0c;试试锚点注入 English#<script>alert(xss)</script> 这里不知道什么…

Mitel MiCollab 企业协作平台 任意文件读取漏洞复现(CVE-2024-41713)

0x01 产品简介 Mitel MiCollab是加拿大Mitel(敏迪)公司推出的一款企业级协作平台,旨在为企业提供统一、高效、安全的通信与协作解决方案。通过该平台,员工可以在任何时间、任何地点,使用任何设备,实现即时通信、语音通话、视频会议、文件共享等功能,从而提升工作效率和…

【PostgreSQL系列】列类型从整数转换为 UUID

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【原生js案例】webApp实现鼠标移入移出相册放大缩小动画

图片相册这种动画效果也很常见&#xff0c;在我们的网站上。鼠标滑入放大图片&#xff0c;滑出就恢复原来的大小。现在我们使用运动定时器来实现这种滑动效果。 感兴趣的可以关注下我的系列课程【webApp之h5端实战】&#xff0c;里面有大量的css3动画效果制作原生知识分析&…

Java项目实战II基于微信小程序的消防隐患在线举报系统(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着城市化进程的加快&…

饲料颗粒机全套设备有哪些机器组成

颗粒饲料机主要用于将各种饲料原料&#xff08;如玉米、豆粕、麦麸、鱼粉等&#xff09;进行混合、压制&#xff0c;制成颗粒状的饲料。这种饲料不仅方便储存和运输&#xff0c;还能提高动物的采食效率和饲料利用率。同时&#xff0c;颗粒饲料在加工过程中能灭部分微生物和寄生…

Free-RTOS实现LED闪烁

开发板&#xff1a;正点原子探索者 F407 LED定时定时闪烁 本次实验验证&#xff1a; 配置文件 1、打开CubeMX 2、选择芯片型号&#xff0c;然后点击开始项目 3、配置时钟 配置烧录引脚&#xff0c;与FreeRTOS系统时钟 选择FreeRTOS 这里已经默认有一个任务&#xff…

【书生大模型实战营】Linux 基础知识-L0G1000

前言&#xff1a;书生大模型实战营是上海人工智能实验室开展的大模型系列实践活动&#xff0c;提供免费算力平台&#xff0c;学员通过闯关式任务&#xff0c;可获得免费算力和存储&#xff0c;助力项目实践。本期是第4期&#xff0c;时间从十一月份开始&#xff0c;持续到十二月…

Python实现Excel中数据条显示

Python中要实现百分比数据条的显示&#xff0c;可以使用pandas库&#xff0c;pandas图表样式的设置与Excel中的条件格式设置比较类似&#xff0c;比如Excel里常用的数据条的用法&#xff0c;在pandas中使用代码进行高亮显示&#xff0c;用来突出重点数据&#xff0c;下面一起来…

R语言机器学习论文(三):特征提取

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据一、数据归一化二、离散型分类变量的编码三、筛选特征四、重要特征五、输出结果六、总结系统信息介绍 在数据分析和机器学习项目中,经常需要对数据进行预…

Java 初学者的第一个 SpringBoot 登录系统

Java 初学者的第一个 SpringBoot 登录系统 对编程初学者而言&#xff0c;都存在一个 “第一个系统” 的问题。有些学习者找不到自己的 “第一个系统”&#xff0c;他们即使再努力也没有办法了解完整的系统&#xff0c;即使他们把教科书里的所有程序都跑通了。但是&#xff0c;…

PlantUML——类图

背景 类图是UML模型中的静态视图&#xff0c;其主要作用包括&#xff1a; 描述系统的结构化设计&#xff0c;显示出类、接口以及它们之间的静态结构和关系。简化对系统的理解&#xff0c;是系统分析与设计阶段的重要产物&#xff0c;也是系统编码和测试的重要模型依据。 在U…