#### golang的append到底干了啥 ####

news2024/11/22 15:20:26

 代码1.0

package main

import "fmt"

func main() {
	a := make([]int64, 0, 0) // 改为 a := make([]int64, 0, 2) 时执行输出也都一样的
	println(fmt.Sprintf("a: %v", a))
	// 输出:a: []

	solve(a)
	println(fmt.Sprintf("a: %v", a))
	// 输出:a: []
}

func solve(currA []int64) {
	currA = append(currA, 1)
	println(fmt.Sprintf("currA: %v", currA))
	// 输出:currA: [1]
}

在调solve函数时,发生了引用的复制:currA = a,引用指的是这个东西:

// runtime/slice.go
 
type slice struct {
    array unsafe.Pointer // 数组指针
    len int // 长度 注意:append时len是占位的
    cap int // 容量
}

也就是产生了slice struct的两个实例——currA和a,其中currA中各项(包含:array指针、len、cap三个字段)的值是从a的各项值复制来的,其中array指针相同则意味着currA和a指向的底层数组是一个,但是后续currA和a会各自维护自己的len和cap。

这也就是为什么【在子函数solve里append后不会影响父函数main里的切片,但是在子函数solve里用下标修改元素值后是会影响父函数main里的切片(下标修改的过程见下面代码)】的原因。

package main

import "fmt"

func main() {
	a := make([]int64, 0, 0)
	a = append(a, 1)
	println(fmt.Sprintf("a: %v", a))
	// 输出:a: []

	solve(a)
	println(fmt.Sprintf("a: %v", a))
	// 输出:a: [2]
}

func solve(currA []int64) {
	currA[0] = 2
	println(fmt.Sprintf("currA: %v", currA))
	// 输出:currA: [2]
}

 代码2.0

package main

import "fmt"

func main() {
	a := make([]int64, 0, 2) // 后续不需要扩容
	println(fmt.Sprintf("a: %v", a))
	// 输出:a: []

	b := append(a, 1)
	println(fmt.Sprintf("a: %v, b: %v", a, b))
	// 输出:a: [], b: [1]

	c := append(a, 2)
	println(fmt.Sprintf("a: %v, b: %v, c: %v", a, b, c))
	// 输出:a: [], b: [2], c: [2]
}

总结分析

append时没有发生底层数组数据的直接复制;相反,append 函数返回了一个新的切片值,该值可能与原始切片共享相同的底层数组(在不需要扩容的情况下),但具有不同的长度和(可能)容量。

另外,append时会会根据原接片的长度去追加。例如原切片长度为1(下标=0),则append时会在下标为0+1的地方追加。

详细分析

当你执行 b := append(a, 1) 时,这里发生了几件事情:

  1. 如果 append 操作不需要扩展底层数组(即新元素的添加不会导致切片的容量不足),那么 append 会直接在底层数组的末尾添加新元素,并返回一个新的切片值。这个新的切片值会包含原始切片的所有元素(在这个例子中是空的,因为没有元素),加上新追加的元素。重要的是,这个新的切片值会共享原始切片的底层数组(如果可能的话),但它会有自己的长度(现在至少为 1,因为我们添加了一个元素)。

  2. 在这个特定的例子中,由于 a 是空的,且其容量足以容纳至少一个额外的元素(因为我们设置了容量为 2),append(a, 1) 实际上是在 a 的底层数组的末尾(尽管这个数组目前还是空的)添加了一个元素,并返回了一个新的切片值 b。这个新的切片值 b 指向与 a 相同的底层数组(在这个例子中这个“相同”的底层数组实际上还没有被使用来存储任何元素,但它已经为存储元素做好了准备),但它的长度是 1,因为它现在包含一个元素。

  3. 然而,重要的是要理解 a 和 b 是两个不同的切片值。尽管它们可能(在这个例子中确实)共享相同的底层数组,但每个切片值都有自己独立的长度和容量。当你将 b 赋值给一个新变量时,你并没有复制底层数组,而是创建了一个新的切片头(包含指向底层数组的指针、长度和容量),并将其赋值给该变量。

  4. 因此,当你说“包含了 a 的所有元素”时,你实际上是在说新切片 b 包含了原始切片 a 在追加操作之前所拥有的所有元素(在这个例子中是空的),并且额外添加了一个新的元素。这并不意味着底层数组的数据被复制了;相反,它意味着新的切片值 b 指向了与 a 相同的底层数组(或一个新的、更大的数组,如果追加操作导致了切片的重新分配),但具有不同的长度。

