Lecture 6: Bit Processing
Prerequisites: Functions; integer data types and arithmetic operators.
1. Overview
- Computers store and process all data as sequences of binary digits (bits); understanding how to read, manipulate, and reason about individual bits is fundamental to systems programming, embedded development, and performance-critical code.
- After this chapter you will be able to convert between numeral systems, apply the six C bitwise operators (
&,|,^,~,<<,>>), construct bit masks to set, clear, toggle, and test individual bits, and recognise common bit-level idioms used in hardware programming.
2. Learning Outcomes
After completing this chapter, students will be able to:
- Explain the positional-value principle and convert numbers fluently between binary, octal, decimal, and hexadecimal representations.
- Describe how non-negative and negative integers are stored in memory using two's complement representation, and perform two's complement conversion by hand.
- Distinguish between the six C bitwise operators and predict their results on given operands.
- State the precedence and associativity rules for bitwise operators and use parentheses to avoid precedence-related bugs.
- Use left shift and right shift for efficient multiplication and division by powers of two, and explain the difference between logical and arithmetic right shifts.
- Construct bit masks to set, clear, toggle, and test individual bits or ranges of bits within an integer.
- Apply bit-manipulation idioms to practical scenarios such as hardware port control, packet validation, and power-of-two detection.
3. Key Terms
| Term (EN) | 中文 | Notes |
|---|---|---|
| Bit | 比特(位) | The smallest unit of data: 0 or 1. |
| Byte | 字节 | 8 bits. |
| Most Significant Bit (MSB) | 最高有效位 | The leftmost bit; in a signed integer it is the sign bit. |
| Least Significant Bit (LSB) | 最低有效位 | The rightmost bit; determines whether a number is odd or even. |
| Numeral system (radix/base) | 进制(基数) | A writing system for numbers; common bases are 2, 8, 10, 16. |
| Binary (Base-2) | 二进制 | Digits 0–1. C prefix: 0b (GCC/Clang extension) or no standard prefix. |
| Octal (Base-8) | 八进制 | Digits 0–7. C prefix: 0 (leading zero). |
| Hexadecimal (Base-16) | 十六进制 | Digits 0–9, A–F. C prefix: 0x or 0X. |
| Two's complement | 补码 | The standard signed-integer representation: invert all bits and add 1. |
Bitwise AND (&) | 按位与 | Result bit is 1 only when both operand bits are 1. |
Bitwise OR (|) | 按位或 | Result bit is 1 when at least one operand bit is 1. |
Bitwise XOR (^) | 按位异或 | Result bit is 1 when the operand bits differ. Easily confused with exponentiation (which C does not have). |
Bitwise NOT (~) | 按位取反 | Flips every bit; unary operator. |
Left shift (<<) | 左移 | Shifts bits leftward, filling with 0 on the right. Equivalent to multiplication by 2^n. |
Right shift (>>) | 右移 | Shifts bits rightward. For unsigned types, fills with 0 (logical shift); for signed types, typically fills with the sign bit (arithmetic shift — implementation-defined). |
| Bit mask | 位掩码 | A value used with a bitwise operator to select, set, clear, or toggle specific bits. |
| Set a bit | 置位 | Force a bit to 1 using OR with a mask. |
| Clear a bit | 清位 | Force a bit to 0 using AND with the complement of a mask. |
| Toggle a bit | 翻转位 | Flip a bit using XOR with a mask. |
4. Core Concepts
4.1 Numeral Systems
A numeral system is a positional notation in which each digit's contribution to the overall value is determined by its position and the base (also called radix) of the system. The general formula for a number with digits in base is:
Four bases are of particular importance in computing:
| Base | Name | Digit set | Typical use |
|---|---|---|---|
| 2 | Binary | 0, 1 | Direct hardware representation |
| 8 | Octal | 0–7 | Legacy Unix file permissions; compact binary groups of 3 bits |
| 10 | Decimal | 0–9 | Everyday human arithmetic |
| 16 | Hexadecimal | 0–9, A–F | Compact binary groups of 4 bits; memory addresses, colour codes |
Worked conversions:
A handy shortcut: each hexadecimal digit maps to exactly four binary digits (a nibble), and each octal digit maps to exactly three binary digits. This is why hexadecimal is the preferred compact notation for binary data.
Minimal example
File: p06_a.c
Build: gcc p06_a.c -o p06_a
Run: ./p06_a
/* p06_a.c – Printing an integer in decimal, octal, and hexadecimal */
#include <stdio.h>
int main(void) {
int dec = 26; /* decimal literal */
int oct = 032; /* octal literal (= 26 decimal) */
int hex = 0x1A; /* hex literal (= 26 decimal) */
printf("DEC: %d\n", dec);
printf("OCT: %o (DEC: %d)\n", oct, oct);
printf("HEX: %x (DEC: %d)\n", hex, hex);
return 0;
}
What you should see: All three variables hold the same value (26); the format specifiers %d, %o, and %x simply display it in different bases.
Takeaway: The prefix 0 denotes an octal literal in C and 0x denotes a hexadecimal literal. A common trap is writing int x = 010; and expecting it to equal ten—it actually equals eight.
4.2 How Data Are Stored in Memory
Regardless of the numeral system used in source code, all values are ultimately stored as sequences of binary bits. A typical int on modern systems occupies 4 bytes (32 bits). The leftmost bit is called the Most Significant Bit (MSB) and the rightmost is the Least Significant Bit (LSB).
For example, the integer 13 is stored as:
00000000 00000000 00000000 00001101
^ ^
MSB LSB
The bit positions, counted from the right starting at 0, carry the values . For 13 we have bits 0, 2, and 3 set: .
You can verify the size of any type on your system with the sizeof operator:
printf("int is %zu bytes (%zu bits)\n", sizeof(int), sizeof(int) * 8);
Takeaway: Every integer you write in C is, at the hardware level, a fixed-width pattern of bits. Understanding this is the prerequisite for all bitwise operations.
4.3 Two's Complement Representation
Modern computers represent signed integers using two's complement. In this scheme the MSB serves as the sign bit: 0 for non-negative, 1 for negative. Positive numbers are stored in their ordinary binary form. To obtain the representation of a negative number:
- Write the binary representation of its positive counterpart.
- Invert (flip) every bit.
- Add 1.
Example: Representing −13 in an 8-bit signed char:
| Step | Action | Binary |
|---|---|---|
| 1 | Start with +13 | 00001101 |
| 2 | Invert all bits | 11110010 |
| 3 | Add 1 | 11110011 |
Verification: interpreted as unsigned is ; as a signed byte it is