unsafe.Pointer 面试题,几乎没几个人答得对,有人知道答案的原因吗?

af913337456 · · 2580 次点击
你的答案根本不在点上 先说答案: fun1() 如果不panic的话,是能输出44。但是有很大概率panic fun2() 输出44 fun3() 输出445 原因如下: 首先,要理解go的string类型是什么 通过reflect包,我们可以知道,在Golang底层,string其实是struct: type StringHeader struct { Data uintptr Len int } 其实一个string是一个保存了真实字符数组指针和长度的结构体 对于fun1 , a是一个int变量,unsafe.Pointer(&a) 是一个原生指针,指向的内存保存了一个int c := (*string)(unsafe.Pointer(&a)) 将这个原生指针转换为了指向string的指针 注意c 现在是指向string的指针,换句话说,c指向的内存被当做了一个StringHeader 结构体来处理 但是c指向的内存,只分配了一个int的长度,并且这个int的值是2 所以目前c中 Data 值为2, Len是无效的,因为Len现在已经溢出了有效内存范围,其内的值是随机的(该溢出区域内存的原始值) 这时对 *c 进行赋值 "44", "44"是一个字符串常量,保存在只读的常量存储区中。 赋值时 其实是把常量字符串"44"的指针赋给了c的Data字段,把其长度2赋给了c的Len字段。 注意这里是一个野指针赋值,因为 Len 字段已经溢出到有效内存之外了,但是这里panic的概率很低,因为内存padding机制 然后输出 fmt.Println(*c) 这里,因为c的Len字段是溢出到无效内存中,所以如果其他指令有变量定义的操作,很可能把Len字段给覆盖掉 一旦Len被覆盖,那么其中保存的长度就不在是2,这时输出c,很大概率就会panic。 如果运气好,Len没有被覆盖,那么输出c,就能正常输出44 对于fun2,可以根据上述原理自行分析。因为a变量也是一个StringHeader,c其实与unsafe.Pointer(&a)指向同一个StringHeader。 对于fun3, 因为c本来就是一个新创建的string变量,也就是一个独立的StringHeader,没有复用变量a的内存,所以是正常的赋值和输出,自然不会 有问题。
#9
更多评论
```go func fun1() { a := 2 c := (*string) (unsafe.Pointer(&a)) //c为a的unsafe.pointer强制转为string指针,*c指的是a的值,a的值是int型的,这里*c = "44"是强制赋了个字符串,所以赋值是失败的,只是没有提示,因为使用了unsafe *c = "44" fmt.Println(*c) //这时的c是string指针,*c指的应该是一个字符串,但是里面实际上是int数据,所以操作也是失败的 } func fun2() { a := "654" c := (*string) (unsafe.Pointer(&a)) //根据fun1的解释 a 为字符串型,那整个流程就通了,自然*c打出来的是 44,在*c="44"上面打印出来的是肯定是654 *c = "44" fmt.Println(*c) } func fun3() { a := 3 c := *(*string) (unsafe.Pointer(&a)) //这里获取的是a地址里的string值,但a的值是int型,所以操作失败,没有得到值,但c被定义里了string型变量,c = "445",给string型变量赋string值 自然是正常的 c = "445" fmt.Println(c) } ```
#1
实际工作中会用到这些吗?
#2