Fundamental C - Basic Bits
Written by Harry Fairhead   
Tuesday, 23 March 2021
Article Index
Fundamental C - Basic Bits
Masks

This extract, from my book on programming C in an IoT context explains the C basics of bit manipulation. It is core to working with C so much so that you can almost claim that if you aren't doing bit manipulation you probably are using the wrong language...

Fundamental C: Getting Closer To The Machine

Now available as a paperback and ebook from Amazon.

  1. About C
      Extract Dependent v Independent
                  & Undefined Behavio
  2. Getting Started With C Using NetBeans
  3. Control Structures and Data
  4. Variables
      Extract Variables
  5. Arithmetic  and Representation
      Extract Arithmetic and Representation
  6. Operators and Expression
      Extract: Expressions
      Extract Side Effects, Sequence Points And Lazy Evaluation
      First Draft of Chapter: Low Down Data
  7. Functions Scope and Lifetime
  8. Arrays
      Extract  Simple Arrays
      Extract  Ennumerations
  9. Strings
      Extract  Simple Strings
     
    Extract: String I/O ***NEW!!
  10. Pointers
      Extract  Starting Pointers
      Extract  Pointers, Cast & Type Punning
  11. Structs
      Extract Basic Structs
      Extract Typedef
  12. Bit Manipulation
      Extract Basic Bits
      Extract Shifts And Rotates 
  13. Files
     Extract Files
     
    Extract Random Access Files 
  14. Compiling C – Preprocessor, Compiler, Linker
     Extract Compilation & Preprocessor

Also see the companion volume: Applying C

<ASIN:1871962609>

<ASIN:1871962463>

<ASIN:1871962617>

<ASIN:1871962455>

 

Cbookcover

 

Bit manipulation is almost dead in high-level languages and it isn’t as common in C as it once was. If you are writing low-level programs that interact in any way with the hardware, however, then bit manipulation will still be an essential part of what you do and, to get things right and to make sure you are doing things in sensible ways, you need to master the technique. The good news is that it isn’t difficult once you start to think about the contents of memory as a bit pattern that has many interpretations.

The Bitwise Operators

C has a number of operators designed to allow you to perform bit manipulation. There are four bitwise operators:

AND

&

OR

|

XOR (exclusive or)

^

NOT

~

 

As you would expect the NOT operator has the highest priority.

Notice that there are also corresponding Boolean operators &&, || and ! which only work with Boolean values – with zero as false and anything non-zero as true - and not with bit patterns.

The bitwise operators work with integer types. For example:

int a = 0xF0;
int b = 0xFF;
int c = ~a & b;
printf("%X\n",c)

This first works out the bitwise NOT of a, i.e. 0F. This is then bitwise ANDed with b, i.e. 0F & FF which is F. The %X format specifier prints the value in hex. You can use %x for lower case and %d for decimal.

Signed v Unsigned

Now we come to a subtle and troublesome point. Bitwise operators are only uniquely defined for unsigned values. The reason is that unsigned values have an unambiguous representation in binary and hence the operations are well-defined in terms of the values the bit patterns represent.

That is:

unsigned int value=5;

is always 0101 and hence:

value | 0x2

is not only always 0111, but also always represents +7.

The same is not true for signed values simply because the way in which negative numbers are represented isn’t fixed. The most common representation is two’s complement, but this is not part of the standard and so logical operations on unsigned numbers are implementation-dependent. Notice that this does not mean logical operations on signed values are undefined behavior – if this was the case most C programs would stop working properly. In addition “implementation-dependent” has one fairly consistent meaning:

take the bit pattern that represents the value and perform the specified logical operation as if everything involved was an unsigned value.

This is the only sane way to deal with the problem and it is exactly what you would expect. So, where the bit pattern isn’t defined, as in:

signed int value=-5;

if the machine uses two's complement representation, see Chapter 5, then the bit pattern is:

11111111111111111111111111111011‬

and

value | 0x4

changes the third bit to 1 i.e. -1 in two’s complement.

As long as you assume a representation for the signed value then the logical operation is usually well defined as the bitwise application of the operator on the bit patterns. It is the representation that is implementation-dependent and not the operation.

Masks

So what do you use the bitwise logical operators for?

In many cases you have the problem of setting or clearing particular bits in a value. The value is usually stored in a variable that is usually regarded as a status variable or flag.

You can set and unset bits in a flag using another value, usually called a mask, that defines the bits to be changed.

For example if you only want to change the first (least significant) bit then the mask would be 0x01.

If you wanted to change the first and second bits the mask would be 0x03 and so on.

If you find working out the correct hexadecimal value needed for any particular mask difficult then you could use the strtol function with a radix of two. For example:

char *prt;
int a = strtol("01",&prt,2);

sets a to 0x01 and:

int a = strtol("11",&prt,2);

sets a to 0x03 and so on.

To create a mask just write down a string of zeros and ones with ones in the positions you want to change and use strtol to convert to a mask value.

Now that you have a mask what do you do with it?

Suppose the variable mask contains a value that in binary has a one at each bit location you want to change. Then:

flag | mask;

returns a bit pattern with the same bits set to one as in the mask. Notice that the bits that the mask doesn't specify, i.e. are zero in the mask, are left at their original values.

For example:

char *prt;
int mask = strtol("11",&prt,2);
int flag = 0xFFF0;
int result = flag | mask;
printf("%X\n",result);

sets result to 0xFFF3, i.e. it sets the first (least significant) two bits.

If you use:

flag & ~mask;

then the bits specified in the mask are set to zero - or are unset if you prefer. Notice that you have to apply a NOT operator to the mask.

For example:

char *prt;
int mask = strtol("11",&prt,2);
int flag = 0xFFFF;
int result = flag & ~mask;
printf("%X\n",result);

sets result to 0xFFFC, i.e. it unsets the first two bits.

As well as setting and unsetting particular bits, you might also want to "flip" the specified bits, i.e. negate them so that if the bit was a one it is changed to a zero and vice versa. You can do this using the exclusive or (XOR) operator:

flag ^ mask

flips the bits specified by the mask.

For example:

char *prt;
int mask = strtol("11",&prt,2);
int flag = 0xFFFF;
int result = flag ^ mask;
printf("%X\n",result);

sets result to 0xFFFC because it changes the lower two bits from ones to zeros. Again bits not specified by mask are unaffected.



Last Updated ( Tuesday, 23 March 2021 )