C Tutorial
C Bitwise Operators
&, |, ^, ~, and shifts work on bits. Use them for flags, masks, and compact storage.
Operators that work on bits
Arithmetic operators work on whole values. Bitwise operators work on each bit. & is AND: a bit is 1 only if both sides have 1. | is OR: a bit is 1 if either side has 1. ^is XOR: a bit is 1 if the sides differ. ~ flips every bit. << shifts left.>> shifts right.
These are not the logical operators && and ||. Logical AND and OR produce 0 or 1 from whole conditions. Bitwise AND and OR produce a new integer from the bits.
Small integers, printed in decimal
The programs in this chapter print results with %d. You can still think in bits:5 is 0101, 3 is 0011. AND those and you get 0001, which is 1. Left shift multiplies by two per step (when it does not overflow). Right shift divides by two per step for non-negative values.
Example
#include <stdio.h>
int main(void) {
int a = 5;
int b = 3;
printf("5 & 3 = %d\n", a & b);
printf("5 | 3 = %d\n", a | b);
printf("5 ^ 3 = %d\n", a ^ b);
printf("~5 = %d\n", ~a);
printf("3 << 2 = %d\n", b << 2);
printf("12 >> 2 = %d\n", 12 >> 2);
return 0;
}Typical output: 1, 7, 6, -6, 12,3. ~5 is -6 on two’s complement machines because every bit flips, including the sign bit. Do not treat ~ as “the other small positive number.”
Compile at /c/try. That is gcc in the browser. Changea and b to other small positives and match the decimal results to the bits.
What each operator does
| Operator | Name | Example |
|---|---|---|
& | AND | 5 & 3 is 1 |
| | OR | 5 | 3 is 7 |
^ | XOR | 5 ^ 3 is 6 |
~ | NOT | ~5 is -6 (typical) |
<< | Shift left | 3 << 2 is 12 |
>> | Shift right | 12 >> 2 is 3 |
Flags in one integer
Each flag is a different bit. OR turns a flag on. AND with the flag tests it. AND with the complement of the flag turns it off. XOR toggles it. This program uses 1, 2, and 4 so the bits do not overlap.
Example
#include <stdio.h>
int main(void) {
const int READ = 1;
const int WRITE = 2;
const int EXEC = 4;
int flags = READ | WRITE;
printf("flags: %d\n", flags);
if (flags & READ) {
printf("readable\n");
}
if (flags & EXEC) {
printf("executable\n");
}
flags |= EXEC;
printf("after adding EXEC: %d\n", flags);
flags &= ~WRITE;
printf("after clearing WRITE: %d\n", flags);
return 0;
}READ | WRITE is 3. After adding EXEC the value is 7. After clearingWRITE it is 5 (READ and EXEC). The if (flags & EXEC) branch does not print on the first test because that bit is still off.
Last chapter in this track
This is the last chapter of the C tutorial. You have gcc, types, pointers, structs, files, and the standard library. Return to the C dashboard to reopen any lesson, or keep compiling in/c/try. That editor is gcc.
Worked examples
The short listings above are there so you can see the grammar. The programs here use the same statements on quantities that already have units: a speed, a pH, a count of bases. They are classroom numbers. Air resistance is ignored. g is 9.81 m/s² unless a line says otherwise.
Open them in the C editor at /c/try. Change one measurement and check whether the result still has the right unit.
Engineering
Status bits on a sensor
Hardware packs on/off flags into one byte. Bit 0 over-temperature, bit 1 low battery. OR sets a flag. AND tests it. This is not the logical && you use in if (temp && pressure).
After setting OVERTEMP, that test prints 1 and the battery test prints 0. The other bits are unused in this sketch.
Example
#include <stdio.h>
int main(void) {
const int OVERTEMP = 1 << 0;
const int LOWBAT = 1 << 1;
int status = 0;
status = status | OVERTEMP;
printf("over-temp? %d\n", (status & OVERTEMP) != 0);
printf("low battery? %d\n", (status & LOWBAT) != 0);
return 0;
}Physics
Two 4-bit readings in one byte
Nine in the high nibble and four in the low nibble pack as (9 << 4) | 4 = 148. Shift right 4 to recover 9. Mask with 15 (binary 1111) to recover 4. Old instruments did this when bytes were expensive.
Example
#include <stdio.h>
int main(void) {
int high = 9;
int low = 4;
int packed = (high << 4) | low;
printf("packed = %d\n", packed);
printf("high nibble = %d\n", packed >> 4);
printf("low nibble = %d\n", packed & 15);
return 0;
}