忆梦|我擦~字符串转字节切片后,切片的容量竟然千奇百怪( 二 )

在看汇编代码之前 ,我们首先来看一看runtime.stringtoslicebyte的函数签名
func stringtoslicebyte(buf *tmpBuf, s string) []byte到这里只靠关键词已经无法看出更多的信息了,还是需要稍微了解一下汇编的语法,笔者在这里列出一点简单的分析 ,之后我们还是可以通过取巧的方法发现更多的东西
// 现象一给runtime.stringtoslicebyte的传参0x002f 00047 (test.go:6) LEAQ go.string."abc"(SB), AX // 将字符串"abc"放入寄存器AX0x0036 00054 (test.go:6) MOVQ AX, "".a+96(SP) // 将AX中的内容存入变量a中0x003b 00059 (test.go:6) MOVQ $3, "".a+104(SP) // 将字符串长度3存入变量a中0x0044 00068 (test.go:7) MOVQ $0, (SP) // 将0 传递个runtime.stringtoslicebyte(SB)的第一个参数(笔者猜测对应go中的nil)0x004c 00076 (test.go:7) PCDATA $0, $0 // 据说和gc有关 ,具体还不清楚 ,一般情况可以忽略0x004c 00076 (test.go:7) MOVQ AX, 8(SP) // 将AX中的内容传递给runtime.stringtoslicebyte(SB)的第二个参数0x0051 00081 (test.go:7) MOVQ $3, 16(SP) // 将字符串长度传递给runtime.stringtoslicebyte(SB)的第二个参数0x005a 00090 (test.go:7) CALL runtime.stringtoslicebyte(SB) // 调用函数 ,此行后面的几行代码是将返回值赋值给变量bs// 现象二给runtime.stringtoslicebyte的传参0x002f 00047 (test.go:6) LEAQ go.string."abc"(SB), AX // 将字符串"abc"放入寄存器AX0x0036 00054 (test.go:6) MOVQ AX, "".a+120(SP) // 将AX中的内容存入变量a中0x003b 00059 (test.go:6) MOVQ $3, "".a+128(SP) // 将字符串长度3存入变量a中0x0047 00071 (test.go:7) PCDATA $0, $20x0047 00071 (test.go:7) LEAQ ""..autotmp_5+64(SP), CX // 将内部变量autotmp_5放入寄存器CX0x004c 00076 (test.go:7) PCDATA $0, $10x004c 00076 (test.go:7) MOVQ CX, (SP) // 将CX中的内容传递给runtime.stringtoslicebyte(SB)的第一个参数0x0050 00080 (test.go:7) PCDATA $0, $00x0050 00080 (test.go:7) MOVQ AX, 8(SP) // 将AX中的内容传递给runtime.stringtoslicebyte(SB)的第二个参数0x0055 00085 (test.go:7) MOVQ $3, 16(SP) // 将字符串长度传递给runtime.stringtoslicebyte(SB)的第二个参数0x005e 00094 (test.go:7) CALL runtime.stringtoslicebyte(SB)通过上面汇编代码的分析可以知道 , 现象一和现象二的区别就是传递给runtime.stringtoslicebyte的第一个参数不同 。 通过对runtime包中stringtoslicebyte函数分析 , 第一个参数是否有值和字符串长度会影响代码执行的分支 , 从而生成不同的切片 ,因此容量不一样也是常理之中 ,下面我们看源码
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}然而 ,stringtoslicebyte的第一个参数什么情况下才会有值 , 什么情况下为nil, 我们仍然不清楚 。 那怎么办呢 ,只好祭出全局搜索大法:
# 在go源码根目录执行下面的命令grep stringtoslicebyte -r . | grep -v "//"最终在go的编译器源码cmd/compile/internal/gc/walk.go发现了如下代码块
忆梦|我擦~字符串转字节切片后,切片的容量竟然千奇百怪我们查看mkcall 函数签名可以知道, 从第四个参数开始的所有变量都会作为参数传递给第一个参数对应的函数 ,最后生成一个*Node的变量 。 其中Node结构体解释如下:
// A Node is a single node in the syntax tree.// Actually the syntax tree is a syntax DAG, because there is only one// node with Op=ONAME for a given instance of a variable x.// The same is true for Op=OTYPE and Op=OLITERAL. See Node.mayBeShared.


推荐阅读