0

0

如何避免 Go 中字节切片(byte slice)在函数调用中被意外修改

花韻仙語

花韻仙語

发布时间:2025-12-31 14:38:18

|

771人浏览过

|

来源于php中文网

原创

如何避免 Go 中字节切片(byte slice)在函数调用中被意外修改

go 中切片是引用类型,直接赋值不会复制底层数组;若需保持原切片不变,必须显式创建独立副本(如用 append([]byte(nil), src...)),否则 shuffle 等就地操作会同时影响原始数据。

在 Go 语言中,[]byte 是一个切片(slice),其底层结构包含指向数组的指针、长度(len)和容量(cap)。当你执行 cryptkey := alphabet 时,并未创建新底层数组,而是让 cryptkey 和 alphabet 共享同一块内存。因此,后续对 cryptkey 的任何就地修改(如交换元素)都会直接反映在 alphabet 上——这正是你观察到“两个切片都被打乱”的根本原因。

要真正隔离数据,必须进行深拷贝(deep copy)。最简洁、惯用且安全的方式是使用 append 构造新切片:

out := append([]byte(nil), b...)

该语句等价于:分配一个长度为 len(b)、类型为 []byte 的新切片,并将 b 的所有元素逐个复制进去。[]byte(nil) 提供空切片作为起点,append 自动处理内存分配,语义清晰且零分配冗余(相比 make([]byte, len(b)) + copy() 更简练)。

修正后的完整示例:

HTTPie AI
HTTPie AI

AI API开发工具

下载
package main

import (
    "fmt"
    "math/rand"
    "time" // 注意:添加 time 包以正确初始化随机种子
)

func main() {
    alphabet := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz.")
    cryptkey := alphabet // 此时仍共享底层数组(但后续 shuffle 不再影响它)

    fmt.Println("Original alphabet:", string(alphabet))

    // 初始化随机种子,避免每次运行结果相同
    rand.Seed(time.Now().UnixNano())
    cryptkey = shuffle(cryptkey)

    fmt.Println("After shuffle — alphabet unchanged:", string(alphabet))
    fmt.Println("Shuffled cryptkey:", string(cryptkey))
}

func shuffle(b []byte) []byte {
    l := len(b)
    if l <= 1 {
        return append([]byte(nil), b...) // 边界情况也保证副本
    }
    out := append([]byte(nil), b...) // ✅ 关键:创建独立副本

    for i := range out {
        dest := rand.Intn(l)
        out[i], out[dest] = out[dest], out[i]
    }
    return out
}

⚠️ 注意事项

  • rand.Intn 在 Go 1.20+ 已弃用,生产环境建议改用 rand.New(rand.NewSource(seed)).Intn(l) 或 rand.New(rand.NewPCG()),但本例为简洁保留旧用法(需配合 rand.Seed);
  • 切勿使用 out := b[:len(b):len(b)] 或 out := b,它们仍共享底层数组;
  • 若需高性能批量复制,copy(dst, src) 亦可,但 append(..., b...) 更符合 Go 的惯用风格且不易出错。

总结:Go 中切片赋值不等于复制数据。凡涉及可能修改切片内容的函数(如 shuffle、reverse、sort.Slice 等),务必在函数内部通过 append([]T(nil), s...) 或 copy 显式创建副本,才能保障输入参数的不可变性(immutability)与函数纯度(purity)。

相关专题

更多
sort排序函数用法
sort排序函数用法

sort排序函数的用法:1、对列表进行排序,默认情况下,sort函数按升序排序,因此最终输出的结果是按从小到大的顺序排列的;2、对元组进行排序,默认情况下,sort函数按元素的大小进行排序,因此最终输出的结果是按从小到大的顺序排列的;3、对字典进行排序,由于字典是无序的,因此排序后的结果仍然是原来的字典,使用一个lambda表达式作为key参数的值,用于指定排序的依据。

378

2023.09.04

go语言 数组和切片
go语言 数组和切片

本专题整合了go语言数组和切片的区别与含义,阅读专题下面的文章了解更多详细内容。

45

2025.09.03

go语言 数组和切片
go语言 数组和切片

本专题整合了go语言数组和切片的区别与含义,阅读专题下面的文章了解更多详细内容。

45

2025.09.03

append用法
append用法

append是一个常用的命令行工具,用于将一个文件的内容追加到另一个文件的末尾。想了解更多append用法相关内容,可以阅读本专题下面的文章。

338

2023.10.25

python中append的用法
python中append的用法

在Python中,append()是列表对象的一个方法,用于向列表末尾添加一个元素。想了解更多append的更多内容,可以阅读本专题下面的文章。

1060

2023.11.14

python中append的含义
python中append的含义

本专题整合了python中append的相关内容,阅读专题下面的文章了解更多详细内容。

167

2025.09.12

append用法
append用法

append是一个常用的命令行工具,用于将一个文件的内容追加到另一个文件的末尾。想了解更多append用法相关内容,可以阅读本专题下面的文章。

338

2023.10.25

python中append的用法
python中append的用法

在Python中,append()是列表对象的一个方法,用于向列表末尾添加一个元素。想了解更多append的更多内容,可以阅读本专题下面的文章。

1060

2023.11.14

小游戏4399大全
小游戏4399大全

4399小游戏免费秒玩大全来了!无需下载、即点即玩,涵盖动作、冒险、益智、射击、体育、双人等全品类热门小游戏。经典如《黄金矿工》《森林冰火人》《狂扁小朋友》一应俱全,每日更新最新H5游戏,支持电脑与手机跨端畅玩。访问4399小游戏中心,重温童年回忆,畅享轻松娱乐时光!官方入口安全绿色,无插件、无广告干扰,打开即玩,快乐秒达!

30

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3.1万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

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