在(gcc)go中的紧凑结构体

8

我有一些旧的C代码,其中使用了大量的紧凑结构。我正在研究使用Go作为该代码的包装器,但是很难找到一种传递或甚至编写这些结构定义的方法。

例如:

import "unsafe";

type AlignTest struct {
    c byte;
    y int16;
    z int16;
    q int32;
}

func main() {

    vr := new(AlignTest);

    fmt.Println(unsafe.Sizeof(*vr),  "\n");

}

使用紧凑/未对齐的结构,返回12而不是我想要的1+2+2+4=9。

我知道我可以创建一个字节数组并手动解析,但这似乎非常脆弱和容易出错...


你不能自己填充结构体吗?将“c”变成一个4字节的数组?对于这个特定的结构体,这样做就可以了。 - Vinicius Kamakura
你能把 c 移到后面吗?这样你可能会得到 9 个连续的数据字节... - Kerrek SB
不幸的是,在给定的示例中重新排列结构可能是可行的,但我提出问题的原因涉及由外部要求指定的结构,不能重新排列。当然,我可以进行大量复制以将数据放入未打包的结构中,但这似乎是浪费的。 - Guy B
4个回答

6
你可以尝试像这样做。
package main

import (
    "encoding/binary"
    "bytes"
    "fmt"
)

type Unpacked struct {
    C byte
    Y int16
    Z int16
    Q int32
}

type Packed struct {
    B [9]byte
}

func main() {
    var u Unpacked
    var p Packed
    var buf = bytes.NewBuffer(make([]byte, 0, len(p.B)))
    // Unpacked to Packed 
    u = Unpacked{1, 2, 3, 4}
    if err := binary.Write(buf, binary.BigEndian, &u); err != nil {
        fmt.Println(err)
    }
    if err := binary.Read(buf, binary.BigEndian, &p); err != nil {
        fmt.Println(err)
    }
    fmt.Println("u", u, "to", "p", p)
    // Packed to Unpacked
    p = Packed{[...]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}}
    if err := binary.Write(buf, binary.BigEndian, &p); err != nil {
        fmt.Println(err)
    }
    if err := binary.Read(buf, binary.BigEndian, &u); err != nil {
        fmt.Println(err)
    }
    fmt.Println("p", p, "to", "u", u)
}

.

Output:
u {1 2 3 4} to p {[1 0 2 0 3 0 0 0 4]}
p {[1 2 3 4 5 6 7 8 9]} to u {1 515 1029 101124105}

5

您可能需要重新考虑您的架构- 尝试将二进制输入传递到C层并使用现有的结构(您不会破坏未更改的内容)。我假设结构打包看起来像这样:

#ifdef WINDOWS
#pragma pack(push)
#endif
#pragma pack(BYTEALIGNMENT) // e.g. "#pragma pack(1)" or "#pragma pack(8)"

//--- Some packed structs

#ifdef WINDOWS
#pragma pack(pop)
#endif
#ifdef POSIX
#pragma pack()
#endif

所有底层或第三方库所做的就是将一些void*或const char*进行类型转换。因此,如果可能的话,请将这些数据转发到C层(其中您可以获取指针),不要完全暴露结构。


2

无法告诉gccgo编译紧密结构体的方法。我能想到的最好解决方案是手动添加填充:

type AlignTest struct {
    c byte
    _ [3]byte // anonymous padding
    y int16
    z int16
    q int32
}

1
这个可以工作:

package main

import "unsafe"
import "fmt"

/*
struct test {
    char c;
    short i;
    short j;
    int k;
} __attribute__((packed));
*/
import "C"

type AlignTest struct {
    c byte
    y int16
    z int16
    q int32
}

func main() {

    vr := new(AlignTest)
    v := new(C.struct_test)

    fmt.Println(unsafe.Sizeof(*vr), "\n")
    fmt.Println(unsafe.Sizeof(*v), "\n")

}

输出:

12

9

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接