Go 数组和切片反思

news/2024/11/27 0:21:29/

切片的底层数据结构是数组,所以,切片是基于数组的上层封装,使用数组的场景,也完全可以使用切片。

类型比较

我看到 go 1.17 有对切片和数组转换的优化,禁不住纳闷,有什么场景是必须数组来完成的呢?我特意去查了一下数组和切片的核心差异,刨去有的没的,核心就 2 点:

  • 数组是可比较的,切片是不可比较的,验证的方式也非常简单。声明一个 map 类型,如果 key 的类型是切片,会报如下的编译错误。如果是数组类型,可以正常编译。
  • 数组赋值是值拷贝,切片赋值是引用拷贝,切片类型在值拷贝前后,底层共用的还是同一个数组,而数组的值拷贝就是值拷贝。

在这里插入图片描述
在查看的过程中,我也有发现数组指针的用法,除了可以通过数组本身来操作数组,还可以通过数组指针来操作。而这种使用手法,是切片不具备的。

下面的代码,通过数组指针 (&b) 可以像直接使用 b 一样访问数组的元素,而通过使用指针赋值的方式,也可以让两个变量共用同一个数组。

func main() {var b = [3]int{11, 12, 13} //fmt.Println((&b)[0])fmt.Println(b[0])
}

下面数组的赋值过程, a 和 b 是值拷贝,完全独立的两份数据,b 数组修改第一个元素,并不会影响到数组 a。而 c 和 a 是数组指针类型的值拷贝,共享同一份数组数据,c 数组修改第一个元素,也就修改了 a 指向的数组。

func main() {var a = [3]int{11, 12, 13} //b := ab[0] = 10fmt.Println(a[0])fmt.Println(b[0])c := &ac[0] = 10fmt.Println(a[0])fmt.Println(c[0])
}

类型转换

数组和切片的转换,说实话,在业务开发的过程中,我基本上没有用到这种转换场景。因为切片的底层是数组,数组可以直接转换为切片类型,转换后,切片和数组共享同一份空间。

func main() {var a = [3]int{11, 12, 13} b := a[:len(a)]fmt.Printf("%T,%T", b, a)
}

仔细想想,切片在 go 中的结构为 SliceHeader ,除了数组 Data 部分,还包含了 Len 和 Cap 两个属性。所以,go 数组转切片的过程,也是封装这个 SliceHeader 的过程。

type SliceHeader struct {Data uintptrLen  intCap  int
}

但在 1.17 之前,切片是不能直接转换为数组的,要想转换为数组,就需要通过 unsafe 包来实现。不过,切片要转换为数组,相比,数组转换为切片,要简单很多。

原理上就是获取切片底层数组的第一个元素的指针,转换为数组的指针。

func main() {var a = []int{11, 12, 13} //b := (*[3]int)(unsafe.Pointer(&a[0]))fmt.Println(b)
}

现在 go 1.17 默认也支持了这个转换,转换的方式也很好理解,转换为数组类型的指针。因为是指针,所以,转换后的数组 b 和 a 仍然还是共用同一份数据。

为什么默认转换使用的类型是数组指针 (*[3]int),而非 ([3]int) 呢?我理解,如果是值类型的话,还需要底层数据的拷贝,语义上也没有指针类型好理解。

func main() {var a = []int{11, 12, 13} //b := (*[3]int)(a)fmt.Println(b)
}

应用

结合以往的工作,比较适合使用数组替代切片的场景大概就是:控制并发的顺序。

我们有一组数组 reqs,要请求某一个接口。然后,需要按照请求的顺序,返回请求的结果。我们使用 go 协程并发请求接口,然后基于请求的索引,将接口返回的结果存储到对应索引数组的位置。

因为数组的个数是固定的,所以,这种场景还是比较少的


func main() {var reqs [3]interface{} = [...]interface{}{1, 2, 3}var res [3]interface{}for i:= range reqs {go func(index int) {res[index] = reqs[index]}(i)}time.Sleep(time.Second)fmt.Println(res)
}

http://www.ppmy.cn/news/26025.html

相关文章

超低成本DDoS攻击来袭,看WAF如何绝地防护

一、DDoS攻击,不止于网络传输层 网络世界里为人们所熟知的DDoS攻击,多数是通过对带宽或网络计算资源的持续、大量消耗,最终导致目标网络与业务的瘫痪;这类DDOS攻击, 工作在OSI模型的网络层与传输层,利用协…

Python-TCP网络编程基础以及客户端程序开发

文章目录一. 网络编程基础- 什么是IP地址?- 什么是端口和端口号?- TCP介绍- socket介绍二. TCP客户端程序开发三. 扩展一. 网络编程基础 - 什么是IP地址? IP地址就是标识网络中设备的一个地址 IP地址分为 IPv4 和 IPv6 IPv4使用十进制, IPv6使用十六进制 查看本机IP地址: l…

ChatGPT入门案例|商务智能对话客服(三)

本篇介绍智能客服的基本功能架构和基本概念,并利用对话流技术构建商务智能应用。 01、商务智能客服功能结构 互联网的发展已经深入到社会的各个方面,智能化发展已经成为社会发展的大趋势。在大数据和互联网时代,企业和组织愈加重视客户沟通…

Websocket详细介绍

需求背景 在某个资产平台,在不了解需求的情况下,我突然接到了一个任务,让我做某个页面窗口的即时通讯,想到了用websocket技术,我从来没用过,被迫接受了这个任务,我带着浓烈的兴趣,就…

rt-thread pwm 多通道

一通道pwm参考 https://blog.csdn.net/yangshengwei230612/article/details/128738351?spm1001.2014.3001.5501 以下主要是多通道与一通道的区别 芯片 stm32f407rgt6 1、配置PWM设备驱动相关宏定义 添加PWM宏定义 #define BSP_USING_PWM8 #define BSP_USING_PWM8_CH1 #d…

LeetCode 338. 比特位计数

给你一个整数 n &#xff0c;对于 0 < i < n 中的每个 i &#xff0c;计算其二进制表示中 1 的个数 &#xff0c;返回一个长度为 n 1 的数组 ans 作为答案。 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;[0,1,1] 解释&#xff1a; 0 --> 0 1 --> …

实战打靶集锦-006-Stapler

**写在前面&#xff1a;**记录博主的一次打靶经历。 目录1. 主机发现2. 端口发现3. 服务枚举4. 服务探查4.1 FTP探查4.1.1 匿名登录4.1.2 Elly用户4.1.3 John用户4.1.4 EXP搜索4.2 dnsmasq探查4.2.1 基础信息获取4.2.2 EXP搜索4.3 WEB应用探查4.3.1 浏览器访问4.3.2 目录扫描4.…

【安全开发】专栏文章汇总

安全开发–1–TCP和UDP网络编程 安全开发–2–嗅探邮箱协议口令 安全开发–3–Python实现ARP缓存投毒 安全开发–4–SSH通信工具开发 安全开发–5–编写简单的netcat工具 安全开发–6–一个简单的TCP代理工具开发 安全开发–7–SSH隧道工具开发 安全开发–8–Python实现流量数据…