
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 没翻源码,花了几分钟看了下汇编。 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 |