目的
我正在用 C(特别是 gnu89
)编写一个网络程序,我想通过将某个 struct X
重新解释为大字节数组(又名 char
),通过网络发送字节,并在另一端将它们重新解释为 struct X
。为此,我决定使用 gcc 的 __attribute__((__packed__ ))。我已尽最大努力确保正确完成此操作(即我已考虑字节顺序和其他相关问题)。
问题
除了保证 struct X
尽可能小之外,gcc 是否保证用 __attribute__((__packed__ )) 定义的 struct
保留原始顺序?我进行了大量搜索,但尚未找到任何关于此保证是否存在的文档。
注意事项
可以安全地假设发送方和接收方都不会遇到可移植性问题(例如,服务器上的 sizeof(int)
等于服务器上的 sizeof(int)
客户)。
最佳答案
是的,__attribute__((packed))
(不需要第二组下划线)是实现二进制(即非文本)网络协议(protocol)的正确方法。元素之间不会有间隙。
但是你应该明白,packed
不仅打包结构,而且:
- 使其所需的对齐为一个字节,并且
- 确保其成员(可能因打包和结构本身缺乏对齐要求而错位)被正确读取和写入,即其字段的错位由编译器在软件中处理.
但是,如果您直接访问结构成员,编译器只会处理错位。您应该永远不要将指针指向压缩结构的成员(除非您知道该成员所需的对齐方式为 1,例如 char 或其他压缩结构)。以下 C 代码演示了该问题:
#include <stdio.h>
#include <inttypes.h>
#include <arpa/inet.h>
struct packet {
uint8_t x;
uint32_t y;
} __attribute__((packed));
int main ()
{
uint8_t bytes[5] = {1, 0, 0, 0, 2};
struct packet *p = (struct packet *)bytes;
// compiler handles misalignment because it knows that
// "struct packet" is packed
printf("y=%"PRIX32", ", ntohl(p->y));
// compiler does not handle misalignment - py does not inherit
// the packed attribute
uint32_t *py = &p->y;
printf("*py=%"PRIX32"\n", ntohl(*py));
return 0;
}
在 x86 系统(不强制内存访问对齐)上,这将产生
y=2, *py=2
如预期。另一方面,例如,在我的 ARM Linux 板上,它产生了看似错误的结果
y=2, *py=1
关于c - GCC 的 __attribute__((__packed__)) 是否保留原始顺序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1756811/