Go 如何理解uintptr和struct之间的关系?

Go 如何理解uintptr和struct之间的关系?,go,Go,我学习了如下代码 func str2bytes(s string) []byte { x := (*[2]uintptr)(unsafe.Pointer(&s)) h := [3]uintptr{x[0], x[1], x[1]} return *(*[]byte)(unsafe.Pointer(&h)) } 此功能用于将字符串更改为[]字节,而无需后台复制数据。 我尝试将num转换为reverseNum type Num struct { n

我学习了如下代码

func str2bytes(s string) []byte {
    x := (*[2]uintptr)(unsafe.Pointer(&s))
    h := [3]uintptr{x[0], x[1], x[1]}
    return *(*[]byte)(unsafe.Pointer(&h))
}
此功能用于将
字符串
更改为
[]字节
,而无需后台复制数据。
我尝试将num转换为reverseNum

type Num struct {
    name  int8
    value int8
}

type ReverseNum struct {
    value int8
    name  int8
}
func main() {
    n := Num{100, 10}
    z := (*[2]uintptr)(unsafe.Pointer(&n))
    h := [2]uintptr{z[1], z[0]}
    fmt.Println(*(*ReverseNum)(unsafe.Pointer(&h))) // print result is {0, 0}
}
这段代码没有得到我想要的结果。
有谁能告诉我那太复杂了。
简单的

主程序包
进口(
“fmt”
“不安全”
)
类型Num struct{
名称int8
值int8
}
类型反向枚举结构{
值int8
名称int8
}
func main(){
n:=Num{name:42,value:12}
p:=(*ReverseEnum)(不安全的指针(&n))
格式打印项次(p.值,p.名称)
}
产出“42、12”



但真正的问题是,你到底为什么要搞这样的把戏,而不是复制两个奇怪的字节,而这两个字节是在任何运行合理的CPU go程序上立即完成的

您的方法的另一个问题是,IIUC中的任何内容都不能保证具有相同字段的两个类型必须具有相同的内存布局。我认为他们应该在大多数实现中使用,但我不认为他们必须这样做

也可以考虑,在数据类型中也有额外的字段(即使是类型<代码>结构{ } /代码>!)之类的看似无害的东西,可能会对这些类型的变量的内存布局进行处理,因此假设你可以按照你想要的方式重新解释GO变量的内存,这可能是完全危险的。 。。。我只是想了解包装背后的原理

这是一个很好的例子

所有强类型但编译过的语言都有一个基本问题:运行编译程序的实际机器没有与编译器相同的类型系统。1也就是说,机器本身可能有一个线性地址空间,在这个空间中,字节被组装成机器字,并分组到页面中,依此类推。操作系统还可以提供页面粒度的访问:如果您需要更多内存,操作系统将为您提供一个页面—4096字节,8192字节,65536字节,或任何页面大小—每次都有额外内存

有很多方法可以解决这个问题。例如,可以使用硬件的指令集,直接用机器(或汇编)语言编写代码,与操作系统对话,以实现操作系统级的功能。然后,该代码可以与编译后的程序对话,充当中间人。如果编译后的程序需要分配一个40字节的数据结构,那么这个机器级代码可以在操作系统页面大小分配的限制范围内找出如何做到这一点

但是编写机器代码既困难又耗时。这正是我们首先拥有高级语言和编译器的原因。如果我们有办法,在高级语言中,违反语言强加的正常规则,会怎么样?通过以特定的方式违反特定的需求,将这些方式与所有其他也违反这些需求的代码仔细协调,我们可以在远离常规应用程序编程的代码中,用高级语言编写大部分内存管理、进程管理等

换句话说,我们可以使用
unsafe
(或其他语言中类似的东西)故意破坏我们的高级语言提供的类型安全性。当我们这样做时,当我们违反规则时,我们必须知道所有规则是什么,并且当我们与所有遵守正常规则的正常代码以及与所有违反规则的特殊、不安全代码相结合时,我们在这里的具体违规行为将正常运行

这通常需要编译器本身的帮助。如果检查随Go一起分发的
运行时
源代码,您将发现带有注释的例程,如
Go:noescape
Go:noinline
Go:nosplit
、和
Go:nowritebarrier
。如果要充分利用一些逃生舱编程,您需要知道何时以及为什么需要这些

一些更简单的用法,如访问字符串或切片头的技巧,是。。。它们仍然是不安全的,但是它们以更可预测的方式是不安全的,并且不需要与编译器本身进行这种密切的协调

要了解它们如何、何时以及为什么工作,您需要了解编译器和运行时如何分配和使用字符串和片,在某些情况下,还需要了解硬件上内存的布局,以及关于Go垃圾收集器的一些规则。特别是,GC代码知道
unsafe.Pointer
,但不知道
uintpttr
。这其中的大部分是相当棘手的:例如,请参见和的链接,其中向Go指针值写入
nil
会导致Go的垃圾收集器抱怨,因为写入导致GC检查以前存储的无效值



1有关设计硬件以运行编译的高级语言的显著尝试,请参阅。过去也有其他类似的项目,虽然有些IBM项目更为成功。

7。这简直不安全。@kostix,对不起,我是新来的。我只是想了解包装背后的原理。粘贴在问题中的代码让我感到困惑。我不会在实际项目中编写这样的代码。你能把剩下的6点贴在你的评论上吗?非常感谢,他们可以帮我很多!哦,那不是我,那是@Volker;我的评论是对他的双关语:-)当你是新手时:远离a)不安全b)反射和c)空界面。你不能从包不安全中学到任何你不能用更简单的方法学到的东西。(有人删除了1.到6。)“但真正的问题是,你到底为什么要搞这种诡计,而不是复制两个奇怪的字节,而这两个字节是在任何运行合理的CPU go程序上立即完成的?”这里没有必要攻击OP。这显然是一个人为的例子,他们想知道