英文:
Packed Structs in (gcc)go
问题
我有一些旧的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,这是一个紧凑/非对齐的结构。
我知道我可以只创建一个字节数组并手动解析,但这似乎非常脆弱和容易出错...
英文:
I have some old C code that makes somewhat heavy use of packed structures. I'm looking into using Go as a wrapper for this code, but am having difficulty finding a way to pass or even write definitions for these structures.
Example:
import "unsafe";
type AlignTest struct {
c byte;
y int16;
z int16;
q int32;
}
func main() {
vr := new(AlignTest);
fmt.Println(unsafe.Sizeof(*vr), "\n");
}
Returns 12 rather than the 1+2+2+4 = 9 that I would want with a packed/unaligned struct.
I know that I could just create a byte array and do the parsing manually, but that seems very brittle and error prone...
答案1
得分: 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)
}
.
输出:
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}
英文:
You could try something like this.
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}
答案2
得分: 5
你可能需要重新考虑你的架构-尝试将二进制输入传递到C层,并使用现有的结构(你不会破坏你不改变的东西)。我假设结构体的打包看起来像这样:
#ifdef WINDOWS
#pragma pack(push)
#endif
#pragma pack(BYTEALIGNMENT) // 例如 "#pragma pack(1)" 或 "#pragma pack(8)"
//--- 一些打包的结构体
#ifdef WINDOWS
#pragma pack(pop)
#endif
#ifdef POSIX
#pragma pack()
#endif
所有底层或第三方库所做的就是将一些void或const char进行类型转换为这些结构体。所以如果可能的话,尝试将这些数据转发到C层(在那里你可以获得指针),并且不要暴露这些结构体。
英文:
You may want to rethink your architecture- try passing the binary input down to the C layer and use the existing structures (you won't break what you don't change). I'm assuming the structure packing looks something like this:
#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
All the underlying or 3rd Party libs are then doing is taking some void* or const char* and typecasting it to these. So if possible, try forwarding that data into a C layer (where you can get pointers) and don't expose the structures at all.
答案3
得分: 2
没有办法告诉gccgo编译紧凑结构。我能想到的最好的解决方案是手动添加填充:
type AlignTest struct {
c byte
_ [3]byte // 匿名填充
y int16
z int16
q int32
}
英文:
There's no way to tell gccgo to compile packed structures. The best solution I can think of is to manually add padding:
type AlignTest struct {
c byte
_ [3]byte // anonymous padding
y int16
z int16
q int32
}
答案4
得分: 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
英文:
This works:
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")
}
Output:
12
9
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论