Go 语言切片遍历地址会发生改变吗?

news2024/10/7 4:23:49

引言:今天面试的时候,面试官问了一道学 Go 语言的同学都会的简单代码,是关于 Go 语言 for 循环问题的,他询问了一个点,循环中共享变量的地址会发生改变吗?

相信听到这个问题的你,第一反应肯定是不会变啊,怎么会发生改变呢?听到这里面试官选择摇了摇头,那到底是怎么回事呢?接下来我们来研究一下。

题目

Go 语言切片遍历地址会发生改变吗?

推荐解析

正文

首先是下面这段代码,这段代码相信很多在学习 Go 语言的同学都有接触过,那么我们现在来问一个问题,就是在

for 循环中,v 的地址会发生改变吗?

package main

import (
	"fmt"
	"runtime"
)

func main() {
	slice := make([]int, 3, 5)
	fmt.Println(runtime.Version())
	fmt.Println("---------------------")
	for i, v := range slice {
		fmt.Printf("%p\n", &v)
		fmt.Println(i)
	}
}

在此之前,问过一些学习 Go 的同学,如下图所示:

这位学习 Go 的同学第一直觉就是不会变的,那我们现在来运行一下看看结果:

我们会发现,这个关于 v 地址打印的内容,其是会发生改变的,相信很多人第一反应都是不敢相信的的,我们再来测试一组数据:

这次我们会发现,v 的地址又没有改变了,这是怎么回事呢?我们回到原文,将两次运行结果复制下来对比一下:

第一次运行结果:
go1.22.1
---------------------
0xc00000a100
0
0xc00000a110
1
0xc00000a120
2

==========================

第二次运行结果:
go1.21.2
---------------------
0xc00000a100
0
0xc00000a100
1
0xc00000a100
2

我们会发现一个很关键的点,就是第一次运行结果和第二次运行结果关于 Go 语言的版本号有所不同,到这里我们就可以得出一个初步结论,go 语言 1.22 之后与 1.22 之前的版本是存在差异的,即在 1.22 之前,for 循环遍历循环变量是不会发生改变的,1.22 以及之后的版本都是会进行改变的,那改变的原因是什么呢,我们继续来探究一下。

探究

谷歌每次发布 Go 语言的新版本都会发布博客,首先我们来看一下谷歌发布的博客

谷歌 Go 1.22 博客地址:https://golang.google.cn/blog/go1.22

我们会发现其已经发生了改变了,然后我们将官方 Demo 截取下来:

func main() {
    done := make(chan bool)

    values := []string{"a", "b", "c"}
    for _, v := range values {
        go func() {
            fmt.Println(v)
            done <- true
        }()
    }

    // wait for all goroutines to complete before exiting
    for _ = range values {
        <-done
    }
}

期望输出

这段代码在 go 1.21 版本运行的时候,可能会报一个错误,即 loop variable v captured by func literal

但是其还是会输出的,运行结果如下:

c
c
c

但如果使用 1.22 版本输出的话,顺序随机,但是每个值都会输出

a
c
b

共享变量

到这里我们大概就知道了,在 Go 1.22 之前,循环闭包共享的是同一个变量,而 1.22 之后,其不再共享同一个变量,这里我们再打印地址测试一下,代码如下:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    done := make(chan bool)

    values := []string{"a", "b", "c"}
    fmt.Println("Go Version:", runtime.Version())
    for _, v := range values {
        go func() {
            fmt.Println(&v)
            done <- true
        }()
    }

    // wait for all goroutines to complete before exiting
    for _ = range values {
        <-done
    }
}

运行结果如下:

Go Version: go1.22.1
0xc0000260a0
0xc000026080
0xc000026090
=======================
Go Version: go1.21.2
0xc000026080
0xc000026080
0xc000026080

总结

到这里,我们就探究完毕了,Go 1.22 之后,for 循环的循环变量不再共享了,因此循环变量的地址也会发生改变。

最后

最后,我们回到一开始的问题,for 循环遍历切片,循环变量的地址会发生改变吗?这个时候我们需要回答到关于版本变更的问题,即在 1.22 之前,循环变量的地址是共享的,不会发生改变,在 1.22 之后,循环变量的地址不再是共享的,是会发生改变的。

其他补充

鱼聪明 AI 的回答:

鱼聪明 AI 地址:https://www.yucongming.com/

在 Go 语言中,切片(slice)是一个引用类型(reference type)。当你遍历切片时,遍历过程中获取的元素地址不会改变,因为切片本身并不会重新分配内存或改变其底层数组的地址。

