Golang:不安全的动态字节数组

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

Golang: unsafe dynamic byte array

问题

我正在尝试使用Go语言与Windows的dll进行接口交互。我想要使用的dll函数接受一个指向字节数组的指针,因此我需要提供这个字节数组。

我正在使用syscall库来调用dll,可以在这里看到示例。我的基本要求是:

  • 我已经知道了字节数组的所需大小
  • 我创建了字节数组
  • 我需要获取字节数组的指针
  • 然后将指针传递给Windows的dll

我无法弄清楚如何在Go语言中创建字节数组并获取其指针。这显然是一项不安全的操作,unsafe库可能会有所帮助,但首先我需要创建一个动态长度的字节数组。使用"make"创建切片对我没有帮助,除非我能够获取切片的底层数组的指针。

是否有其他人遇到过这个问题或者有任何想法?

英文:

I am trying to interface with a Windows dll using Go. The dll function I want to use accepts a pointer to a byte array. Therefore I need to give it that byte array.

I am using the syscall libary to call the dll, as demonstrated here. My basic requirements are:

  • I am given the required size for the byte array
  • I create the byte array
  • I must get a pointer to the byte array
  • I then pass the pointer to the Windows dll

I can't figure out how to create a byte array in go, and get a pointer to it. This is obviously an unsafe operation, and the unsafe library can be helpful, but I need to create a dynamic-length byte array in the first place. Creating a slice with "make" doesn't help me, unless I can get a pointer to the slice's backing array.

Has anyone else encountered this or have any ideas?

答案1

得分: 3

我找到了一个粗略的解决方案。显然,一个切片的结构包含一个指向后备字节数组的指针,后备字节数组的长度,以及后备字节数组的容量。

我只对字节数组的指针感兴趣,所以我只需要切片内部数据的第一个成员。

Go的unsafe.Pointer无法将切片转换为不安全指针,但它可以将指针转换为切片作为不安全指针。由于我可以将不安全指针转换为任何类型的指针,我可以将其转换为指向指针的指针,从而恢复切片内部数据的第一个成员。

这是一个可行的示例。我想要一个uintptr,但你可以将其转换为任何指针类型。

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	// 任意大小
	n := 4

	// 创建一个正确大小的切片
	m := make([]int, n)

	// 使用复杂的间接方式将切片的前几个字节转换为不安全的uintptr
	mPtr := *(*uintptr)(unsafe.Pointer(&m))

	// 检查是否成功
	m[0] = 987
	// (我们必须将uintptr重新转换为*int才能检查它)
	fmt.Println(m[0], *(*int)(unsafe.Pointer(mPtr)))
}

如果你想要一个*int,你可以这样做:mPtr := *(**int)(unsafe.Pointer(&m))

只要切片保持这种内部数据结构,这个方法就有效。我当然愿意接受一个更健壮的解决方案,不依赖于Go内部的结构。

英文:

Well I found one gross solution. Apparently the structure of a slice contains a pointer to the backing byte array, the length of the backing byte array, and then the capacity of the backing byte array.

I am only interested in a pointer to the byte array, so I only need the first member of the slice's internal data.

Go's unsafe.Pointer will not cast a slice to an unsafe pointer, but it will cast a pointer to a slice as an unsafe pointer. Since I can cast an unsafe pointer to any old type of pointer I want, I can cast it to a pointer-to-a-pointer, which recovers the first member of the slice's internal data.

Here's a working example. I wanted a uintptr but you could cast it to any pointer type.

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	// Arbitrary size
	n := 4

	// Create a slice of the correct size
	m := make([]int, n)

	// Use convoluted indirection to cast the first few bytes of the slice
	// to an unsafe uintptr
	mPtr := *(*uintptr)(unsafe.Pointer(&m))

	// Check it worked
	m[0] = 987
	// (we have to recast the uintptr to a *int to examine it)
	fmt.Println(m[0], *(*int)(unsafe.Pointer(mPtr)))
}

If you wanted a *int instead, you could do mPtr := *(**int)(unsafe.Pointer(&m))

This works as long as a slice maintains this internal data structure. I am definitely open to a more robust solution that doesn't depend on the structure of Go's internals.

答案2

得分: 3

我认为syscall.ComputerName的实现https://golang.org/src/syscall/syscall_windows.go#395是一个很好的例子。它使用uint16而不是字节,但其他方面...

在你的情况下,应该是ptr := &myslice[0]

Alex

英文:

I think syscall.ComputerName implementation https://golang.org/src/syscall/syscall_windows.go#395 would be a good example. It uses uint16s, not bytes, but otherwise ...

In your case it would be ptr := &myslice[0].

Alex

huangapple
  • 本文由 发表于 2013年11月1日 12:53:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/19721008.html
匿名

发表评论

匿名网友

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

确定