GCC中位域的字节序问题

8

位域的字节序是由实现定义的。是否有一种方法可以在编译时通过某些宏或其他编译器标志来检查gcc的位域字节序是什么?

换句话说,给定以下内容:

struct X {
    uint32_t a : 8;
    uint32_t b : 24;
};

在编译时,有没有一种方法可以让我知道a是不是X的第一个或最后一个字节?


1
为什么不直接使用htonl将网络字节序转换为主机字节序,然后再使用ntohl将主机字节序转换为网络字节序呢? - immortal
2
https://gcc.gnu.org/onlinedocs/gcc/Structures-unions-enumerations-and-bit-fields-implementation.html 上说它是“由ABI决定的”。 - melpomene
2
@melpomene 非常好。我怎么知道 ABI 是如何确定的呢? - Barry
1
嗯,你可以在配置时编译和运行测试程序?当然,除非你是交叉编译。 - melpomene
讨论字中字节的字节序与OP的问题无关。他们想要检查位域是否从字节中最高或最低有效位开始分配。这个决定与底层硬件的小/大/中/其他字节序无关。 - kbro
显示剩余8条评论
2个回答

9
在Linux系统上,您可以检查__BYTE_ORDER宏,以查看它是__LITTLE_ENDIAN还是__BIG_ENDIAN。虽然这不是权威性的,但在实践中应该可以工作。
提示正确方法的定义在 netinet/ip.h 中的struct iphdr中,这是用于IP头的结构体。第一个字节包含两个4位字段,这些字段被实现为位域,因此顺序很重要。
struct iphdr
  {
#if __BYTE_ORDER == __LITTLE_ENDIAN
    unsigned int ihl:4;
    unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
    unsigned int version:4;
    unsigned int ihl:4;
#else
# error "Please fix <bits/endian.h>"
#endif
    u_int8_t tos;
    u_int16_t tot_len;
    u_int16_t id;
    u_int16_t frag_off;
    u_int8_t ttl;
    u_int8_t protocol;
    u_int16_t check;
    u_int32_t saddr;
    u_int32_t daddr;
    /*The options start here. */
  };

3
可能会有权威性。请参考GCC邮件列表中的这条评论:位域始终分配给第一个可用位,可能受其他因素(例如对齐)的限制。这意味着它们从小端的低位开始,从大端的高位开始。这是做事情的“正确”方式。编译器以不同的方式执行此操作非常不寻常。 - user9041001
这只对GCC具有权威性(好吧,这是OP的问题)。但要注意,尽管GCC的作者认为“编译器以不同的方式执行此操作非常不寻常”,但其他编译器编写者可以自由地按照自己的方式进行。我的Linux系统上的netinet/ip.h副本说:“此文件是GNU C库的一部分”,因此它与GCC紧密绑定。另一方面,您的头文件应该在假定使用__BYTE_ORDER来控制位域排序是安全的之前检查#ifdef __GNUC__#error也是一个逃避责任的行为 - 如果__BYTE_ORDER == __PDP_ENDIAN会怎样呢? - kbro

1

有趣的是,当位域是8位的倍数时,体系结构的字节序似乎并不重要。

请参见此处 [godbolt.org]

我在这个godbolt示例中选择了arm架构,因为它支持大端和小端,并且很容易比较差异。

请注意,无论体系结构是大端还是小端,在两种情况下,8位字段都位于结构的开头。

我测试了所有可以为is_8bit_tag_at_start函数生成可读汇编代码的godbolt编译器,它们似乎都返回true。


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