英文:
Slice type casting in Go
问题
我对Go语言还比较陌生,来自C++背景,遇到了一些奇怪的问题。以下是要翻译的代码:
package main
import (
"fmt"
"unsafe"
)
func main() {
arr := []string{"one", "two", "three"}
address := unsafe.Pointer(&arr)
addPtr := (*[]string)(unsafe.Pointer(*(*uintptr)(address)))
fmt.Println((*addPtr)[0])
}
这段代码报错如下:
runtime error: growslice: len out of range
如果我将类型转换更改为以下形式:
addPtr := (*[0]string)(unsafe.Pointer(*(*uintptr)(address)))
上述代码将正常工作。
我理解这是将数组指针进行类型转换,而数组必须具有常量大小,但是如何将其转换为切片指针呢?
更令人困惑的是,可以获取切片的地址并将其分配给指针,如下所示:
func main() {
arr := []string{"one", "two", "three"}
var arrPtr *[]string = &arr
fmt.Println((*arrPtr)[0])
}
这次,尽管指针的类型与我在第一个示例中进行不安全指针转换的类型相同,但一切都能正常工作。有人可以帮助理解这里到底发生了什么吗?
英文:
I'm quite new to Go coming from C++ background and have stumbled upon some weird issue.
Here's the code:
package main
import (
"fmt"
"unsafe"
)
func main() {
arr := []string { "one", "two", "three" }
address := unsafe.Pointer(&arr)
addPtr := (*[]string)(unsafe.Pointer(*(*uintptr)(address)))
fmt.Println((*addPtr)[0])
}
This code fails with:
> runtime error: growslice: len out of range
If i change cast, for example, to this:
addPtr := (*[0]string)(unsafe.Pointer(*(*uintptr)(address)))
The code above works just fine.
I understand that this is a cast to an array pointer and array must have constant size,
but how to cast it to pointer to a slice then?
What's even more confusing that it's possible to take an address of slice and assign it to the pointer like this:
func main() {
arr := []string { "one", "two", "three" }
var arrPtr *[]string = &arr
fmt.Println((*arrPtr)[0])
}
And this time everything will work despite the fact that the type of pointer is the same type that i was casting unsafe pointer to in the first example.
Can someone help to understand what's exactly going on here?
答案1
得分: 2
一些背景信息:切片头包含指向后备数组的指针、长度和容量。
问题的第一部分代码将切片头转换为切片头指针。go vet
命令警告该问题中的代码可能误用了 unsafe.Pointer。
修复方法是删除额外的解引用操作,使代码从切片头指针转换为切片头指针。
arr := []string{"one", "two", "three"}
address := unsafe.Pointer(&arr)
addPtr := (*[]string)(unsafe.Pointer((*uintptr)(address)))
fmt.Println((*addPtr)[0]) // 输出 one
不需要将其转换为 *uintptr。简化为:
arr := []string{"one", "two", "three"}
address := unsafe.Pointer(&arr)
addPtr := (*[]string)(unsafe.Pointer(address))
fmt.Println((*addPtr)[0]) // 输出 one
不需要使用 unsafe 操作。简化为:
arr := []string{"one", "two", "three"}
addPtr := &arr
fmt.Println((*addPtr)[0]) // 输出 one
使用以下代码将切片的后备数组指针转换为数组指针。该代码很脆弱,因为它假设切片头的第一个字是指向后备数组的指针。
arr := []string{"one", "two", "three"}
address := unsafe.Pointer(&arr)
addPtr := (*[1]string)(unsafe.Pointer(*(*uintptr)(address)))
fmt.Println((*addPtr)[0]) // 输出 one
前面片段中的 uintptr 转换是不必要的。简化为:
arr := []string{"one", "two", "three"}
address := unsafe.Pointer(&arr)
addPtr := (*[]string)(address)
fmt.Println((*addPtr)[0]) // 输出 one
希望对你有所帮助。
英文:
Some background: A slice header contains a pointer to a backing array, length and capacity.
The code in the first part of the question converts a slice header to a pointer to a slice header. The go vet
command warns that the code in the question is possible misuse of unsafe.Pointer.
Fix by removing the extra dereference operation so that the code converts from pointer to slice header to pointer to slice header.
arr := []string{"one", "two", "three"}
address := unsafe.Pointer(&arr)
addPtr := (*[]string)(unsafe.Pointer((*uintptr)(address)))
fmt.Println((*addPtr)[0]) // prints one
The conversion to *uintptr is not required. Simplify to:
arr := []string{"one", "two", "three"}
address := unsafe.Pointer(&arr)
addPtr := (*[]string)(unsafe.Pointer(address))
fmt.Println((*addPtr)[0]) // prints one
The unsafe shenanigans are not required. Simplify to:
arr := []string{"one", "two", "three"}
addPtr := &arr
fmt.Println((*addPtr)[0]) // prints one
Convert the slice's backing array pointer to an array pointer using the following code. The code is fragile because it assumes that the first word of the slice header is the pointer to the backing array.
arr := []string{"one", "two", "three"}
address := unsafe.Pointer(&arr)
addPtr := (*[1]string)(unsafe.Pointer(*(*uintptr)(address)))
fmt.Println((*addPtr)[0]) // prints one
The uintptr conversions in the previous snippet are not needed. Simplify to:
arr := []string{"one", "two", "three"}
address := unsafe.Pointer(&arr)
addPtr := (*[]string)(address)
fmt.Println((*addPtr)[0]) // prints one
I hope this helps.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论