
在go语言中,切片(slice)是一种强大且灵活的数据结构,它引用一个底层数组的连续片段。切片本身不存储任何数据,它只是一个结构体,包含指向底层数组的指针、长度(len)和容量(cap)。
创建切片最常用的方法是使用内置的make函数。make函数有以下两种形式用于创建切片:
索引访问规则: 对切片s进行索引访问s[i]时,要求索引i必须满足 0 <= i < len(s)。如果i超出这个范围,Go运行时就会触发一个panic: runtime error: index out of range错误。
考虑一个常见的场景:创建并操作一个二维像素网格([][]uint8)。以下是一个可能导致“索引越界”错误的代码示例:
package main
import (
"fmt"
"golang.org/x/tour/pic"
)
func Pic(dx, dy int) [][]uint8 {
fmt.Printf("%d x %d\n\n", dx, dy)
// 错误示例:外层切片初始化时长度为0
pixels := make([][]uint8, 0, dy) // 长度为0,容量为dy
for y := 0; y < dy; y++ {
// 第一次访问 pixels[y] 时,由于 pixels 的长度为0,y=0 已经越界
pixels[y] = make([]uint8, 0, dx) // 长度为0,容量为dx
for x := 0; x < dx; x++ {
// 如果能执行到这里,pixels[y] 的长度也为0,pixels[y][x] 会再次越界
pixels[y][x] = uint8(x * y)
}
}
return pixels
}
func main() {
pic.Show(Pic)
}运行上述代码,会得到类似以下的错误信息:
panic: runtime error: index out of range [0] with length 0
这个错误明确指出,在尝试访问pixels[0]时发生了越界,因为pixels切片的当前长度为0。
立即学习“go语言免费学习笔记(深入)”;
错误原因剖析: 问题出在pixels := make([][]uint8, 0, dy)这行代码。它创建了一个容量为dy但长度为0的切片。这意味着尽管底层数组可能已经分配了足够的空间来容纳dy个[]uint8切片,但我们不能通过索引0到dy-1来直接访问它们,因为切片的可访问长度是0。任何对pixels[y]的直接赋值或读取操作,只要y >= 0,都会导致索引越界。
同样的问题也存在于内层切片的初始化中:pixels[y] = make([]uint8, 0, dx)。即使外层切片初始化正确,如果内层切片也以长度0初始化,那么对pixels[y][x]的访问也会导致越界。
要正确地创建并操作一个二维切片,我们需要确保在访问任何索引之前,切片的长度已经足够。
方法一:预先分配所有层级的长度
这是最直接且推荐的方法,尤其适用于已知确切尺寸的网格结构。
package main
import (
"fmt"
"golang.org/x/tour/pic"
)
func Pic(dx, dy int) [][]uint8 {
fmt.Printf("%d x %d\n\n", dx, dy)
// 正确初始化:外层切片长度为 dy
// 这将创建一个包含 dy 个 nil []uint8 切片的切片
pixels := make([][]uint8, dy)
for y := 0; y < dy; y++ {
// 正确初始化:为每个内层切片分配长度 dx
// 此时 pixels[y] 不再是 nil,而是长度为 dx 的 []uint8 切片
pixels[y] = make([]uint8, dx)
for x := 0; x < dx; x++ {
// 现在可以安全地通过索引访问并赋值
pixels[y][x] = uint8(x * y)
}
}
return pixels
}
func main() {
pic.Show(Pic)
}在这个修正后的代码中:
方法二:使用append构建(不适用于预知尺寸的网格直接赋值)
虽然不适用于直接通过[y][x]索引赋值的网格,但理解append与make(..., 0, cap)的配合使用,对于动态构建切片非常重要。如果外层切片长度为0但容量非0,我们通常会使用append来添加元素。
// 这是一个append的示例,不适用于本教程的二维网格直接索引赋值场景
func ExampleAppend(size int) []int {
s := make([]int, 0, size) // 长度为0,容量为size
for i := 0; i < size; i++ {
s = append(s, i) // 使用append添加元素
}
return s
}注意:对于需要像pixels[y][x]这样直接通过索引赋值的二维结构,append方法并不适用。append主要用于在切片末尾添加新元素,而不是填充预分配但未填充的索引位置。
通过掌握Go语言切片的这些核心概念和初始化技巧,开发者可以有效地避免常见的“索引越界”错误,编写出更加健壮和高效的Go程序。
以上就是Go语言切片深度解析:避免“索引越界”的陷阱的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号