C 中具有结构和字节顺序的位域提取
Bit field extract with struct and endianness in C
我需要阐明字节序如何影响 C 结构中的位提取字段。
以下结构声明了 rtp header:
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;
由于字节序影响内存中的字节顺序,我很难理解为什么在小端架构中以这种方式定义结构
谢谢
字节序也会影响位,而不仅仅是字节,但是您通常唯一会看到这种影响的情况是在位域中。这就是为什么结构中位域的排序以及它们所在的字节偏移量是实现定义的原因之一。
看看这个定义,它似乎暗示对于给定的实现,位域在大端系统上是按物理顺序放置的,而在小端系统上每个字节的顺序是相反的。
具体来说,前4个位域占8位,后2个位域占8位。所以在小端的情况下,前 4 个位域的顺序相互颠倒,最后 2 个位域相互颠倒。
这样的代码在系统 header 文件中很常见。例如,Linux 上的 /usr/include/netinet/in.h 包含以下结构来对 IP header 建模:
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
函数族来正确读取占用超过一个字节的整数字段。
除了字节序问题之外,您无法知道每个位域的位置。
根据 6.7.2 Type specifiers, paragraph 11 of the C Standard:
An implementation may allocate any addressable storage unit large enough to hold a bit- field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.
如果您需要确切知道位在数据中的位置,位域的使用完全不可移植,即使在同一平台上的不同编译器之间也是如此。
我需要阐明字节序如何影响 C 结构中的位提取字段。
以下结构声明了 rtp header:
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;
由于字节序影响内存中的字节顺序,我很难理解为什么在小端架构中以这种方式定义结构
谢谢
字节序也会影响位,而不仅仅是字节,但是您通常唯一会看到这种影响的情况是在位域中。这就是为什么结构中位域的排序以及它们所在的字节偏移量是实现定义的原因之一。
看看这个定义,它似乎暗示对于给定的实现,位域在大端系统上是按物理顺序放置的,而在小端系统上每个字节的顺序是相反的。
具体来说,前4个位域占8位,后2个位域占8位。所以在小端的情况下,前 4 个位域的顺序相互颠倒,最后 2 个位域相互颠倒。
这样的代码在系统 header 文件中很常见。例如,Linux 上的 /usr/include/netinet/in.h 包含以下结构来对 IP header 建模:
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
函数族来正确读取占用超过一个字节的整数字段。
除了字节序问题之外,您无法知道每个位域的位置。
根据 6.7.2 Type specifiers, paragraph 11 of the C Standard:
An implementation may allocate any addressable storage unit large enough to hold a bit- field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.
如果您需要确切知道位在数据中的位置,位域的使用完全不可移植,即使在同一平台上的不同编译器之间也是如此。