【问题标题】:Copy from one struct to another struct in GO在 GO 中从一个结构复制到另一个结构
【发布时间】:2018-02-04 22:06:43
【问题描述】:

我有两个结构:

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 中不使用任何外部库的简单方法。有没有人有什么建议?我会很感激的。

【问题讨论】:

  • copy(p1.arr[k:], p2); 你想将一个结构复制到一个切片中?
  • @Sridhar 我想将整个 p2.b1、p2.b2 和 p2.b3 复制到一个切片中

标签: go


【解决方案1】:

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

对于您的问题,您需要将其重述为要按顺序将每个数组从 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 中使用 unsafe 本质上会触发 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[:])

【讨论】:

  • 感谢斯蒂芬的回答!我花了一段时间才明白你在那一行中做了什么!但是,如果我想安全地实现它并将每个数组从 p2 复制到 p1,我可以使用 copy 来实现吗?因为 p2 和 p1 中的数组有不同的类型(byte 和 uint64)。
【解决方案2】:

您想绕过不安全的 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]}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-24
    • 1970-01-01
    • 2014-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多