People working with cross platform low level code are usually aware of the issues related to the little and big endian data presentation methods which dictate how bytes are ordered in memory. This helps them avoid pitfalls related to cross platform data transfer such as when transferring data over the network or when moving files from a little endian platform to a big endian platform and vice versa.
But what about bit field ordering, is there any standard ordering of C/C++ bit-fields and what effect can this have on your code?
As it turns out there is no ANSI bit ordering standard and bit ordering is compiler dependant.
To demonstrate the problems that might arise from this lack of standardization, consider the following C++ code that describes an IP packet header:
struct IPHeader
{
unsigned int version:4; // version of IP
unsigned int headerLen:4; // length of IP header
unsigned char tos; // type of service
unsigned short totalLen; // total length of the packet
unsigned short id; // unique identifier
unsigned short flags; // flags
unsigned char ttl; // time to live
unsigned char protocol; // protocol type (TCP, UDP etc)
unsigned short checksum; // IP checksum
unsigned int sourceIP; // source IP address
unsigned int destIP; // destination IP address
};
The IP header length and version fields are both 4 bit, bit-fields.
Logic might lead you to assume that the header length will be placed after the version in memory like the other IPHeader data members but in fact this is compiler dependant.
For example Visual C++ documentation says the following about Microsoft's implementation of C++ bit fields:
"The ordering of data declared as bit fields is from low to high bit..."
The result of this would be that length will be placed before the version field and this will produce an invalid IP header.
This means that for the IP header to be generated correctly on Visual C++ you will need to reverse the order of the IP header length and version fields:
struct IPHeader
{
unsigned int headerLen:4; // length of IP header
unsigned int version:4; // version of IP
unsigned char tos; // type of service
unsigned short totalLen; // total length of the packet
unsigned short id; // unique identifier
unsigned short flags; // flags
unsigned char ttl; // time to live
unsigned char protocol; // protocol type (TCP, UDP etc)
unsigned short checksum; // IP checksum
unsigned int sourceIP; // source IP address
unsigned int destIP; // destination IP address
};
To avoid those types of pit falls when working with bit fields, make sure you know and control the type of bit ordering your compiler and data structures employ.
Some compilers might let you change the default bit ordering but be sure to consider all the possible interactions with other parts of the code.