从GO中的一个结构复制到另一个结构

时间:2018-02-03 23:50:18

标签: go

我有两个结构:

type struct1 struct{
     arr [num1]byte
}

type struct2 struct{
     b1 [n1]uint64
     b2 [n2]uint64
     b3 [n3]uint64
}

现在,我有两个指向这些结构的指针:

p1 := new(struct1);
p2 := new(struct2);

在一些计算之后,我想将p2复制到p1的指定部分。类似的东西:

copy(p1.arr[k:], p2);

其中k是正整数。它可以使用memcpy在C中轻松完成,但我无法在GO中找到一种简单的方法,无需使用任何外部库。有没有人有什么建议?我真的很感激。

2 个答案:

答案 0 :(得分:2)

你所说的问题在Go的类型方面没有意义。与C不同,Go强制执行其类型,并且不允许您访问底层字节(不使用不安全)。

对于您的问题,您需要重新设置它,以便按顺序将每个数组从p2复制到p1。为此,您需要单独复制每个:

k += copy(p1.arr[k:], p2.b1)
k += copy(p1.arr[k:], p2.b2)
k += copy(p1.arr[k:], p2.b3)

通过unsafe可以破坏类型安全并直接复制字节。问题是,无法保证它实际上会做你想要的。编译器可能在实际内存布局中将b2放在b1之前,并导致它执行您不想要的操作。这不太可能,但编译人员在追求性能方面做了很多奇怪的事情。 C人喜欢称那些东西"未定义的行为"。在Go中使用不安全实质上会触发UB,我不会推荐它。但是,为了完整起见,以下是Go中的memcopy方式:

p2Slice = (*(*[unsafe.Sizeof(*p2)]byte)(unsafe.Pointer(p2)))[:]
copy(p1.arr[k:], p2Slice)

如果那看起来很难看......那是故意的,因为你不应该这样做!但严肃地说,你可以把我在一行上做的事情分成多行,使其更具可读性。

const s = unsafe.Sizeof(*p2)
p2Arr = *(*[s]byte)(unsafe.Pointer(p2))
copy(p1.arr[k:], p2Arr[:])

答案 1 :(得分:1)

您想绕过Go类型系统,这是不安全的。因此,你应该非常小心。将其实现为一个函数,并使用Go测试包编写测试。检查错误。编写简单易读的代码。不要聪明。

例如,

package main

import (
    "fmt"
    "unsafe"
)

const num1 = 42

type struct1 struct {
    arr [num1]byte
}

const (
    n1 = 1
    n2 = 2
    n3 = 1
)

type struct2 struct {
    b1 [n1]uint64
    b2 [n2]uint64
    b3 [n3]uint64
}

func arrInsert(p1 *struct1, i1 int, p2 *struct2) int {
    if p1 == nil || p2 == nil {
        return 0
    }
    if i1 < 0 || i1 >= len(p1.arr) {
        return 0
    }
    s1 := p1.arr[i1:]
    s2 := (*[unsafe.Sizeof(*p2)]byte)(unsafe.Pointer(p2))[:]
    return copy(s1, s2)
}

func main() {
    p1 := new(struct1)
    p2 := new(struct2)
    for i := range p2.b1 {
        p2.b1[i] = uint64(i + 10)
    }
    for i := range p2.b2 {
        p2.b2[i] = uint64(i + 20)
    }
    for i := range p2.b3 {
        p2.b3[i] = uint64(i + 30)
    }
    n := arrInsert(p1, 1, p2)
    fmt.Println(n)
    fmt.Println(*p1)
    fmt.Println((*[unsafe.Sizeof(*p2)]byte)(unsafe.Pointer(p2))[:])
    fmt.Println(*p2)
}

游乐场:https://play.golang.org/p/KA0B0xpFR6l

输出:

32
{[0 10 0 0 0 0 0 0 0 20 0 0 0 0 0 0 0 21 0 0 0 0 0 0 0 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]}
[10 0 0 0 0 0 0 0 20 0 0 0 0 0 0 0 21 0 0 0 0 0 0 0 30 0 0 0 0 0 0 0]
{[10] [20 21] [30]}