问 fun1 和 fun2 fun3分别输出什么,为什么?
```go
func fun1() {
a := 2
c := (*string) (unsafe.Pointer(&a))
*c = "44"
fmt.Println(*c)
}
func fun2() {
a := "654"
c := (*string) (unsafe.Pointer(&a))
*c = "44"
fmt.Println(*c)
}
func fun3() {
a := 3
c := *(*string) (unsafe.Pointer(&a))
c = "445"
fmt.Println(c)
}
```
其实 unsafe.Pointer 强转,就是获取一个变量的原生指针,也就是我们所谓的C指针,它指向此变量在内存中的首地址。
注意看如下代码
a := 2
p := (*reflect.StringHeader)(unsafe.Pointer(&a))
fmt.Println(p)
c := (*string)(unsafe.Pointer(&a))
fmt.Println(p)
fmt.Println(len(*c))
*c = "44"
fmt.Println(p)
fmt.Println(len(*c))
a = 12321
fmt.Println(p)
fmt.Println(len(*c))
输出为
&{2 540179238}
&{2 540179238}
540179238
&{7341052 2}
2
&{12321 2}
2
这段输出说明什么?
把变量a的地址强转为StringHeader指针,得到p
把变量a的地址墙砖为string指针,得到c
输出p是为了看内存中的数据
输出len(*c)是为了证明字符串的长度跟p的Len字段是一样的
不能直接输出c是因为这个字符串中的Data字段,也就是真实内存块的指针经常是无效的
p := (*reflect.StringHeader)(unsafe.Pointer(&a))
把int变量a转为StringHeader指针p,输出得到
&{2 540179238}
p的Data字段值是2,因为a原本就是2. p的Len字段是540179238 这是一个内存中的原本的值,因为Len字段溢出了。
c := (*string)(unsafe.Pointer(&a))
把a转为string指针c,输出c的长度得到 540179238 证明目前c的长度跟p.Len是一致的
*c = "44"
对c进行赋值之后,输出p和len(*c)得到
&{7341052 2}
2
注意看p.Len也变成了2,因为字符串“44”长度就是2。而p.Data字段变成了7341052 ,这其实是字符串常量"44"在常量存储区里的内存地址 0x7003FC
再次对a赋值 a = 12321,输出p和len(*c) 得到
&{12321 2}
2
因为&a, p, c 这个三个地址是完全相同的。对a进行赋值,a的长度是int,跟p.Data的长度一致,所以p.Data被赋值成了12321,p.Len不变,所以len(*c) 也不变,都是2
注意这时不能输出*c,因为这时字符串的Data字段指向一个无效内存地址12321.
#16
更多评论
```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