代码2.1

package main

import "fmt"

func main() {
	a := make([]int64, 0, 0) // 后续不需要扩容
	println(fmt.Sprintf("a: %v", a))
	// 输出:a: []

	b := append(a, 1)
	println(fmt.Sprintf("a: %v, b: %v", a, b))
	// 输出:a: [], b: [1]

	c := append(a, 2)
	println(fmt.Sprintf("a: %v, b: %v, c: %v", a, b, c))
	// 输出:a: [], b: [1], c: [2]
}

如果append时需要扩容,则将原数组的值复制到扩容后的数组。执行到【b := append(a, 1)】和【c := append(a, 2)】时会发生扩容,但扩容不会对a产生任何影响,因为没有【a := append(a, 1或2)】的操作。

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

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

相关文章

2024 年人工智能和数据科学的五个主要趋势

引言 2023年,人工智能和数据科学登上了新闻头条。生成性人工智能的兴起无疑是这一显著提升曝光度的驱动力。那么,在2024年,该领域将如何继续占据头条,并且这些趋势又将如何影响企业的发展呢? 在过去几个月,…

If you already have a 64-bit JDK installed ,defined a JAVA_HOME...的错误

今天感觉idea有点卡,修改了一下内存,结果就报这个错误了,网上的解决方案好多,都不行 以下是解决方案 打开 C:\Program Files\JetBrains\IntelliJ IDEA 2024.1.4\bin\jetbrains_client64.exe 把jihuo这个目录下所有的文件都删掉&…

[leetcode hot 150]第三题,无重复字符的最长子串

题目: 给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串的长度。 可以使用"滑动窗口"的方法来解决这个问题。基本思路如下: 使用两个指针(start和end)来定义一个窗口移动end指针来扩大窗口,直到遇到重复字符如果遇到重复字符,移动s…

FreeRTOS和UCOS操作系统使用笔记

FreeRTOS使用示例 UCOS使用示例 信号量使用 信号量访问共享资源区/ OS_SEMMY_SEM; //定义一个信号量,用于访问共享资源OSSemCreate ((OS_SEM* )&MY_SEM, //创建信号量,指向信号量(CPU_CHAR* )"MY_SEM", //信号量名字(OS_SEM_CTR )1, …

【C++】多态(详解)

前言:今天学习的内容可能是近段时间最难的一个部分的内容了,C的多态,这部分内容博主认为难度比较大,各位一起慢慢啃下来。 💖 博主CSDN主页:卫卫卫的个人主页 💞 👉 专栏分类:高质量&#xff23…

绝地求生PUBG服务器延迟太高 购买领取响应时间长怎么解决

绝地求生PUBG是一款特别热门的射击类吃鸡游戏,游戏还有多张地图可供玩家选择,玩家们需要乘坐飞机空投跳伞至不同的各个角落,赤手空拳寻找武器,车辆以及物资,并在多种多样的地形中展开战斗。想要取得胜利,我…

微信小程序转发朋友圈详细教程

微信小程序转发朋友圈功能,官方说的很官方,容易踩坑 官方链接戳这里 想分享朋友圈必须要分享好友 onShareTimeline() { } 想要生效必须先定义 onShareAppMessage() { } /*** 用户点击右上角分享*/onShareAppMessage() { },onShareTimeline() { } 简单…

应对SQL注入攻击:保障网站安全的策略

在互联网的广阔天地中,网站安全始终是站长用户和企业开发者不可忽视的重要议题。其中,SQL注入攻击作为一种常见的网络攻击手段,严重威胁着网站的数据安全和业务稳定。什么是SQL注入攻击,我们该如何应对这种攻击呢?今天…

广州外贸建站模板

Yamal外贸独立站wordpress主题 绿色的亚马尔Yamal外贸独立站wordpress模板,适用于外贸公司建独立站的wordpress主题。 https://www.jianzhanpress.com/?p7066 赛斯科Sesko-W外贸建站WP主题 适合机械设备生产厂家出海做外贸官网的wordpress主题,红橙色…