具体来说:

  • 切片包含了一个指向数组的指针(或称为引用),以及切片的长度和容量。
  • 当你通过索引访问切片的元素时,实际上是访问底层数组中对应索引位置的元素。
  • 切片的底层数组在不需要扩展容量时,其地址是固定的,因此切片的元素地址在遍历过程中是稳定的。

例如,考虑以下示例代码:

package main

import "fmt"

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

    for i := range nums {
        fmt.Printf("Element at index %d has address: %p\n", i, &nums[i])
    }
}

在这个例子中,切片 nums 的每个元素地址在整个循环过程中保持不变,因为 nums 持有的是底层数组的引用,而不是元素本身的复制。

总结来说,Go 语言中遍历切片获取元素的地址是安全的,因为切片的底层数组地址不会随着遍历过程发生改变。

欢迎交流

本文主要介绍 Go 语言切片遍历地址是否会发生改变的问题,在文末还有剩余三个思考题,欢迎小伙伴在评论区进行留言!近期面试鸭小程序已全面上线,想要刷题的小伙伴可以积极参与!

1)for range 循环遍历 slice 有什么问题?

2)如果对切片进行 for range 遍历的时候,遍历过程中追加的元素会不会遍历到?

3)对于需要跨版本兼容的 Go 代码,如何处理循环变量在不同版本中的行为差异?

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

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

相关文章

你还能顶几天?

A总&#xff1a;你还能顶几天&#xff1f; 汪汪队&#xff1a;顶到奉命撤退的那一天 A总&#xff1a;你在这守散钱点几十年了&#xff0c;从来没跟我提过任何的要求&#xff0c;难道你不困难吗&#xff1f; 汪汪队&#xff1a;有困难&#xff0c;但是我提了有什么用呢&#…

Java异常处理详解【入门篇】

Java异常处理详解【入门篇】 Java异常处理详解1. 异常的概念2. 异常的分类2.1 检查异常&#xff08;Checked Exception&#xff09;2.2 非检查异常&#xff08;Unchecked Exception&#xff09;2.3 错误&#xff08;Error&#xff09; 3. 异常处理机制3.1 try-catch3.2 finally…

kubuadm 方式部署 k8s 集群

准备三台机器 主机名IP地址CPU/内存角色K8S版本Docker版本k8s231192.168.99.2312C4Gmaster1.23.1720.10.24k8s232192.168.99.2322C4Gwoker1.23.1720.10.24k8s233192.168.99.2332C4Gwoker1.23.1720.10.24 需要在K8S集群各节点上面安装docker&#xff0c;如未安装则参考 …

如何3分钟上手传得神乎其神的AI绘画!一篇文章带你搞懂!

前言 今年 AI 绘画绝对是大火的概念之一&#xff0c;这次加入了生财 AI 绘画小航海的船&#xff0c;今天是体验的第1天&#xff0c;那么 AI 绘画是什么呢&#xff1f; 简单来说就是利用 AI 实现绘画&#xff0c;在特定的软件或者程序中&#xff0c;输入一定的关键词或者指令&…

springboot系列八: springboot静态资源访问,Rest风格请求处理, 接收参数相关注解

文章目录 WEB开发-静态资源访问官方文档基本介绍快速入门注意事项和细节 Rest风格请求处理基本介绍应用实例注意事项和细节思考题 接收参数相关注解基本介绍应用实例PathVariableRequestHeaderRequestParamCookieValueRequestBodyRequestAttributeSessionAttribute ⬅️ 上一篇…

数据结构复习指南

数据结构复习指南 本文中列举了数据结构期末考试可能存在的考点 绪论 数据的基本单位 数据元素是数据的基本单位 数据项 数据项是组成数据的、有独立含义的、不可分割的最小单位。 数据对象 数据对象是性质相同的数据元素的集合&#xff0c;是数据的一个子集。 数据结…

【DevExpress】WPF DevExpressMVVM 24.1版本开发指南

DevExpressMVVM WPF 环境安装 前言重要Bug&#xff08;必看&#xff09;环境安装控件目录Theme 主题LoginWindow 登陆窗口INavigationService 导航服务DockLayout Dock类型的画面布局TreeView 树状列表注意引用类型的时候ImageSource是PresentationCore程序集的博主找了好久&am…

【python】OpenCV—QR Code

文章目录 1 QR Code2 准备工作3 生成 QR 码4 读取 QR 码5 与 Zbar 比较 1 QR Code QR Code&#xff08;Quick Response Code&#xff09;是一种二维条码&#xff0c;由日本Denso-Wave公司于1994年发明。QR Code的主要特点是存储信息量大、编码范围广、容错能力强、识读速度快&…

如何让ubuntu开机自动启动DHCP呢?

