【问题标题】:Bit field extract with struct and endianness in CC 中具有结构和字节序的位字段提取
【发布时间】:2019-01-16 18:43:51
【问题描述】:

我需要澄清字节顺序如何影响 C 结构中的位提取字段。

以下结构声明了 rtp 标头:

typedef struct {
 #if BYTE_ORDER == BIG_ENDIAN
    unsigned int version:2; /* protocol version */
    unsigned int p:1;       /* padding flag */
    unsigned int x:1;       /* header extension flag */
    unsigned int cc:4;      /* CSRC count */
    unsigned int m:1;       /* marker bit */
    unsigned int pt:7;      /* payload type */
#else
   unsigned int cc:4;      /* CSRC count */
   unsigned int x:1;       /* header extension flag */
   unsigned int p:1;       /* padding flag */
   unsigned int version:2; /* protocol version */
   unsigned int pt:7;      /* payload type */
   unsigned int m:1;       /* marker bit */
#endif
  unsigned int seq:16;    /* sequence number */
  uint32_t ts;        /* timestamp */
  uint32_t ssrc;      /* synchronization source */
  uint32_t csrc[0];       /* optional CSRC list */
} rtp_hdr_t;

由于字节序会影响内存中的字节顺序,我几乎不明白为什么在小端架构中以这种方式定义结构

谢谢

【问题讨论】:

  • 您操作的 C++ 变量总是表现为大端。如果你对int 进行位操作,无论底层架构是大端还是小端(甚至是混合端),结果都是一样的。只有当您从程序外部的字节数组导出/导入数据时,您才需要关心。
  • 我认为这样的#ifdef 根本没有必要。该代码的作者可能错过了使用 htonx()/ntohx() 函数在 c / c++ 代码中以透明的方式处理字节序。
  • 这个问题是关于 C++,而不是 C。请去掉'c'标签
  • @user3629249 这个问题是如何只针对 c++ 的?
  • 问题的名称/标题。问题的第一句话。

标签: c endianness


【解决方案1】:

字节序也可以影响位,而不仅仅是字节,但是您通常看到效果的唯一时间是在位域中。这就是为什么结构中位域的顺序以及它们所在的字节偏移量是实现定义的原因之一。

看看这个定义,似乎暗示对于给定的实现,位域在大端系统上按物理顺序放置,而在小端系统上每个字节的顺序相反。

特别是,前 4 个位域占用 8 位,接下来的 2 个位域占用 8 位。所以在小端的情况下,前 4 个位域的顺序相互颠倒,最后 2 个位域的顺序相互颠倒。

这样的代码在系统头文件中很常见。例如,Linux 上的 /usr/include/netinet/in.h 包含以下结构来模拟 IP 标头:

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. */
  };

大概的想法是,包含原始网络数据包的缓冲区可以使用memcpy 将字节复制到此结构的实例中(或者如果对齐正确,则只需指向此结构的指针指向缓冲区)简化序列化/反序列化。但是,您仍然需要调用 htonx/ntohx 系列函数才能正确读取占用超过一个字节的整数字段。

【讨论】:

  • 我没有看到这样做的好处。已经有(POSIX)函数可用,可以正确处理网络字节顺序。
  • @πάνταῥεῖ 这是处理位顺序,而不是字节顺序。我不确定 OP 的代码是否来自系统头文件,但 /usr/include/netinet/ip.h 中struct iphdr 的定义是一样的。
  • 是的,我认为唯一合理的解释是原始编码器希望确保无论字节序如何,位的顺序都是相同的。
【解决方案2】:

除了字节序问题,您无法知道每个位域的位置。

6.7.2 Type specifiers, paragraph 11 of the C Standard:

实现可以分配任何大到足以容纳位域的可寻址存储单元。如果有足够的空间剩余,紧跟在结构中另一个位域之后的位域将被打包到同一单元的相邻位中。如果剩余空间不足,是否将不适合的位域放入下一个单元或与相邻单元重叠是实现定义的。单元内位域的分配顺序(高位到低位或低位到高位)是实现定义的。可寻址存储单元的对齐方式未指定

如果您需要准确了解某个位在数据中的位置,则位域的使用完全是不可移植的,即使在同一平台上的不同编译器之间也是如此。

【讨论】:

    猜你喜欢
    • 2014-01-26
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    • 2012-10-16
    • 1970-01-01
    • 1970-01-01
    • 2017-08-21
    • 1970-01-01
    相关资源
    最近更新 更多