Numbers stored on a computer are stored in binary, but are usually input and
output in decimal format. The mathematical operations such as addition,
subtraction, multiplication and division are independent of what base a number
is stored in. *Bitwise* operators, however, only make sense when thinking
of number in binary.

The simplest bitwise operators are `&` and `|` which work
like the logical `&&` and `||` operators, except they work
bit by bit instead of treating their arguments as one value.

For instance `5 & 6` would be calculated as:

101 & 110 --- 100

And be equal to 4.

`5 | 6` would be calculated as:

101 | 110 --- 111

And be equal to 7.

Another bitwise operator is the `^` operator which
means "exclusive or" or "xor". This operator compares bits
and results in a 1 when exactly one of the two bits is set:

101 ^ 110 --- 011

So `5 ^ 6 = 3`.

These three operators are summarized in the following truth table:

A | B | A & B | A | B | A ^ B |
---|---|---|---|---|

0 | 0 | 0 | 0 | 0 |

0 | 1 | 0 | 1 | 1 |

1 | 0 | 0 | 1 | 1 |

1 | 1 | 1 | 1 | 0 |

The `!` operator is used for logical negation. The bitwise
operator for negation is `~`. The result of a bitwise negation
very much depends on the size of the values being negated. For example
~5 as a single byte:

~ 00000101 ---------- 11111010 = 250

However, if we are using 4 byte integers:

~ 00000000000000000000000000000101 ---------------------------------- 11111111111111111111111111111010 = 4294967290

Another thing we can do with bits is shift them left or right. For example, if we shift 5 left three spots:

101 << 3 ------ 101000 = 40

Shifting left by 3 is the same thing as multiplying by 8. In general, shifting left by $N$ is the same as multiplying by $2^N$.

We can also shift right using the `>>` operator:

10010 = 18 >> 2 ----- 100 = 4

When shifting right, the bits on the right are lost.
So the rightmost `10` are shifted off of the number.

Notice that shifting right by 2 is the same as dividing by 4 (and dropping any remainder. In general, shifting right by $N$ is the same as dividing by $2^N$.

This program tests all of the bitwise
operators. Notice it uses unsigned chars as the data type which can store
numbers from 0 - 255. The `%hhu` format specifier is for unsigned
chars:

```
#include <stdio.h>
int main() {
unsigned char a, b;
printf("Enter two numbers [0 - 255]: ");
scanf("%hhu %hhu", &a, &b);
printf("%hhu & %hhu = %hhu\n", a, b, a & b);
printf("%hhu | %hhu = %hhu\n", a, b, a | b);
printf("%hhu ^ %hhu = %hhu\n", a, b, a ^ b);
printf("%hhu << %hhu = %hhu\n", a, b, a << b);
printf("%hhu >> %hhu = %hhu\n", a, b, a >> b);
printf("~%hhu = %hhu\n", a, ~a);
printf("~%hhu = %hhu\n", b, ~b);
return 0;
}
```

One thing we will want to do with the bitwise operators is treat a large value as a several individual values packed together. For instance, we might view a 32-bit integer as eight 4-bit values, 32 one-bit values, or any other combination.

You can only *directly* read or write an entire byte, but with the bitwise
operators, we can read and write bits.

How could we write a function that tells us if a single bit in a value is set or not?

With such a function, we could write a program to print a number in binary:

```
/* return whether a given bit is 1 or not */
int test_bit(unsigned int value, int bit_number) {
int mask = 1 << bit_number;
if ((value & mask) == 0) {
return 0;
} else {
return 1;
}
}
```

Likewise, can we write a function to *set* a given bit in a number,
and make it 1?

```
/* sets a bit in a number to 1 */
void set_bit(unsigned int* value, int bit_number) {
int mask = 1 << bit_number;
*value = *value | mask;
}
```

How could we *clear* a bit which would set it to zero?

```
/* clear a bit in a number to 0 */
void clear_bit(unsigned int* value, int bit_number) {
int mask = ~(1 << bit_number);
*value = *value & mask;
}
```

Here we want to flip, or invert a single bit in a number. How can we do that?

```
/* invert a given bit in a number */
void flip_bit(unsigned int* value, int bit_number) {
int mask = 1 << bit_number;
*value = *value ^ mask;
}
```

These functions can be found in bits.c.

We have seen the printf and scanf functions for doing I/O in C. There are versions of these functions call fprintf and fscanf which work similarly except that they work with files instead of standard input and output.

To use them, we must create a `FILE*` object, which is a pointer
to a FILE structure.

To create a file, we use the `fopen` function which takes the name
of the file as the first parameter. The second parameter is the *mode*
string which is one of the following:

Mode | Meaning |

r | Open for reading. |

r+ | Open for reading and writing, file is not overwritten. |

w | Open for writing. |

w+ | Open for reading and writing, but file is overwritten if exists. |

a | Open for appending (writes at end of file). |

a+ | Open for reading and appending. |

To read a number from an input file, we could use:

```
#include <stdio.h>
int main() {
FILE* file = fopen("data.txt", "r");
int number;
fscanf(file, "%d", &number);
printf("Read %d\n", number);
fclose(file);
return 0;
}
```

C also has functions for reading and writing binary data.
The `fread` function takes the following parameters:

**void* ptr**This is a pointer into memory where the data should be read into. A

`void*`is a pointer that can point to any data type. fread can be used to read any data type (including structs).**int size**The size of each data element to be read. Because fread works for any type, we must tell it how big each element is.

**int num**The number of elements to read. If greater than 1, fread will read in multiple elements at once (as in a whole array).

**FILE* file**The file to read from (which must already be open).

fread returns the number of elements which were successfully read. You should check that this is equal to the number you attempted to read.

The following example shows how we could read in an array of 5 numbers stored in binary:

```
int array[5];
if (fread(array, sizeof(int), 5, file) != 5) {
/* there was an error */
}
```

The analogous function for output is `fwrite`, which takes
exactly the same parameters as fread, except that it writes the data
to a file instead of reading it.

An example which would write the binary values above would look like:

```
int array[5];
if (fwrite(array, sizeof(int), 5, file) != 5) {
/* there was an error */
}
```

This programs reads 5 ints from the
user and writes them to a file in binary. Notice that the file will not
be human-readable afterwards! You can use the `hexdump -C` command
to view the file.

Copyright © 2024 Ian Finlayson | Licensed under a Creative Commons BY-NC-SA 4.0 License.