使用不安全指针从 []string 中获取值。

huangapple go评论120阅读模式
英文:

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(&copy)
	header = ((*SliceHeader)(pointerToCopy))
	fmt.Println(*header)
	// 打印相同的内容
	// {824634220576 2 2}

	// 现在让我们用接口来做同样的事情
	var copy2 interface{} = mySlice1
	var pointerToCopy2 = unsafe.Pointer(&copy2)
	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
}



huangapple
  • 本文由 发表于 2022年4月15日 16:53:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/71881969.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定