首页 > 后端开发 > Golang > 正文

Go语言中数组与切片的区别及引用行为解析

霞舞
发布: 2025-07-20 14:36:14
原创
216人浏览过

Go语言中数组与切片的区别及引用行为解析

本文深入探讨Go语言中数组与切片的核心差异。数组是固定长度的值类型,赋值和函数传参时会进行完整复制;而切片是动态长度的引用类型,它指向底层数组,赋值和传参时仅复制其引用(切片头),因此多个切片可能共享同一底层数组。通过分析示例代码,我们将详细解释为何对一个切片的操作会影响另一个看似独立的切片,从而澄清Go中数据结构的行为机制。

go语言中,数组(array)和切片(slice)是两种常用的复合数据类型,它们在内存管理和行为上有着本质的区别。理解这两种类型的工作原理对于编写高效且正确的go程序至关重要,尤其是在涉及数据传递和修改时。

1. Go语言中的数组(Arrays)

数组是Go语言中一种固定长度的、同类型元素的集合。数组的长度在声明时确定,并且是其类型的一部分。这意味着[3]int和[4]int是两种不同的类型。

核心特性:

  • 固定长度: 数组一旦声明,其长度就不能改变。
  • 值类型: 数组是值类型。这意味着当一个数组被赋值给另一个数组,或者作为参数传递给函数时,会创建一个完整的副本。对副本的修改不会影响原始数组。

声明和初始化示例:

云雀语言模型
云雀语言模型

云雀是一款由字节跳动研发的语言模型,通过便捷的自然语言交互,能够高效的完成互动对话

云雀语言模型 54
查看详情 云雀语言模型
// 声明一个包含3个整数的数组,并初始化
arr1 := [3]int{1, 2, 3}

// 声明一个数组,让编译器根据初始化值推断其长度
arr2 := [...]int{1, 2, 3} // 等同于 [3]int{1, 2, 3}

// 声明一个包含3个整数的数组,元素将被初始化为零值(int的零值是0)
var arr3 [3]int
登录后复制

如果执行以下操作:

立即学习go语言免费学习笔记(深入)”;

a := [3]int{1, 2, 3}
b := a // b是a的一个完整副本
b[0] = 99
fmt.Println(a) // 输出: [1 2 3]
fmt.Println(b) // 输出: [99 2 3]
登录后复制

可以看到,对b的修改不会影响a。

2. Go语言中的切片(Slices)

切片是Go语言中一种更常用、更灵活的动态长度序列。切片是对底层数组的一个引用,它包含三个组件:指向底层数组的指针、切片的长度(length)和切片的容量(capacity)。

核心特性:

  • 动态长度: 切片的长度可以在运行时动态变化(通过append等操作)。
  • 引用类型: 切片是引用类型。当一个切片被赋值给另一个切片,或者作为参数传递给函数时,复制的不是底层数组,而是切片头(即指向底层数组的指针、长度和容量)。因此,多个切片可以引用同一个底层数组。对其中一个切片通过索引进行的修改会影响到所有引用该底层数组的切片。

声明和初始化示例:

// 声明一个切片,并初始化,Go会自动创建一个匿名的底层数组
slice1 := []int{1, 2, 3} // 这是一个切片,不是数组

// 使用make函数创建一个切片,指定长度和容量(容量可选)
slice2 := make([]int, 3)     // 创建一个长度为3的切片,所有元素为零值
slice3 := make([]int, 3, 5)  // 创建一个长度为3,容量为5的切片,所有元素为零值
登录后复制

如果执行以下操作:

立即学习go语言免费学习笔记(深入)”;

s1 := []int{1, 2, 3}
s2 := s1 // s2和s1现在引用同一个底层数组
s2[0] = 99
fmt.Println(s1) // 输出: [99 2 3]
fmt.Println(s2) // 输出: [99 2 3]
登录后复制

可以看到,对s2的修改会影响s1,因为它们共享同一个底层数组。

3. 示例代码解析

现在,让我们分析原始问题中提供的代码,以理解其行为:

package main

import (
        "fmt"
        "math/rand" // 注意:原代码使用"rand",但Go 1.0后推荐使用"math/rand"
        "time"
)

func shuffle(arr []int) { // arr 是一个切片
        // 推荐使用 time.Now().UnixNano() 作为种子
        // rand.Seed() 在 Go 1.20+ 版本中已弃用,建议使用 rand.NewSource 和 rand.New
        rand.Seed(time.Now().UnixNano()) 
        for i := len(arr) - 1; i > 0; i-- {
                j := rand.Intn(i + 1) // rand.Intn(n) 返回 [0, n)
                arr[i], arr[j] = arr[j], arr[i]
        }
}

func main() {
        arr := []int{1, 2, 3, 4, 5} // 声明并初始化一个切片
        arr2 := arr                 // 将切片 arr 赋值给 arr2。此时,arr 和 arr2 共享同一个底层数组。
        shuffle(arr)                // 调用 shuffle 函数,传入 arr。
                                    // shuffle 函数接收到的是 arr 切片头的一个副本,
                                    // 但这个副本仍然指向 arr 所引用的那个底层数组。
        for _, i := range arr2 {
                fmt.Printf("%d ", i)
        }
        fmt.Println() // 打印换行
}
登录后复制

行为解释:

  1. arr := []int{1, 2, 3, 4, 5}:这一行代码创建了一个切片arr。Go语言会为这个切片自动分配一个匿名的底层数组,并让arr指向它。
  2. arr2 := arr:这一行是问题的关键。由于切片是引用类型,arr2并没有复制arr所引用的底层数组。相反,它仅仅复制了arr的切片头(包括指向底层数组的指针、长度和容量)。因此,现在arr和arr2都指向并操作同一个底层数组。
  3. shuffle(arr):当arr作为参数传递给shuffle函数时,shuffle函数内部的参数arr(局部变量)接收到的是main函数中arr切片头的一个副本。然而,这个副本的指针仍然指向main函数中arr所引用的那个底层数组。
  4. 在shuffle函数内部,对arr(即arr[i], arr[j] = arr[j], arr[i])进行的任何修改,都是直接作用于这个共享的底层数组。
  5. 当shuffle函数执行完毕返回main函数后,由于arr2也指向同一个底层数组,因此当main函数遍历arr2并打印其内容时,它看到的是已经被shuffle函数修改(打乱)后的数据。

这就是为什么arr2会被打乱的原因:arr和arr2并非独立的数组,它们是共享同一底层数据的两个切片引用。

4. 注意事项与总结

  • 区分数组和切片: 记住,带固定长度的[N]Type是数组,不带固定长度的[]Type是切片。
  • 值类型 vs. 引用类型: 数组是值类型,复制时是深拷贝;切片是引用类型,复制时是浅拷贝(只复制切片头,底层数据共享)。
  • 函数传参: 当将数组传递给函数时,函数会得到数组的一个副本;当将切片传递给函数时,函数得到的是切片头的一个副本,但它仍然指向原始的底层数组,因此函数内部对切片元素的修改会影响到调用者。
  • 随机数生成: 示例代码中rand.Seed(time.Nanoseconds())在Go的math/rand包中已弃用,推荐使用rand.Seed(time.Now().UnixNano())或更现代的rand.NewSource和rand.New来创建更安全的随机数生成器。

通过深入理解数组和切片这两种数据结构的特性,开发者可以更好地利用Go语言的强大功能,避免常见的编程陷阱,并编写出更加健壮和高效的代码。

以上就是Go语言中数组与切片的区别及引用行为解析的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号