Go基础编程 - 05 - 数组与切片

目录

    • 1. 数组
    • 2. 切片
      • 2.1. slice 声明、初始化
      • 2.2. slice 操作
      • 2.3. append() 追加切片、扩容
      • 2.4. 字符串和切片
    • 3. Copy
    • 4. Array、Slice 内存布局

上一篇:基本类型、常量和变量

1. 数组

数组是同一种类型固定长度的序列(有长度、类型构成)。一但定义,长度不可改变。

  • 数组为值类型,赋值和传参会复制整个数组,而不是指针。
  • 数组声明时,必须固定长度;长度是数组类型的一部分,不同长度的数组类型表示不同的类型([2]int[3]int 为不同的数组类型);
  • 数组未赋值,则初始化为类型缺省值(零值)。
  • 内置函数lencap都返回数组长度。
  • 越界访问产生panic。
  • 指针数组:[n]*T,数组指针:*[n]T
  • 值拷贝行为会造成性能问题,通常会建议使用 slice,或数组指针。
package main

import (
	"fmt"
	"reflect"
)

func main() {
    // 初始化
    var arr1 [5]int
    var arr2 = [5]int{0, 1, 2, 3, 4}
    var arr3 = [3]int{0, 2: 4}
    var arr4 = [...]int{1, 2, 3}
    fmt.Println(arr1, arr2, arr3, arr4)

    // 长度是数组类型的一部分,不同长度的数组类型为不同类型
    if reflect.TypeOf(arr1) == reflect.TypeOf(arr2) {
        fmt.Println("arr1 和 arr2 数据类型相同!")
    }
    if reflect.TypeOf(arr1) != reflect.TypeOf(arr3) {
        fmt.Println("arr1 和 arr3 数据类型不相同!")
    }

    // 数组为值类型:赋值和传参复制整个数组
    arr1copy := arr1
    arr1copy[0] = 10
    fmt.Println(arr1, arr1copy)

    // 值拷贝行为会造成性能问题,通常会建议使用 slice,或数组指针。
    fn := func(arr [5]int) {
        arr[1] = 20
    }
    fn(arr1)
    fmt.Println(arr1)

    // 指针数组:[n]*T
    var pa = [2]*int{new(int)}
    *pa[0] = 10
    pa[1] = new(int) // 引用类型必须先分配内存,须使用new分配内存
    *pa[1] = 20      // 若不分配内存(注释上一行代码),则产生panic: runtime error: invalid memory address or nil pointer dereference
    fmt.Printf("pa:%v  \n", pa)

    // 数组指针:*[n]T
    var ap *[3]int // var ap = new([3]int)
    ap = new([3]int)
    *ap = [3]int{4, 5, 6}
    fmt.Printf("ap:%v  psr:%p  type:%T  \n", *ap, ap, ap)

    // 多维数组
    multArr := [...][2]int{{0, 0}, {1, 1}, {2, 2}}
    fmt.Println(multArr)
}

2. 切片

切片并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。

  • 切片是引用类型;底层数据结构是一个数组,可以看作是对数组某个连续片段的引用。
  • 切片是可变长的(包含长度、容量)。
  • 长度:用len()函数求长度,表示可用元素数量,读写操作不能超过该限制。
  • 容量:用cap()函数求最大容量,为切片底层数组的长度。
  • 扩容及规律: 扩容通常以 2 倍容量重新分配底层数组;当原有切片容量大于 1024 时,以 1.25 倍扩容。
  • 若 slice == nil,则 lencap结果都等于 0。

2.1. slice 声明、初始化

package main

import "fmt"

func main() {
    // 声明
    var s []int     
    if s == nil {
        fmt.Println("s is nil")
    }
    s1 := []int{1, 2, 3, 4}
    // 使用 make()
    var s2 []int = make([]int, 0)
    s3 := make([]int, 5)
    s4 := make([]int, 5, 8)

    fmt.Println(s, s1, s2, s3, s4)
    fmt.Println("Cap:", cap(s), cap(s1), cap(s2), cap(s3), cap(s4))

    // 从数组切片
    arr := [5]int{1, 2, 3, 4, 5}
    var s5 []int
    s5 = arr[1:4] // 前包后不包
    fmt.Println(s5, len(s5), cap(s5))
}

