1. Computing

Low Level Coding in C

Low level C programming

By

This is the tenth tutorial in the series Online C Tutorial for beginners and is about low level coding, though it was originally intended to be about memory management.

The first tutorial in the series is the Online C Tutorial. The previous tutorial was about functions.

This is a rewriting of the original C tutorials into single page bite size tutorials which can usually be run on either codepad.org or ideone.com.

Dealing With Bits and Bytes

In its role as a high level assembler used for writing operating systems, C is often used to access memory locations and change individual bits. You might for example need to access individual bits in an int.

It can sometimes be useful to conserve memory by using a byte to hold 8 flags though with an abundance of ram it's common to just use one byte per flag or even one per int.

Knowing how to extract or alter individual bits is still worthwhile knowing; you may never have to use it but when you have to maintain code that does then you'll know how to.

If you need to remind yourself about binary this article on What is Software may help.

An int is typically 32 bits. It's conventional to number them with the highest bit on the left (31) to right (0) for 32 bit CPUs and 63 to 0 for 64 bit CPUs.

33222222-22221111-11111100-00000000
10987654-32109876-54321098-76543210

The dash shows where the break between bytes is located. To access individual bits we need to work on whole bytes or ints and then isolate the bit we are interested in. This is where the binary operations, "and", "or" and "xor" are used. Don't confuse these with the same name logical operations. Logical operations use two characters, binary use one. This is logical and uses two &.

if (a && b )
  {
    c;
  }

Only if a is non zero and b is non zero does statement c get executed.

Below the variable c = the result of "binary anding" the two numbers a, b together. If a,b and c are all bytes then this "binary ands" all 8 bits of a with the corresponding bits of b to get c. Each bit of c is 1 if the corresponding bits of a and b are both 1. If either a or b is zero (or both) then the bit in c is also 0.

a - 100101102
b - 011111002
--------------------
a & b 000101002

Look at each pair of corresponding bits in a and b. The first one a is 1 and b is 0 so that gives c = 0 and so on.

If byte b is "binary anded" with a byte m where all the bits of m are 0 except for the bit in b that we are interested in, then we can isolate that bit. If the value of the expression is non zero then that bit is set, if it zero it isn't.

So this code works:

if (a & b)
    printf("a & b was non zero") ;
else
    printf("a & b was zero") ;

Warning - Trap Ahead!

You might think from this that there seems little difference between (a && b) and (a & b). Sometimes this is true, but not always. E.g. if a is 4 and b is 2, then (a && b) is true (read it as if a is non zero and b is not zero) but (a & b) is false! Here's why

a     000001002
b     000000102
-------------------
a & b 000000002

Setting Bits

Use the "binary or" operation for this which sets any bits to 1 in the result if either (or both) of the two numbers has a 1 in its corresponding bits.

a     100101102
b     011111002
--------------------
a | b 111111102

So to set the bit in a (below) at position 2 to a 1 we or with b = 000001002

Let's Toggle those bits!

The 3rd binary operation is "exclusive or" or xor which in C uses the ^ operator. If two numbers a and b are xored together then each bit in the result is 0 If the corresponding bits are identical, 1 if they are different. If it helps, think of xor as a "Not the same" function.

To toggle any bit, simply "binary xor" the number with a 1 set in the appropriate bit position. If the corresponding bit is 1, the result bit is 0 etc. To flip all the bits in a byte xor with 255.

a    100100102
b    000001002
--------------------
a ^ b 100101102

Example 1

This example prints out ints in binary. If you want the code listing, click to Download Example 1

// ex1.c
#include <stdio.h>

void PrintBinary(int x,int d)
{
    char buffer[33];
    int index;
    index=0;
    while (d >0) {
        if (x & 1)
            buffer[index++]='1';
        else
            buffer[index++]='0';
        x >>= 1;
        d--;
    }
  while (index >0 )
    printf("%c",buffer[--index]) ;

  printf("B\n") ;
  return;
}

int main(int argc, char* argv[])
{
    PrintBinary(10,6) ;
    PrintBinary(8,4) ;
    PrintBinary(32765,16) ;
    return 0;
}

You can see the output by running it on ideone.com.

This processes an int from right to left. It uses a "binary and" to get the rightmost bit, putting '1' for non zero and '0' in a buffer. This continues for as many digits as were specified in the second parameter to PrintBinary().

x >>= 1;

This uses the right shift operator >> to rotate x by one bit to the right. This is exactly the same as dividing by 2 and many compilers use shift operations to optimize integer division. You could have used:

x /= 2;

instead but I think >>= is more true to the meaning, and the compiler will probably generate right shift anyway.

The buffer is filled out in reverse. so PrintBinary(10,6) for example puts 010100 in the buffer then prints it out in reverse in the while loop. This makes it very easy to add leading zeros.

In the next tutorial, I'll look a little bit more at using xor in encryption.

©2014 About.com. All rights reserved.