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,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,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:因为它简洁而且做早期检查,不是吗?
Q1: is it necessary to early bounds check to guarantee safety of writes in Golang?
这里的答案是“是和否”.通常,“不”,您通常不必在Go中插入边界检查,因为编译器会为您插入它们(这就是为什么当您尝试访问超出切片长度的内存位置时,示例会出现紧急情况).但是,如果您正在执行多次写入,例如给出的示例,“是”,则需要插入早期边界检查,例如提供的示例,以确保您没有只有部分写入成功,处于错误状态(或者像在示例B中那样重构,以便第一次写入到最大的数组,确保在任何写入成功之前发生恐慌).
然而,这不是一个“问题”,因为它是一个通用的bug类.如果您不以任何语言进行边界检查(或者不以最高索引开始,如果它是一种强制执行边界检查自己的语言,如Go),则写入不安全.它也在很大程度上取决于情况;在您发布的标准库的示例中,必须进行用户边界检查.但是,在您发布的第二个示例中,不需要进行用户边界检查,因为代码可以像B一样编写,其中编译器在第一行插入边界检查.
Q2: for early bounds check to guarantee safety of writes which Sample Code is more concise and performance optimized (speed),sample code A,C or … ?
A2: I think B : because it is concise and do early bounds check,isn’t it?
你是对的.在B中,编译器将在第一次写入时插入边界检查,以保护其余写入.因为您使用常量(7,6,… 0)索引切片,所以编译器可以从其余写入中删除边界检查,因为它可以保证它们是安全的.