golang string转化byte数组一定会发生内存拷贝吗

在背golang面试八股文时出现,经常会遇到一个说法:golang在强制类型转换时,会发生内存拷贝。为了验证该说法,本人做了一个实验验证,代码如下:

package main

import (
	"fmt"
	"reflect"
	"strings"
	"unsafe"
)

func main() {
	str := "Hello, World!"
	byteSlice := []byte(str)
	fmt.Printf("String: %p\n", (*reflect.StringHeader)(unsafe.Pointer(&str)).Data)
	fmt.Printf(" slice: %p\n", (*reflect.SliceHeader)(unsafe.Pointer(&byteSlice)).Data)
}

执行结果如下:

String: %!p(uintptr=2029802)
slice: %!p(uintptr=2029802)

明显,两者string和byte切片的底层指向的字符数组起始地址时一致的,这与一定会发生拷贝的现象描述并不符合。那么这种现象的具体原因是什么?
由于string底层指向的是只读的字符数组,是无法编辑,尝试对byte底层数组操作尝试下地址的变化:

package main

import (
	"fmt"
	"reflect"
	"strings"
	"unsafe"
)

func main() {
	str := "Hello, World!"
	byteSlice := []byte(str)
	byteSlice = append(byteSlice, 'c')
	fmt.Printf("String: %p\n", (*reflect.StringHeader)(unsafe.Pointer(&str)).Data)
	fmt.Printf(" slice: %p\n", (*reflect.SliceHeader)(unsafe.Pointer(&byteSlice)).Data)
}

执行结果:

after String: %!p(uintptr=1050905)      
after slice: %!p(uintptr=824634441728)  

结果是byte切换指向的内存地址发生改变,这表明byte切片底层起始指向的byte数组跟string是同一个,编辑后发生了改变。
实际原因:是golang 编译器的优化。Go 1.22版本中(测试使用go版本为go 1.22),编译器对string到byte切片的转换进行了优化。这意味着当你将一个string转换为byte切片时,编译器可能会生成更高效的代码。
查看[]byte()转化的汇编

        0x005f 00095 (D:/mycode/distribid-test/main.go:14)      MOVQ    main.str+40(SP), CX
        0x0064 00100 (D:/mycode/distribid-test/main.go:14)      MOVQ    main.str+48(SP), DX
        0x0069 00105 (D:/mycode/distribid-test/main.go:14)      TESTQ   CX, CX
        0x006c 00108 (D:/mycode/distribid-test/main.go:14)      LEAQ    runtime.zerobase(SB), SI
        0x0073 00115 (D:/mycode/distribid-test/main.go:14)      CMOVQNE CX, SI
        0x0077 00119 (D:/mycode/distribid-test/main.go:14)      MOVQ    SI, main.byteSlice+56(SP)
        0x007c 00124 (D:/mycode/distribid-test/main.go:14)      MOVQ    DX, main.byteSlice+64(SP)
        0x0081 00129 (D:/mycode/distribid-test/main.go:14)      MOVQ    DX, main.byteSlice+72(SP)

其中并没有申请内存的操作。
使用go 1.21版本测试,发现输出结果中指向的底层byte数组不一致,说明编译器并没有对转化操作进行优化

str is %!p(uintptr=13901539)
bytes is %!p(uintptr=824634171080)

[]byte()对应的汇编:

0x0089 00137 (D:/test/tesst.go:13)      MOVUPS  X15, main..autotmp_2+88(SP)
        0x008f 00143 (D:/test/tesst.go:13)      MOVQ    main.bytes+104(SP), AX
        0x0094 00148 (D:/test/tesst.go:13)      PCDATA  $1, $4
        0x0094 00148 (D:/test/tesst.go:13)      CALL    runtime.convT64(SB)
        0x0099 00153 (D:/test/tesst.go:13)      LEAQ    type:uintptr(SB), DX
        0x00a0 00160 (D:/test/tesst.go:13)      MOVQ    DX, main..autotmp_2+88(SP)
        0x00a5 00165 (D:/test/tesst.go:13)      MOVQ    AX, main..autotmp_2+96(SP)
        0x00aa 00170 (D:/test/tesst.go:13)      LEAQ    go:string."bytes is %p"(SB), AX
        0x00b1 00177 (D:/test/tesst.go:13)      MOVL    $11, BX
        0x00b6 00182 (D:/test/tesst.go:13)      LEAQ    main..autotmp_2+88(SP), CX
        0x00bb 00187 (D:/test/tesst.go:13)      MOVL    $1, DI
        0x00c0 00192 (D:/test/tesst.go:13)      MOVQ    DI, SI
        0x00c3 00195 (D:/test/tesst.go:13)      PCDATA  $1, $0
        0x00c3 00195 (D:/test/tesst.go:13)      CALL    fmt.Printf(SB)

其中runtime.convT64操作为申请内存操作。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值