2.2. slice 操作

  • slice 的索引起点为 0。
  • 切片截取:s[i:j:max],满足 i <= j <= max;其中,i为起始索引(包含),省略值 0j结束索引(不包含),省略值len(s)max为截取最大索引,省略值len(s)
  • 截取新切片长度 = j - i
  • 截取新切片容量 = max - i
  • 截取新切片开始索引非 0,指针地址指向底层数组中新切片开始索引所在的地址;起始地址相同的slice指针地址相同,例:s[i], s[i:], s[i:j], s[i:j:max] 的指针引用地址都相同。
package main

import "fmt"

func main() {
    s := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}

    v := s[0]
    fmt.Println(v)

    s1 := s[2:5]
    s2 := s[2:8]
    s3 := s[:10:12]

    fmt.Println("val", v, s1, s2, s3)
    fmt.Println("len", len(s1), len(s2), len(s3))
    fmt.Println("cap", cap(s1), cap(s2), cap(s3))
    fmt.Printf("prs %p %p %p \n", s1, s2, s3)
}

2.3. append() 追加切片、扩容

append() 向 slice 尾部添加数据,返回新的 slice 对象。

扩容通常以 2 倍容量重新分配底层数组。在大批量添加数据时,建议一次性分配足够大的空间,以减少内存分配和数据复制开销。或初始化足够长的 len 属性,改用索引号进行操作。及时释放不再使用的 slice 对象,避免持有过期数组,造成 GC 无法回收。

package main

import "fmt"

func main() {
    arr := []int{0, 1, 2, 3, 4}
    fmt.Println("len =", len(arr), "cap =", cap(arr))       // len = 5  cap = 5

    arr1 := append(arr, 5)       // 追加数据,超出原切片容量,则进行扩容
    fmt.Println("len =", len(arr1), "cap =", cap(arr1))     // len = 6  cap = 10
    fmt.Printf("prs: %p %p \n", arr, arr1)  // 对比底层数组起始指针,扩容后重新分配底层数组,与原数组无关。

    arr1 = append(arr1, []int{6, 7, 8, 9, 10}...)
    fmt.Println("len =", len(arr1), "cap =", cap(arr1))     // len = 11 cap = 20
}

2.4. 字符串和切片

string 底层就是一个 byte/rune 的数组,因此,也可以进行切片操作。

package main

import "fmt"

func main() {
    str := "Hello, Chain"

    s1 := str[:5]
    println(s1) // Hello

    //string 本身是不可变的,因此要改变string中字符;需将字符转为slice,进行更改,再转为string
    //str[0] = 'X'     // 报错:cannot assign to str[0] (value of type byte)

    s := []byte(str) // 英文字符使用 byte,代表ASCII码的一个字符
    // 赋值需要使用单引号(''),单引号定义一个字符,双引号定义个字符串
    s = append(s, '!')
    str = string(s)
    fmt.Println(str)

    str2 := "你好,中国"
    s2 := []rune(str2) // 包含中文字符使用 rune,代表一个UTF-8字符
    s2[3] = '伟'
    s2[4] = '大'
    s2 = append(s2, []rune{'的', '中', '国'}...)
    fmt.Println(string(s2))
}

3. Copy

  • 深拷贝:拷贝的是数据本身;值类型数据,默认是深拷贝,Array、Int、Sting、Struct、Bool、Float。
  • 浅拷贝:拷贝的是数据地址;引用类型数据,默认浅拷贝,Map、Slice。
package main

import "fmt"

