V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
beego
yellowmarlboro
V2EX  ›  Go 编程语言

golang 的指针地址问题

  •  
  •   yellowmarlboro · 361 天前 · 1505 次点击
    这是一个创建于 361 天前的主题,其中的信息可能已经有所发展或是发生改变。
    	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
    
    • 为什么 & 出来的总差 16 ? unsafe.Pointer 是一样的?(请忽略切片的)
    第 1 条附言  ·  355 天前
    我又凑不要脸来加问题了

    我该怎么调用字符串 struct 中的指针指向的底层数组并且修改他的值(如果可以修改的话)?
    12 条回复    2021-02-03 20:02:34 +08:00
    yellowmarlboro
        1
    yellowmarlboro  
    OP
       361 天前
    已经了解!
    End
    iQXQZX
        2
    iQXQZX  
       361 天前
    为什么为什么为什么?插眼
    iamzuoxinyu
        3
    iamzuoxinyu  
       361 天前 via Android
    分清栈上变量和字符串常量。
    koujyungenn
        4
    koujyungenn  
       361 天前
    @yellowmarlboro 谜语人?
    kakach
        5
    kakach  
       361 天前
    蹲一个大佬的回答
    kiddingU
        6
    kiddingU  
       361 天前
    你自己用法姿势不对,正确的应该是这样吧:unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&s0)))
    yuguorui96
        7
    yuguorui96  
       361 天前   ❤️ 1
    没翻源码,花了几分钟看了下汇编。

    s0, s1, s2 之间的地址总差 16 是因为 String 在栈上的结构是:
    struct String {
    char *data;
    int length;
    };
    sizeof(一个指针)+sizeof(一个 int)正好是 16 字节( 64 位环境下)。

    又因为栈上变量都是编译器按序分配的,所以有了楼主看到的结果。

    至于为啥 unsafe.Pointer 显示的结果是一致的,我个人猜测是因为字符串常量池(这个得去翻 Golang 的编译器,一会儿早上有会就不深挖了),即相同的字符串不会在内存中存在两次,有多次引用就把上面 data 的指针指向实际存储数据的 backing store 。这是一个常见优化,Python 中也存在。
    Dganzh
        8
    Dganzh  
       361 天前
    字符串实际是一个**stuct
    struct string {
    byte* str;
    intgo len;
    }

    大小就是 16
    unsafe.Sizeof(s0) == 16
    kiddingU
        9
    kiddingU  
       361 天前
    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
    })
    kiddingU
        10
    kiddingU  
       361 天前
    @yuguorui96 编译一下,可以看到,golang 对于相同的字符串确实是做了优化处理,只存一份的
    iQXQZX
        11
    iQXQZX  
       361 天前
    @kiddingU 学到了
    yellowmarlboro
        12
    yellowmarlboro  
    OP
       355 天前
    下棋的时候翻出答案就没来得及回具体解决,抱歉~
    @kiddingU @yuguorui96 @Dganzh 等大佬都回答的要比我好!
    多谢各位~
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3238 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 92ms · UTC 10:19 · PVG 18:19 · LAX 02:19 · JFK 05:19
    ♥ Do have faith in what you're doing.