如果你看"编码/二进制"包裹:
func (littleEndian) Uint64(b []byte) uint64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
}
func (littleEndian) PutUint64(b []byte, v uint64) {
_ = b[7] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
b[4] = byte(v >> 32)
b[5] = byte(v >> 40)
b[6] = byte(v >> 48)
b[7] = byte(v >> 56)
}
你会看到:
_ = b[7] // early bounds check to guarantee safety of writes below
现在考虑一下这个示例代码A(见注释):
package main
import "fmt"
func main() {
b := []byte{0, 1, 2, 3, 4, 5, 6}
var v uint64 = 0x0807060504030201
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
b[4] = byte(v >> 32)
b[5] = byte(v >> 40)
b[6] = byte(v >> 48)
b[7] = byte(v >> 56) // panic: runtime error: index out of range
fmt.Println(b)
}
此示例代码B(见注释):
package main
import "fmt"
func main() {
b := []byte{0, 1, 2, 3, 4, 5, 6}
var v uint64 = 0x0807060504030201
b[7] = byte(v >> 56) // panic: runtime error: index out of range
b[6] = byte(v >> 48)
b[5] = byte(v >> 40)
b[4] = byte(v >> 32)
b[3] = byte(v >> 24)
b[2] = byte(v >> 16)
b[1] = byte(v >> 8)
b[0] = byte(v)
fmt.Println(b)
}
示例代码C:
package main
import "fmt"
func main() {
b := []byte{0, 1, 2, 3, 4, 5, 6}
var v uint64 = 0x0807060504030201
_ = b[7] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
b[4] = byte(v >> 32)
b[5] = byte(v >> 40)
b[6] = byte(v >> 48)
b[7] = byte(v >> 56)
fmt.Println(b)
}
所以我有两个问题:
Q1:是否有必要进行早期检查以确保Golang中写入的安全性?
Q2:对于早期检查,保证写入的安全性,样本代码更简洁,性能更优化(速度),样本代码A,B,C或......?
A2:我认为B:因为它简洁并且做了早期检查,不是吗?
答案 0 :(得分:2)
Q1:是否有必要进行早期检查以确保Golang中写入的安全性?
这里的答案是“是和否”。通常,“不”,您通常不必在Go中插入边界检查,因为编译器会为您插入它们(这就是为什么当您尝试访问超出切片长度的内存位置时,示例会出现紧急情况)。但是,如果您正在执行多次写入,例如给出的示例,“是”,则需要插入早期边界检查,例如提供的示例,以确保您没有只有部分写入成功,处于错误状态(或者像在示例B中那样重构,以便第一次写入到最大的数组,确保在任何写入成功之前发生恐慌)。
然而,这不是一个“问题”,因为它是一个通用的bug类。如果您不以任何语言进行边界检查(或者不以最高索引开头,如果它是一种强制执行边界检查自己的语言,如Go),则写入不安全。它也在很大程度上取决于情况;在您发布的标准库的示例中,必须进行用户边界检查。但是,在您发布的第二个示例中,不需要进行用户边界检查,因为代码可以像B一样编写,其中编译器在第一行插入边界检查。
Q2:对于早期检查,以确保写入的安全性,样本代码更简洁,性能更优化(速度),示例代码A,B,C或......?
A2:我觉得B:因为它简洁而且做早期检查,不是吗?
你是对的。在B中,编译器将在第一次写入时插入边界检查,以保护其余写入。因为您使用常量(7
,6
,... 0
)为切片编制索引,所以编译器可以从其余写入中删除边界检查,因为它可以保证它们是安全的
答案 1 :(得分:1)
关于&#34;写作安全的评论&#34;在这里误导。在开始时放置最高边界检查只是一个优化。如果省略它,行为将不会改变(或变为&#34;不安全&#34;)但是您可能会遭受多个边界检查而不仅仅是一个的性能损失,因为每个后续较高索引的最小必需边界增量。
评论中说&#34;保证写入安全&#34;它只是意味着它将保证编译器后续写入是安全的,而不需要插入更多的边界检查。离开它不会使写入不安全,它只会让编译器插入更多的边界检查。在任何情况下,编译器都不会生成不安全的内存访问。
在代码中插入这个假的早期边界检查是否是一个好主意是有争议的,而不是使用它或重写代码以合法地使用最高索引,如在示例代码B中那样。只要它&#39;明确为什么会出现这种情况(例如,带有明智且无误导性的评论)我会说如果你愿意,可以使用它,并发现它有益。通常,通过手动优化,未来的编译器优化可能会使其冗余或以其他方式改变其有效性。