func main() {
   arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   arr2 := arr
   arr[1] = 10
   fmt.Println(arr, arr2)
   fmt.Printf("prs: %p %p \n", &arr, &arr2) // Array 属于深copy
   fmt.Println()

   s1 := arr[:6]
   s2 := arr[4:]
   s1[5] = 50 // Slice 属于浅copy
   fmt.Println(s1, s2)
   fmt.Printf("prs:%p %p \n", s1, s2)

   // copy函数只能用于切片,属于浅拷贝;允许元素区间重叠
   copy(s1, s2)
   s1[1] = 100
   fmt.Println(s1, s2, arr)
}

4. Array、Slice 内存布局


array-slice

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Postgres 正在吞噬数据库世界

Postgres 正在吞噬数据库世界 作者&#xff1a;Ruohang Feng&#xff08;Vonng&#xff09;|微信| Medium | 2024-03-04 标签&#xff1a; PostgreSQL生态系统 PostgreSQL 不仅仅是一个简单的关系型数据库&#xff0c;它还是一个数据管理框架&#xff0c;具有席卷整个数据库领…

基于WPF技术的换热站智能监控系统04--实现左侧历史曲线

1、区域划分 左侧分5行&#xff0c;第一行信息标题&#xff0c;第二行历史曲线 2、安装livecharts图表控件 3、引入图表控件命名空间 4、使用控件 5、运行效果 走过路过不要错过&#xff0c;点赞关注收藏又圈粉&#xff0c;共同致富&#xff0c;为财务自由作出贡献

IP地址乱成一团?用Shell一键搞定!

在日常的运维工作中&#xff0c;我们经常需要对各种数据进行处理和分析&#xff0c;其中包括对IP地址的管理和排序。排序后的IP地址列表可以帮助我们更好地进行日志分析、网络流量监控和故障排除。 本文将模拟一个运维场景&#xff0c;展示如何对IP地址进行排序&#xff0c;并探…

Mongodb使用$pop删除数组中的元素

学习mongodb&#xff0c;体会mongodb的每一个使用细节&#xff0c;欢迎阅读威赞的文章。这是威赞发布的第67篇mongodb技术文章&#xff0c;欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题&#xff0c;欢迎在文章下面点个赞&#xff0c;或者关…

编译和连接

目录1. 翻译环境和运行环境2. 翻译环境:预编译编译汇编链接1. 翻译环境和运行环境 在ANSI C 的任何一种实现中&#xff0c;存在两个不同环境。 (1) 翻译环境&#xff0c;在这种环境中源代码被转换为可执行的机器指令(二进制指令)。 (2) 执行环境&#xff0c;它用于实际执行的代…

PostgreSQL 多表连接不同维度聚合统计查询

摘要:在本文中,你将学习到如何使用 PostgreSQL 完全外连接,从两个或多个表中聚合维度统计数据。 文章目录 一、常用的连接类型图示二、数据库表设计示例三、连接查询示例1. inner join 内连接(不能满足维度统计需求)2. full join 完全外连接(满足维度统计需求)一、常用的…

Golang免杀-分离式加载器(传参)AES加密

