英文:
use unsafe pointer to get value from a []string
问题
我正在尝试学习Go语言中指针的工作原理。为什么下面的示例不起作用?
package main
import (
"fmt"
"unsafe"
)
type SliceOfStrings []string
// 创建一个[]string切片的函数
// 返回interface{}类型是因为我想学习指针的工作原理
func Create() interface{} {
var mySlice1 SliceOfStrings = make([]string, 0)
mySlice1 = append(mySlice1, "str1")
mySlice1 = append(mySlice1, "str2")
// 返回一个包含["str1","str2"]的切片
return mySlice1
}
func main() {
x := Create()
// 0xc000021940
fmt.Printf("x的地址是 %p \n", &x)
// 获取x的不安全指针
// 不安全指针。打印结果为0xc000021940
p1 := unsafe.Pointer(&x)
fmt.Println(p1)
// 无符号指针。打印结果为824633858368
p2 := uintptr(p1)
fmt.Println(p2)
// 打印结果与p1相同,为0xc000021940
p3 := unsafe.Pointer(p2)
fmt.Println(p3)
// 使p4指向与0xc000021940相同的地址
p4 := (*SliceOfStrings)(p3)
//fmt.Println(p4)
// 为什么这里不打印"str1"??
fmt.Println((*p4)[0])
// 我得到错误:运行时错误:无效的内存地址或空指针解引用
}
英文:
I am trying to learn how pointers work on go. Why is the following example not working?
package main
import (
"fmt"
"unsafe"
)
type SliceOfStrings []string
// function that creates an slice of []string
// returns interface{} cause I am interested on learning how pointers work
func Create() interface{} {
var mySlice1 SliceOfStrings = make([]string, 0)
mySlice1 = append(mySlice1, "str1")
mySlice1 = append(mySlice1, "str2")
// return a slice with as ["str1","str2"]
return mySlice1
}
func main() {
x := Create()
// 0xc000021940
fmt.Printf("address of x is %p \n", &x)
// get unsafe pointer to address of x
// unsafe pointer. Prints 0xc000021940
p1 := unsafe.Pointer(&x)
fmt.Println(p1)
// unsigned pointer. Prints 824633858368
p2 := uintptr(p1)
fmt.Println(p2)
// prints same value as p1 0xc000021940
p3 := unsafe.Pointer(p2)
fmt.Println(p3)
// Make p4 point to same address as 0xc000021940
p4 := (*SliceOfStrings)(p3)
//fmt.Println(p4)
// why this does not print "str1" ??
fmt.Println((*p4)[0])
// I get error: runtime error: invalid memory address or nil pointer dereference
}
答案1
得分: 4
Create()
返回一个类型为 interface{}
的值,所以 x
的类型是 interface{}
,因此 &x
的类型是 *interface{}
,而不是 *SliceOfStrings
。所以 x
指向的是一个 interface{}
值,而不是 SliceOfStrings
值!
如果你从 Create()
的返回值中进行 类型断言,它就可以工作:
x := Create().(SliceOfStrings)
还要在 main()
的末尾添加 runtime.KeepAlive(x)
,因为如果你不再引用 x
,它可能随时被垃圾回收。
通过这个改变,它可以正常工作并输出 str1
。在 Go Playground 上试一试。
总的来说,尽量避免使用 unsafe
包。你可以学习和使用指针,而不需要使用 unsafe
包。只有在万不得已的情况下才考虑使用 unsafe
!
英文:
Create()
returns a value of type interface{}
, so type of x
is interface{}
, so type of &x
is *interface{}
, and not *SliceOfStrings
. So x
points to an interface{}
value and not to a SliceOfStrings
value!
If you type assert SliceOfStrings
from the return value of Create()
, it works:
x := Create().(SliceOfStrings)
Also add runtime.KeepAlive(x)
at the end of your main()
, because if you don't refer to x
anymore, it can be garbage collected at any time.
With this change it works and outputs str1
. Try it on the Go Playground.
In general, stay away from package unsafe
as much as possible. You can learn and use pointers without package unsafe
. Only think of unsafe
as a last-last resort!
答案2
得分: 1
我能理解为什么会发生这种情况:
package main
import (
"fmt"
"unsafe"
)
type SliceOfStrings []string
// 当将切片传递给方法时,实际上是传递了这些数据。让我们来证明一下
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
func main() {
// 在 Go 中,一切都是通过值的复制来传递的。当你将一个切片传递给一个函数时,实际上是传递了这个:
// 参考:https://stackoverflow.com/a/39993797/637142
/*
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
*/
// 创建一个新的切片
var mySlice1 SliceOfStrings = make([]string, 0)
// 当添加元素时,我们需要保留新的内容,这就是为什么我们重新赋值
mySlice1 = append(mySlice1, "str1")
mySlice1 = append(mySlice1, "str2")
// 换句话说,这样做没有意义:
// _ = append(mySlice1, "str3") // 这样做会丢失新的头部值
// 看一下 mySlice 的头部内容
var pointerToMySlice1 = unsafe.Pointer(&mySlice1)
var header *SliceHeader = ((*SliceHeader)(pointerToMySlice1))
fmt.Println(*header)
// {824634220576 2 2}
// 将该头部复制到另一个切片中
var copy SliceOfStrings = mySlice1
var pointerToCopy = unsafe.Pointer(©)
header = ((*SliceHeader)(pointerToCopy))
fmt.Println(*header)
// 打印相同的内容
// {824634220576 2 2}
// 现在让我们用接口来做同样的事情
var copy2 interface{} = mySlice1
var pointerToCopy2 = unsafe.Pointer(©2)
header = ((*SliceHeader)(pointerToCopy2))
fmt.Println(*header)
// 这样打印出来了!
// {4845280 824634375976 0}
// 我不明白为什么会发生这种情况。但这就是为什么这个示例不起作用的原因
// 我试图从错误的内存地址 4845280 访问数组
// 现在让我们按照 izca 告诉我们的做法来做
var copy3 interface{} = mySlice1
tmp := (copy3).(SliceOfStrings)
var pointerToCopy3 = unsafe.Pointer(&tmp)
header = ((*SliceHeader)(pointerToCopy3))
fmt.Println(*header)
// 这样打印出来了!
// {824634220576 2 2}
// 这才是正确的值
// 让我们尝试从该内存地址(824634220576)获取数组
pointerToActualArray := unsafe.Pointer(header.Data)
// 将其转换为 (*[2]string)
var pointerFinal *[2]string = ((*[2]string)(pointerToActualArray))
// 现在打印第一个值
fmt.Println((*pointerFinal)[0])
// 打印 str1
}
英文:
I was able to understand why this happens:
package main
import (
"fmt"
"unsafe"
)
type SliceOfStrings []string
// when passing a slice to a method you are passing this data. Lets prove it
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
func main() {
// On go everything is passed by coping values. When you pass a slice to a function you are passing this:
// reference: https://stackoverflow.com/a/39993797/637142
/*
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
*/
// create a new slice
var mySlice1 SliceOfStrings = make([]string, 0)
// when appending we need to keep the new content that is why we reasig it
mySlice1 = append(mySlice1, "str1")
mySlice1 = append(mySlice1, "str2")
// in other words this will make no sense:
// _ = append(mySlice1, "str3") // doing this will lose the new header value
// lets see the content of header mySlice
var pointerToMySlice1 = unsafe.Pointer(&mySlice1)
var header *SliceHeader = ((*SliceHeader)(pointerToMySlice1))
fmt.Println(*header)
// {824634220576 2 2}
// lets copy that header to another slice
var copy SliceOfStrings = mySlice1
var pointerToCopy = unsafe.Pointer(&copy)
header = ((*SliceHeader)(pointerToCopy))
fmt.Println(*header)
// prints the same thing
// {824634220576 2 2}
// now lets do the same thing but with an interface
var copy2 interface{} = mySlice1
var pointerToCopy2 = unsafe.Pointer(&copy2)
header = ((*SliceHeader)(pointerToCopy2))
fmt.Println(*header)
// this prints!
// {4845280 824634375976 0}
// I dont understand why this happens. But this is the reason why the example does not work
// I was trying to access an array from memory address 4845280 that is wrong
// now lets do what izca told us
var copy3 interface{} = mySlice1
tmp := (copy3).(SliceOfStrings)
var pointerToCopy3 = unsafe.Pointer(&tmp)
header = ((*SliceHeader)(pointerToCopy3))
fmt.Println(*header)
// this prints!
// {824634220576 2 2}
// that is the correct value
// lets try to get the array from that memory address (824634220576)
pointerToActualArray := unsafe.Pointer(header.Data)
// lets cast that to (*[2]string)
var pointerFinal *[2]string = ((*[2]string)(pointerToActualArray))
// now print the first value
fmt.Println((*pointerFinal)[0])
// prints str1
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论