互联网应用主流框架整合之SpringCloud微服务治理

微服务架构理念 关于微服务的概念、理念及设计相关内容,并没有特别严格的边界和定义,某种意义上说,适合的就是最好的,在之前的文章中有过详细的阐述,微服务[v1.0.0][Spring生态概述]、微服务[设计与运行]、微服务[v1.0.0][服务调用]、微服务[开发生命周期]、微服务[面临的…

Unity之创建与导出PDF

内容将会持续更新,有错误的地方欢迎指正,谢谢! Unity之创建与导出PDF TechX 坚持将创新的科技带给世界! 拥有更好的学习体验 —— 不断努力,不断进步,不断探索 TechX —— 心探索、心进取! 助力快速…

最靓丽的C++开源通知弹框SnoreToasts自动监听软件及网页通知

SnoreToasts,作为一款轻量级的C开源项目,为开发者提供了一个便捷的方式来在Windows操作系统上展示通知弹框(Toast Notifications)。 特点与优势 轻量级:SnoreToasts采用了简洁的代码设计,避免了不必要的依…

SQLServer:从数据类型 varchar 转换为 numeric 时出错。

1.工作要求 计算某两个经纬度距离 2.遇到问题 从数据类型 varchar 转换为 numeric 时出错。 3.解决问题 项目版本较老,使用SQLServer 2012 计算距离需执行视图,如下: SET QUOTED_IDENTIFIER ON SET ANSI_NULLS ON GO ALTER view vi_ord…

「前端」快速排序算法演示

快速排序算法演示。 布局描述 一个简单的HTML页面,用户可以在其中输入一系列用逗号分隔的数字。 一个CSS样式表,提供了一个美观大方的布局和样式。 一个JavaScript脚本,实现了快速排序算法,并在用户点击按钮时对输入的数字进行排序,并显示结果。 效果演示 核心代码 <…

PyPDF2拆分PDF文件的高级应用:指定拆分方式

本文目录 前言一、拆分方式选择1、代码讲解2、实现效果图3、完整代码前言 前两篇文章,分别讲解了将使用PyPDF2将PDF文档分割成为单个页面、在分割PDF文档时指定只分割出指定页面,如果你还没有看过,然后有需要的话,可以去看一下,我把文章链接贴到这里: PyPDF2拆分PDF文件…

苹果可能与谷歌大模型合作,马斯克xAI下个月推出Grok-2,比尔·盖茨:Scaling Law快要走到尽头

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 1、苹果被曝 Gemini 模型今秋或融入苹果智能生态系统 苹果知名爆料人马克古尔曼&#xff08;Mark Gurman&#xff09;最新透露&#xff0c;苹果公司将于今年秋季宣布与Alphabet旗下的谷歌的大模型Gem…

计算机网络网络层复习题2

一. 单选题&#xff08;共22题&#xff0c;100分&#xff09; 1. (单选题)如果 IPv4 数据报太大&#xff0c;会在传输中被分片&#xff0c;对分片后的数据报进行重组的是&#xff08; &#xff09;。 A. 中间路由器B. 核心路由器C. 下一跳路由器D. 目的主机 我的答案: D:目的…

如何学好自动化测试

1. 什么是自动化测试 自动化测试是使用脚本和工具来执行测试任务&#xff0c;以替代手工测试过程。它可以提高效率、减少人工错误&#xff0c;并增加测试覆盖率。在软件开发过程中&#xff0c;自动化测试已经成为了不可或缺的一部分。 自动化测试主要有以下好处&#xff1a; …

《人人都是产品经理》:大产品

《人人都是产品经理》&#xff1a;大产品 产品之大时间之大空间之大&#xff1a;商业、产品、技术设计之大以写书为例 团队之大 回答一个问题 产品经理应该是管理者嘛&#xff1f;优点在于&#xff1a;缺点在于&#xff1a;总结&#xff1a; 如何让团队更加开心总结 产品之大 …

Android线性布局的概念与属性

线性布局(LinearLayout)是Android中最简单的布局方式&#xff0c;线性布局方式会使得所有在其内部的控件或子布局按一条水平或垂直的线排列。如图所示&#xff0c;图a是纵向线性布局示意图&#xff0c;图b是横向线性布局示意图。 a&#xff09;纵向线性布局示意图 …