在Ubuntu系统中&#xff0c;确保DHCP服务&#xff08;通常是ISC DHCP服务&#xff09;在系统启动时自动运行&#xff0c;需要进行以下几步操作&#xff1a; ### 1. 确保DHCP服务已安装 如果你还没有安装DHCP服务&#xff0c;可以使用下面的命令进行安装&#xff1a; bash sudo…

TCP:TCP连接的建立与终止

TCP连接的建立与终止 建立连接第一次握手第二次握手第三次握手 终止连接第一次挥手第二次挥手第三次挥手TIME_WAIT 状态 第四次挥手 连接建立超时 T C P是一个面向连接的协议。无论哪一方向另一方发送数据之前&#xff0c;都必须先在双方之间建立一条连接。本文将详细讨论一个T…

文心一言用户达3亿!文心大模型4.0 Turbo发布,支持API,真GPT-4 Turbo国产来了!

文心一言用户规模达到3亿了&#xff01; 这是笔者在今天的百度Wave Summit 2024大会上的看到的数字。需要强调的是&#xff0c;文心一言的用户规模是在去年12月破亿的。这意味着&#xff0c;仅仅隔了6个月&#xff0c;文心一言用户数量在亿这个级别的数字上竟然直接翻了三倍。…

2024最出色的代理软件评估及推荐

随着网络技术的飞速发展&#xff0c;代理软件已成为许多网络活动不可或缺的工具&#xff0c;特别是在数据抓取、网络安全防护等方面。在众多代理软件中&#xff0c;哪些能真正满足用户需求&#xff0c;提供卓越的性能和服务呢&#xff1f;我们的测评团队经过深入研究和测试&…

Reid系列论文学习——无人机场景下基于 Transformer 的轻量化行人重识别

今天介绍的一篇论文是针对无人机场景下的行人重识别&#xff0c;论文题目为&#xff1a;"无人机场景下基于 Transformer 的轻量化行人重识别"。该论文针对无人机场景下行人呈现多角度多尺度的特点、以及传统CNN网络在行人重识别任务中受限于感受野和下采样导致的无法…

Go-知识测试-单元测试

Go-知识测试-单元测试 1. 定义2. 使用3. testing.common 测试基础数据4. testing.TB 接口5. 单元测试的原理5.1 context 单元测试的调度5.1.1 等待并发执行 testContext.waitParallel5.1.2 并发测试结束 testContext.release 5.2 测试执行 tRunner5.3 启动测试 Run5.4 启动并发…

《晨集》开源软件平台的创新与发展

一、引言 在数字化浪潮的推动下&#xff0c;开源软件平台已成为推动软件创新、促进知识共享的重要力量。《晨集》作为新兴的开源软件平台&#xff0c;其上线标志着开源生态圈的又一重要里程碑。本文旨在探讨《晨集》开源软件平台的创新特点、对开发者社区的影响以及未来发展趋…

Flink作业执行之 4.JobGraph

Flink作业执行之 4.JobGraph 1. 入口 前文了解了由Transformation到StreamGraph的过程&#xff0c;StreamGraph即作业的逻辑拓扑结构。 生成逻辑结构后&#xff0c;接下来的操作往往是对逻辑结构的优化。在很多组件中都是这样的处理&#xff0c;如hive、spark等都会执行“逻辑…

【linux】从零到入门

linux概述 Linux是一个免费使用和自由传播的一套操作系统。用户可以无偿地得到它地源代码&#xff0c;和大量地应用程序&#xff0c;并且可以随意修改和增加它们。 Linux的内核起初由林纳斯编写。内核是啥&#xff1f; 驱动设备&#xff0c;文件系统&#xff0c;进程管理&…

『MySQL 实战 45 讲』22 - MySQL 有哪些“饮鸩止渴”提高性能的方法?

MySQL 有哪些“饮鸩止渴”提高性能的方法&#xff1f; 需求&#xff1a;业务高峰期&#xff0c;生产环境的 MySQL 压力太大&#xff0c;没法正常响应&#xff0c;需要短期内、临时性地提升一些性能 短连接风暴 短连接模式&#xff1a;执行很少的 SQL 语句就断开&#xff0c;…

【Sklearn-驯化】一文搞懂机器学习树模型建模可视化过程

【Sklearn-驯化】一文搞懂机器学习树模型建模可视化过程 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取相关内容文档关注&#xff…

LeetCode刷题之HOT100之数组中的第K个最大元素

2024 6/29 今天天气很好啊&#xff0c;想爬山&#xff0c;奈何下午还有最后的一个汇报。做个题先 1、题目描述 2、算法分析 看到这个题我想到的就是: public int findKthLargest(int[] nums, int k) {Arrays.sort(nums);return nums[nums.length - k ];}哈哈&#xff0c;我提…