目录 enc.go 生成: dec.go --执行dec.go...--上线 cs生成个c语言的shellcode. enc.go go run .\enc.go shellcode 生成: --key为公钥. --code为AES加密后的数据, ----此脚本每次运行key和code都会变化. package mainimport ("bytes""crypto/aes"&…

java1.8运行arthas-boot.jar运行报错解决

报错内容 输入java -jar arthas-boot.jar&#xff0c;后报错。 [INFO] JAVA_HOME: D:\developing\jdk\jre1.8 [INFO] arthas-boot version: 3.7.2 [INFO] Can not find java process. Try to run jps command lists the instrumented Java HotSpot VMs on the target system.…

Spring Boot集成antlr实现词法和语法分析

1.什么是antlr&#xff1f; Antlr4 是一款强大的语法生成器工具&#xff0c;可用于读取、处理、执行和翻译结构化的文本或二进制文件。基本上是当前 Java 语言中使用最为广泛的语法生成器工具。Twitter搜索使用ANTLR进行语法分析&#xff0c;每天处理超过20亿次查询&#xff1…

20240612在飞凌的OK3588-C开发板的linux系统下测试以太网

20240612在飞凌的OK3588-C开发板的linux系统下测试以太网 2024/6/12 17:56 欢迎您入坑飞凌的OK3588-C开发板&#xff0c;使用飞凌的预编译的固件&#xff1a;OK3588-linuxfs-img.tar.bz2 Z:\rockdev\update.img tar jxvf OK3588-linuxfs-img.tar.bz2 首先&#xff0c;刷Android…

自己用pip下载好模块啦,但是在pycharm里面不显示?

问题&#xff1a; 今天在cmd里面用pip命令安装第三方模块&#xff0c;最后用pip list 命令发现已经成功安装&#xff0c;但是在pycharm里面用该模块的时候&#xff0c;还是爆红&#xff0c;显示没有该库 。 解决方法&#xff1a; 第一种&#xff08;项目刚创建&#xff09;&am…

虚拟声卡实现音频回环

虚拟声卡实现音频回环 一、电脑扬声器播放声音路由到麦克风1. Voicemeeters安装设置2. 音频设备选择 二、回声模拟 一、电脑扬声器播放声音路由到麦克风 1. Voicemeeters安装设置 2. 音频设备选择 以腾讯会议为例 二、回声模拟 选中物理输入设备“Stereo Input 1”和物理输出设…

GUI listbox

GUI listbox &#xff08;自用笔记&#xff09; 功能details拆分 同时打开多个文件&#xff0c;可以是不同类型的&#xff0c;在listbox中显示出路径和文件名&#xff1b; 计算每个数据文件&#xff08;.txt或.dat&#xff09;掉帧出现的行数&#xff0c;存储到元胞数组&…

Vue10-事件修饰符

一、示例&#xff1a;<a>标签不执行默认的跳转行为 1-1、方式一 <a href"http://www.baidu.com" onclick"event.preventDefault();">点击我</a> 1-2、方式二 1-3、方式三&#xff1a;事件修饰符 二、Vue的六种事件修饰符 2-1、prevent&…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 6月13日,星期四

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年6月13日 星期四 农历五月初八 1、 财政部&#xff1a;将在19日第一次续发行2024年20年期超长期特别国债。 2、 成本低&#xff0c;商载高&#xff0c;我国自主研制HH-100商用无人运输机首飞成功。 3、 四川甘孜州石渠县1…

Mongodb在UPDATE操作中使用$pull操作

学习mongodb&#xff0c;体会mongodb的每一个使用细节&#xff0c;欢迎阅读威赞的文章。这是威赞发布的第68篇mongodb技术文章&#xff0c;欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题&#xff0c;欢迎在文章下面点个赞&#xff0c;或者关…

网站线上模板建设的优缺点

优点&#xff1a; 1.搭建的时间短&#xff0c;在线建站&#xff0c;只需要登录注册然后选择网站模板创建网站即可管理自己的网站后台&#xff0c;就几步操作就可以实现。 2.网站出错率少&#xff0c;因为有很多用户在使用&#xff0c;前期所报出来的问题就已经一一…

PHP开发的爱情盲盒交友系统网站源码

源码介绍 PHP开发的爱情盲盒交友系统网站源码 独立后台 源码截图 源码下载 PHP开发的爱情盲盒交友系统网站源码

【Tkinter界面】Canvas 图形绘制(03/5)

文章目录 一、说明二、画布和画布对象2.1 画布坐标系2.2 鼠标点中画布位置2.3 画布对象显示的顺序2.4 指定画布对象 三、你应该知道的画布对象操作3.1 什么是Tag3.2 操作Tag的函数 https://www.cnblogs.com/rainbow-tan/p/14852553.html 一、说明 Canvas&#xff08;画布&…

【高频】从准备更新一条数据到事务的提交的流程描述

一、相关问题 SQL语句是如何被MySQL解析和分析的?MySQL是如何为SQL语句生成最优的执行计划的?执行计划的各个步骤是如何一步步执行的?MySQL是如何访问存储引擎得到数据的&#xff1f;MySQL是如何将查询结果返回给客户端的?MySQL是如何处理事务的&#xff1f; 参考回答&am…