猿问

为什么 Golang string.Builder String() 可以在没有

Go 的 strings 包定义了一个 Builder 类型,它有一个 String() 方法


func (b *Builder) String() string {

    return *(*string)(unsafe.Pointer(&b.buf))

}

正如 reflect pacakge 所指出的,一个字节切片被定义为一个 SliceHeader 和一个由 header 的 Data 字段指向的关联数据块:


type SliceHeader struct {

  Data uintptr

  Len  int

  Cap  int

}

并且 string 被定义为一个 StringHeader 和一个由 header 的 Data 字段指向的关联数据块


type StringHeader struct {

  Data uintptr

  Len  int

}

SliceHeader 有 3 个字段(Data、Len 和 Cap),而 StringHeader 只有 2 个(Data 和 Len),那么如何直接将字节切片转换为字符串呢?


*(*string)(unsafe.Pointer(&buf))

据我了解,我们应该编写以下代码:


func byte2str(b []byte) string {

  hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))

  return *(*string)(unsafe.Pointer(&reflect.StringHeader{Data: hdr.Data, Len: hdr.Len}))

}


陪伴而非守候
浏览 131回答 1
1回答

翻阅古今

让我们分解一下。表达式&buf是指向切片头的指针。指向切片的指针是指向切片头的指针。该表达式(unsafe.Pointer(&buf)是从切片头指针到unsafe.Pointer. unsafe.Pointer 类型有一个神奇的属性——它可以与任何其他指针类型相互转换。表达式是(*string)(unsafe.Pointer(&buf))从切片头指针到字符串头指针的转换。指向字符串的指针是指向标头的指针。此时,我们有一个字符串头指针,它实际上指向一个切片头。乍一看,这似乎很糟糕,但一切都很好。字符串头的内存布局是切片头的内存布局的前缀。该表达式*(*string)(unsafe.Pointer(&buf))取消引用字符串头指针以获取字符串。此操作将数据和长度字段从切片标头复制到字符串标头。如果对切片头字段重新排序而没有对字符串头字段进行相应更改,则此代码将中断,但这永远不会发生。
随时随地看视频慕课网APP

相关分类

Go
我要回答