s0 := "something"
s1 := "something"
s2 := "something"[7:]
fmt.Println(&s0, &s1, &s2)
fmt.Printf("%d \n", (*reflect.StringHeader)(unsafe.Pointer(&s0)).Data)
fmt.Printf("%d \n", (*reflect.StringHeader)(unsafe.Pointer(&s1)).Data)
fmt.Printf("%d \n", (*reflect.StringHeader)(unsafe.Pointer(&s2)).Data)
结果是
0xc00010a040 0xc00010a050 0xc00010a060
4974445
4974445
4974452
1
yellowmarlboro OP 已经了解!
End |
2
iQXQZX 2021-01-28 23:42:38 +08:00
为什么为什么为什么?插眼
|
3
iamzuoxinyu 2021-01-28 23:56:20 +08:00 via Android
分清栈上变量和字符串常量。
|
4
koujyungenn 2021-01-29 09:12:21 +08:00
@yellowmarlboro 谜语人?
|
5
kakach 2021-01-29 09:31:28 +08:00
蹲一个大佬的回答
|
6
kiddingU 2021-01-29 09:55:23 +08:00
你自己用法姿势不对,正确的应该是这样吧:unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&s0)))
|
7
yuguorui96 2021-01-29 09:55:26 +08:00 1
没翻源码,花了几分钟看了下汇编。
s0, s1, s2 之间的地址总差 16 是因为 String 在栈上的结构是: struct String { char *data; int length; }; sizeof(一个指针)+sizeof(一个 int)正好是 16 字节( 64 位环境下)。 又因为栈上变量都是编译器按序分配的,所以有了楼主看到的结果。 至于为啥 unsafe.Pointer 显示的结果是一致的,我个人猜测是因为字符串常量池(这个得去翻 Golang 的编译器,一会儿早上有会就不深挖了),即相同的字符串不会在内存中存在两次,有多次引用就把上面 data 的指针指向实际存储数据的 backing store 。这是一个常见优化,Python 中也存在。 |
8
Dganzh 2021-01-29 09:58:57 +08:00
字符串实际是一个**stuct
struct string { byte* str; intgo len; } 大小就是 16 unsafe.Sizeof(s0) == 16 |
9
kiddingU 2021-01-29 10:56:36 +08:00
import (
"github.com/davecgh/go-spew/spew" "reflect" "unsafe" ) func main() { s0 := "something" s1 := "something" spew.Dump(&s0) spew.Dump(&s1) spew.Dump((*reflect.StringHeader)(unsafe.Pointer(&s0))) spew.Dump((*reflect.StringHeader)(unsafe.Pointer(&s1))) } -------------- (*string)(0xc0001042b0)((len=9) "something") (*string)(0xc0001042c0)((len=9) "something") (*reflect.StringHeader)(0xc0001042b0)({ Data: (uintptr) 0x1101229, Len: (int) 9 }) (*reflect.StringHeader)(0xc0001042c0)({ Data: (uintptr) 0x1101229, Len: (int) 9 }) |
10
kiddingU 2021-01-29 11:08:00 +08:00
@yuguorui96 编译一下,可以看到,golang 对于相同的字符串确实是做了优化处理,只存一份的
|
12
yellowmarlboro OP |