今天来了解一下go语言的string的底层是怎么实现的,我们知道java的string是一个类实现的,其实go也是类似的,go使用结构体实现了string。
string的底层实现
go的string源码位于src/runtime/string.go
中,通过stringStruct
定义了string的数据结构
type stringStruct struct {
str unsafe.Pointer
len int
}
当我们在赋值一个字符串的时候发生了什么?
var str string
str = "helo"
因为底层是是靠结构体构建的,所以在构建过程中是根据字符串构建stringStruct,在转化为string
//go:nosplit
func gostringnocopy(str *byte) string { // 根据传进来的字符串地址构建string
ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)} // 构建stringStruct结构体
s := *(*string)(unsafe.Pointer(&ss)) // 将结构体转化为string字符串
return s
}
那么string转化为[]byte过程发生了什么呢?
具体转换过程如下
// The constant is known to the compiler.
// There is no fundamental theory behind this number.
const tmpStringBufSize = 32
type tmpBuf [tmpStringBufSize]byte
func stringtoslicebyte(buf *tmpBuf, s string) []byte {
var b []byte
if buf != nil && len(s) <= len(buf) {
*buf = tmpBuf{}
b = buf[:len(s)]
} else {
b = rawbyteslice(len(s))
}
copy(b, s)
return b
}
定义了string缓冲大小是32,使用一个byte数组
当string长度 < 32时,使用一个byte数组接收即可
当string长度 > 32时,就要创建一个新的slice,就是那个rawbyteslice函数,我们来看一下rawbyteslice函数
// rawbyteslice allocates a new byte slice. The byte slice is not zeroed.
func rawbyteslice(size int) (b []byte) {
cap := roundupsize(uintptr(size))
p := mallocgc(cap, nil, false)
if cap != uintptr(size) {
memclrNoHeapPointers(add(p, uintptr(size)), cap-uintptr(size))
}
*(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(cap)}
return
}
最后copy的执行是在切片中
// slicecopy is used to copy from a string or slice of pointerless elements into a slice.
func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen int, width uintptr) int {
if fromLen == 0 || toLen == 0 {
return 0
}
n := fromLen
if toLen < n {
n = toLen
}
if width == 0 {
return n
}
size := uintptr(n) * width
if raceenabled {
callerpc := getcallerpc()
pc := abi.FuncPCABIInternal(slicecopy)
racereadrangepc(fromPtr, size, callerpc, pc)
racewriterangepc(toPtr, size, callerpc, pc)
}
if msanenabled {
msanread(fromPtr, size)
msanwrite(toPtr, size)
}
if asanenabled {
asanread(fromPtr, size)
asanwrite(toPtr, size)
}
if size == 1 { // common case worth about 2x to do here
// TODO: is this still worth it with new memmove impl?
*(*byte)(toPtr) = *(*byte)(fromPtr) // known to be a byte pointer
} else {
memmove(toPtr, fromPtr, size)
}
return n
}
那么string转[]byte会发生内存拷贝吗?
从上面可以看出来,会发生内存拷贝
,理论来说,类型强转都会发生内存拷贝。
但是能不能在转换的时候不发生内存拷贝呢?文章来源:https://www.toymoban.com/news/detail-600829.html
答案是当然可以,通过底层结构转换,将string转化为底层结构体形式,再将这个结构体转换成切片的指针就好了文章来源地址https://www.toymoban.com/news/detail-600829.html
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
str :="helo"
strstruct := *(*reflect.StringHeader)(unsafe.Pointer(&str))
b := *(*[]byte)(unsafe.Pointer(&strstruct))
fmt.Printf("%v",b)
}
到了这里,关于go中string的底层是如何实现的的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!