Bit manipulations good practices

  • A+

As a beginner C programmer, I am wondering, what would be the best easy-to-read and easy-to-understand solution for setting control bits in a device. Are there any standards? Any example code to mimic? Google didn't give any reliable answer.

For example, I have a control block map: Bit manipulations good practices

The first way I see would be to simply set the needed bits. It requires a bunch of explanations in comments and seems to be not all that professional.

DMA_base_ptr[DMA_CONTROL_OFFS] = 0b10001100; 

The second way I see is to create a bit field. I'm not sure if this is the one should I stick to, since I never encountered it being used in such way (unlike the first option I mentioned).

struct DMA_control_block_struct {      unsigned int BYTE:1;      unsigned int HW:1;      // etc } DMA_control_block_struct; 

Is one of the options better than the other one? Are there any options I just don't see?

Any advice would be highly appreciated


The problem with bit fields is that the C standard does not dictate that the order in which they are defined is the same as the order that they are implemented. So you may not be setting the bits you think you are.

Section of the C standard states:

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.

As an example, look at the definition of struct iphdr, which represents an IP header, from the /usr/include/netinet/ip.h file file on Linux:

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;     ... 

You can see here that the bitfields are placed in a different order depending on the implementation. You also shouldn't use this specific check because this behavior is system dependent. It is acceptable for this file because it is part of the system. Other systems may implement this in different ways.

So don't use a bitfield.

The best way to do this is to set the required bits. However, it would make sense to define named constants for each bit and to perform a bitwise OR of the constants you want to set. For example:

const uint8_t BIT_BYTE =     0x1; const uint8_t BIT_HW   =     0x2; const uint8_t BIT_WORD =     0x4; const uint8_t BIT_GO   =     0x8; const uint8_t BIT_I_EN =     0x10; const uint8_t BIT_REEN =     0x20; const uint8_t BIT_WEEN =     0x40; const uint8_t BIT_LEEN =     0x80;  DMA_base_ptr[DMA_CONTROL_OFFS] = BIT_LEEN | BIT_GO | BIT_WORD; 


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: