1 回答
TA贡献1863条经验 获得超2个赞
那个设定
所以,基本上,你得到的映射是
int* buffer
→buffer uintptr
int argc
→unsafe.Pointer(&argsCount)
,其中&argsCount
是一个指向int
char* argv[]
→unsafe.Pointer(&args)
,哪里args
是[]string
const char* fileName
→unsafe.Pointer(syscall.StringToUTF16Ptr(fileName))
const char* key
,const char* prefix
,const char* version
— 同上。
存在的问题
现在这有什么问题呢。
uintptr
禁止在函数之间传递包含活动 Go 对象地址的值。(稍后会详细介绍。)argsCount
您将函数参数的地址传递给argc
。如果将其解释为计数,则典型的商品平台/操作系统(例如 amd64/Windows)上的地址是一个极其巨大的值。我敢打赌,当函数尝试从中读取这么多元素时,它会崩溃,导致
argv
它读取进程未映射的内存。这里有两个问题:
将切片值的地址作为参数传递,期望该切片的第一个元素的地址是错误的。
这是因为切片值(目前,在您应该使用的“参考”Go 实现中)是具有
struct
3 个字段的:底层数据数组的地址、该数组中允许使用的元素数以及总数该数组中此类元素的计数append
,在对切片值调用时,函数可以使用这些元素,而无需重新分配。当您拥有
args []string
并执行时&args
,您将获得该结构的地址,而不是该切片的第一个元素的地址。要执行后者,请使用
&args[0]
.在 Go 中,字符串(通常是这样,我们假设是这样)包含编码为 UTF-8 的字符。这通常不是 Windows 本机 C++ 代码在表示需要
char *
.我猜,你需要首先为 构建一个合适的东西
argv
,比如argv := make([]unsafe.Pointer, 0, len(args))for i, s := range args { argv[i] = unsafe.Pointer(syscall.StringToUTF16Ptr(s)) }
然后传递
&argv[0]
给被调用者。但请参阅下文
syscall.StringToUTF16Ptr()
。
您正在做的将字符串数据传递给具有该类型的其余参数的准备工作
const char*
似乎是正确的,但前提是被调用者确实意味着它char
是一个 16 位整数。换句话说,用于编译该库的源代码和工具链必须确保它
char
确实是wchar_t
或WCHAR
。如果是,那么你的做法应该没问题;否则就不是。你应该验证这一点。
uintptr
关于在表达式之间传递 s 的注意事项
Go 具有垃圾收集功能,因此它的运行时必须知道指向所有当前活动对象的所有指针。只要存在一个变量,其中包含指向在程序运行时分配的内存块的指针,该块就不会被垃圾收集。
值unsafe.Pointer
算作对内存块的正确引用,但uintptr
值则不然。这意味着当代码
p := new(someType) u := uintptr(unsafe.Pointer(&p)) foo(u)return
p
正在运行时,一旦分配了地址,GC 就可以自由地回收由 分配的对象p
,因为p
是对该对象的唯一引用,但u
事实并非如此。
现在考虑参考 Go 实现中的 GC 与程序代码同时运行。这意味着当foo
运行时,GC 也可能会执行,并且它可能会将您的实例someType
从foo
.
作为此一般规则的一个例外,Go 编译器保证uintptr(unsafe.Pointer)
同一语言表达式中发生的所有类型转换都不受 GC 的影响。所以以我们之前的例子为例,这样做就可以了
foo(uintptr(unsafe.Pointer(new(someType)))return
和
p := new(someType) v := unsafe.Pointer(&p) foo(uintptr(v))return
因为类型转换发生uintptr
在单个表达式中——这是一个函数调用。
因此,您不能将指向 Go 对象的指针作为uintptr
s传递,除非它们包含从“C 端”获取的指针。
- 1 回答
- 0 关注
- 145 浏览
添加回答
举报