Table of Contents

1. What is Binary? 2. Counting in Binary 3. Binary to Decimal (and Back) 4. Hexadecimal (Base 16) 5. Octal (Base 8) 6. Bits, Bytes, and Data Sizes 7. Bitwise AND 8. Bitwise OR 9. Bitwise XOR 10. Bitwise NOT 11. Bit Shifting (Left & Right) 12. Two's Complement (Negative Numbers in Binary) 13. Common Bit Manipulation Tricks 14. How Data is Actually Stored 15. ASCII & Unicode 16. Files as Bytes -- PDFs, Images, and More 17. Bitwise Operations in C++ 18. Practice Quiz

1. What is Binary?

You count in base 10 every day. That means you use 10 digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. When you run out of digits, you carry over to the next place. After 9 comes 10 -- you put a 1 in the "tens" column and start over.

Binary is base 2. You only have 2 digits: 0 and 1. That is it. When you run out (after 1), you carry over. After 1 comes 10 (which means "2" in binary).

Why only 0 and 1? Because computers are built from billions of tiny switches (transistors) that can only be in two states: off (0) or on (1). Binary is not a choice -- it is a physical limitation. Everything a computer does comes down to flipping these switches.

Think of it like a row of light switches: OFF OFF OFF OFF OFF OFF OFF ON 0 0 0 0 0 0 0 1 = 1 in decimal OFF OFF OFF OFF OFF OFF ON OFF 0 0 0 0 0 0 1 0 = 2 in decimal OFF OFF OFF OFF OFF OFF ON ON 0 0 0 0 0 0 1 1 = 3 in decimal OFF OFF OFF OFF ON OFF ON OFF 0 0 0 0 1 0 1 0 = 10 in decimal

2. Counting in Binary

Counting in binary works exactly like counting in decimal, just with fewer digits. Watch the pattern:

Decimal Binary What happened ------- ------ ------------ 0 0000 All off 1 0001 Flip last switch on 2 0010 Last switch full, carry left 3 0011 Last switch on again 4 0100 Both right switches full, carry left again 5 0101 6 0110 7 0111 All three right switches on 8 1000 They all reset, carry to the 4th position 9 1001 10 1010 11 1011 12 1100 13 1101 14 1110 15 1111 All four switches on = maximum for 4 bits 16 10000 Need a 5th bit!
The Pattern

Look at the rightmost column: it alternates 0, 1, 0, 1, 0, 1... (every number).
The second column: 0, 0, 1, 1, 0, 0, 1, 1... (every 2 numbers).
The third column: 0, 0, 0, 0, 1, 1, 1, 1... (every 4 numbers).
Each column doubles the frequency. This is not a coincidence -- each position represents a power of 2.

How many values can N bits hold?

With 1 bit: 0 or 1 = 2 values
With 2 bits: 00, 01, 10, 11 = 4 values
With 3 bits: 000 through 111 = 8 values
With 4 bits: 0000 through 1111 = 16 values
With 8 bits (1 byte): 0 through 255 = 256 values
With N bits: 2^N values (always a power of 2)

3. Binary to Decimal (and Back)

Binary to Decimal

Each position in a binary number represents a power of 2, starting from 2^0 on the right. To convert, multiply each digit by its power of 2 and add them all up.

Position values (powers of 2): Bit position: 7 6 5 4 3 2 1 0 Power of 2: 128 64 32 16 8 4 2 1 Example: Convert 10110101 to decimal: Bit: 1 0 1 1 0 1 0 1 Value: 128 0 32 16 0 4 0 1 | | | | +--------------------+-------------+-----------+ 128 + 32 + 16 + 4 + 1 = 181
More Conversion Examples
  • 1010 = 8 + 0 + 2 + 0 = 10
  • 1111 = 8 + 4 + 2 + 1 = 15
  • 100000 = 32 = 32
  • 11001 = 16 + 8 + 0 + 0 + 1 = 25
  • 11111111 = 128+64+32+16+8+4+2+1 = 255

Decimal to Binary

Divide the number by 2 repeatedly. Write down the remainder each time. Read the remainders bottom-to-top.

Convert 25 to binary: 25 / 2 = 12 remainder 1 ---+ 12 / 2 = 6 remainder 0 | 6 / 2 = 3 remainder 0 | Read upwards 3 / 2 = 1 remainder 1 | 1 / 2 = 0 remainder 1 ---+ Answer: 11001 (reading remainders from bottom to top) Verify: 16 + 8 + 0 + 0 + 1 = 25 ✓
Faster Method -- Subtract Powers of 2

Instead of dividing, just find the largest power of 2 that fits, subtract it, and repeat:

Convert 200 to binary:
200 - 128 = 72 (128 fits, write 1) --> 1_______
72 - 64 = 8 (64 fits, write 1) --> 11______
8 - 32? No. (write 0) --> 110_____
8 - 16? No. (write 0) --> 1100____
8 - 8 = 0 (8 fits, write 1) --> 11001___
Done! Remaining positions are 0 --> 11001000
Verify: 128 + 64 + 8 = 200 ✓

4. Hexadecimal (Base 16)

Binary is great for computers but annoying for humans. The number 11010110 is hard to read. Hexadecimal (hex) solves this by grouping every 4 binary digits into one hex digit. Since 4 bits can represent 0-15, and we only have digits 0-9, we borrow letters A-F for 10-15.

Hex Decimal Binary --- ------- ------ 0 0 0000 1 1 0001 2 2 0010 3 3 0011 4 4 0100 5 5 0101 6 6 0110 7 7 0111 8 8 1000 9 9 1001 A 10 1010 B 11 1011 C 12 1100 D 13 1101 E 14 1110 F 15 1111

Hex numbers are usually written with a 0x prefix to distinguish them from decimal. So 0xFF means "FF in hex" not "the letters FF."

Converting Binary to Hex

Group the binary digits into chunks of 4 (from the right), then convert each chunk:

Binary: 1101 0110 1010 0011 Hex: D 6 A 3 So 1101011010100011 in binary = 0xD6A3 in hex Another example: Binary: 0010 1111 Hex: 2 F So 00101111 = 0x2F = 47 in decimal
Where You See Hex Every Day
  • Colors in CSS/HTML: #FF0000 = red (FF=255 red, 00=0 green, 00=0 blue)
  • Memory addresses: 0x7FFE42A0 -- where data lives in RAM
  • MAC addresses: AA:BB:CC:DD:EE:FF
  • Unicode code points: U+0041 = the letter "A"
  • Error codes: 0xDEADBEEF (a famous debug marker)
  • File signatures: PDF files start with bytes 0x25504446 ("%PDF")

Hex to Decimal

Each hex position is a power of 16. Multiply and add:

0x2F = (2 x 16) + (F x 1) = 32 + 15 = 47 0xFF = (15 x 16) + (15 x 1) = 240 + 15 = 255 0x1A3 = (1 x 256) + (10 x 16) + (3 x 1) = 256 + 160 + 3 = 419

5. Octal (Base 8)

Octal uses digits 0-7. Each octal digit represents exactly 3 binary digits. You will mostly see octal in Unix/Linux file permissions (like chmod 755).

Octal Decimal Binary ----- ------- ------ 0 0 000 1 1 001 2 2 010 3 3 011 4 4 100 5 5 101 6 6 110 7 7 111
chmod 755 Explained

In Linux, file permissions are 3 groups of 3 bits: owner, group, others.

7 = 111 = read(4) + write(2) + execute(1) = all permissions (owner)
5 = 101 = read(4) + execute(1) = read and execute (group)
5 = 101 = read(4) + execute(1) = read and execute (others)

6. Bits, Bytes, and Data Sizes

A bit is a single 0 or 1. A byte is 8 bits grouped together. A byte can represent any value from 0 to 255 (2^8 = 256 possible values). Everything in a computer is measured in bytes.

1 bit = 0 or 1 (2 values) 1 byte = 8 bits (256 values: 0-255) 1 KB = 1,024 bytes (a short text file) 1 MB = 1,024 KB = 1,048,576 bytes (a photo) 1 GB = 1,024 MB (a movie) 1 TB = 1,024 GB (a hard drive) Visual: one byte +---+---+---+---+---+---+---+---+ | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | = 0x65 = 101 = letter 'e' +---+---+---+---+---+---+---+---+ bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0

Common Data Types and Their Sizes

Type (C++)SizeRangeUse Case
bool1 bytetrue / falseFlags, conditions
char1 byte-128 to 127 or 0-255Characters, raw bytes
short2 bytes-32,768 to 32,767Small numbers
int4 bytes-2.1 billion to 2.1 billionMost numbers
long long8 bytes-9.2 quintillion to 9.2 quintillionLarge numbers
float4 bytes~7 decimal digits precisionApproximate decimals
double8 bytes~15 decimal digits precisionPrecise decimals
How an int (32 bits) looks in memory: +-------+-------+-------+-------+ | byte3 | byte2 | byte1 | byte0 | +-------+-------+-------+-------+ 00000000 00000000 00000000 00101010 = 42 That is 32 light switches to represent one number.

7. Bitwise AND (&)

Bitwise AND compares two numbers bit by bit. For each position: if both bits are 1, the result is 1. Otherwise, the result is 0. Think of it as: "both must agree to let it through."

AND Truth Table: Visual: 0 AND 0 = 0 A: 1 0 1 1 0 1 1 0 (182) 0 AND 1 = 0 B: 1 1 0 1 0 0 1 1 (211) 1 AND 0 = 0 ───────────────── 1 AND 1 = 1 A&B: 1 0 0 1 0 0 1 0 (146) ↑ ↑ ↑ Only where BOTH are 1
Real-World Analogy

AND is like two security guards at a door. Both must say "yes" for you to get through. If either says "no," you are blocked.

Common Uses of AND
  • Check if a number is even or odd: n & 1 -- if result is 1, n is odd. If 0, n is even. This works because the last bit is the "1s place" -- odd numbers always have it set.
  • Masking bits: Use AND with a mask to extract specific bits. color & 0xFF extracts the last 8 bits (blue channel from a color).
  • Clear bits: AND with 0 forces bits off. n & 0xFFFFFFF0 clears the last 4 bits.
Example: Is 42 even or odd? 42 in binary: 0 0 1 0 1 0 1 0 1 in binary: 0 0 0 0 0 0 0 1 ───────────────── 42 & 1: 0 0 0 0 0 0 0 0 = 0 --> 42 is EVEN 43 in binary: 0 0 1 0 1 0 1 1 1 in binary: 0 0 0 0 0 0 0 1 ───────────────── 43 & 1: 0 0 0 0 0 0 0 1 = 1 --> 43 is ODD
AND — Precise Rule:

A & B = 1 if and only if A = 1 AND B = 1
Otherwise A & B = 0

Algebraic: A & B = A × B (treating bits as 0/1 integers)

8. Bitwise OR (|)

Bitwise OR compares two numbers bit by bit. For each position: if either bit (or both) is 1, the result is 1. Only if both are 0 does the result stay 0.

OR Truth Table: Visual: 0 OR 0 = 0 A: 1 0 1 1 0 0 1 0 (178) 0 OR 1 = 1 B: 0 1 0 0 1 0 0 1 (73) 1 OR 0 = 1 ───────────────── 1 OR 1 = 1 A|B: 1 1 1 1 1 0 1 1 (251) ↑ ↑ ↑ ↑ ↑ ↑ ↑ Anywhere EITHER is 1
Real-World Analogy

OR is like two doors to a room. If either door is open, you can get in. Only if both are closed are you stuck.

Common Uses of OR
  • Setting bits: Use OR to force specific bits to 1. n | 0x01 forces the last bit on (makes any number odd).
  • Combining flags: READ | WRITE | EXECUTE combines permission flags. Each flag is a single bit, OR combines them into one value.
OR — Precise Rule:

A | B = 0 if and only if A = 0 AND B = 0
Otherwise A | B = 1

Algebraic: A | B = A + B - A×B

9. Bitwise XOR (^)

XOR (exclusive OR) is the most interesting bitwise operation. For each position: if the bits are different, the result is 1. If they are the same, the result is 0. Think of it as a "difference detector."

XOR Truth Table: Visual: 0 XOR 0 = 0 A: 1 0 1 1 0 1 1 0 (182) 0 XOR 1 = 1 B: 1 1 0 1 0 0 1 1 (211) 1 XOR 0 = 1 ───────────────── 1 XOR 1 = 0 A^B: 0 1 1 0 0 1 0 1 (101) ↑ ↑ ↑ ↑ Only where bits DIFFER
XOR's Magical Properties
  • a ^ a = 0 -- Any number XORed with itself is zero (all bits are the same, so all become 0)
  • a ^ 0 = a -- XOR with zero changes nothing (0 never flips a bit)
  • a ^ b ^ b = a -- XOR is its own inverse! XOR twice with the same value undoes the operation
  • a ^ b = b ^ a -- Order does not matter (commutative)
XOR's Famous Trick: Swap Two Variables Without a Temp

You can swap two numbers using only XOR -- no temporary variable needed:

Start: a = 5 (0101), b = 3 (0011) Step 1: a = a ^ b a = 0101 ^ 0011 = 0110 (a=6, b=3) Step 2: b = a ^ b b = 0110 ^ 0011 = 0101 (a=6, b=5) Step 3: a = a ^ b a = 0110 ^ 0101 = 0011 (a=3, b=5) Result: a = 3, b = 5 -- Swapped!
XOR Interview Classic: Find the Missing Number

Given an array of numbers from 1 to n with one missing, XOR all of them:

1 ^ 2 ^ 3 ^ 4 ^ 5 = some value
1 ^ 2 ^ 4 ^ 5 = different value (3 is missing)
XOR the two results and you get the missing number! Because every number that appears in both will cancel out (a ^ a = 0), leaving only the missing one.

XOR — Precise Rule:

A ^ B = 1 if and only if A ≠ B
Otherwise A ^ B = 0

Key Properties (Axioms):
• a ^ a = 0 (self-cancel)
• a ^ 0 = a (identity)
• a ^ b = b ^ a (commutative)
• (a ^ b) ^ c = a ^ (b ^ c) (associative)

10. Bitwise NOT (~)

NOT flips every single bit. Every 0 becomes 1, every 1 becomes 0. It is the only bitwise operation that works on a single number (not two).

NOT Truth Table: Visual (8-bit): NOT 0 = 1 A: 0 1 0 1 0 1 0 1 (85) NOT 1 = 0 ~A: 1 0 1 0 1 0 1 0 (170) ↕ ↕ ↕ ↕ ↕ ↕ ↕ ↕ Every bit gets flipped
Watch Out -- Signed Numbers

In C++ with a 32-bit int, ~0 does not give you a giant positive number. Because of two's complement (explained later), ~0 = -1. And ~5 = -6. The formula is: ~n = -(n + 1).

NOT — Precise Rule:

~A flips every bit: 0 → 1, 1 → 0

For a single bit: ~A = 1 - A
For n-bit unsigned: ~x = (2ⁿ - 1) - x

11. Bit Shifting (Left & Right)

Shifting moves all bits left or right by a certain number of positions. Bits that "fall off" the edge are lost. New bits that enter from the other side are 0.

Left Shift (<<)

Shifts all bits to the left. Each left shift multiplies by 2.

5 << 1 (shift left by 1) Before: 0 0 0 0 0 1 0 1 = 5 ← ← ← ← After: 0 0 0 0 1 0 1 0 = 10 5 << 2 (shift left by 2) Before: 0 0 0 0 0 1 0 1 = 5 ← ← ← ← ← After: 0 0 0 1 0 1 0 0 = 20 5 << 3 = 40 (5 x 2 x 2 x 2 = 5 x 8) Pattern: n << k = n x 2^k

Right Shift (>>)

Shifts all bits to the right. Each right shift divides by 2 (rounding down).

20 >> 1 (shift right by 1) Before: 0 0 0 1 0 1 0 0 = 20 → → → → After: 0 0 0 0 1 0 1 0 = 10 20 >> 2 (shift right by 2) Before: 0 0 0 1 0 1 0 0 = 20 → → → → → After: 0 0 0 0 0 1 0 1 = 5 Pattern: n >> k = n / 2^k (integer division, rounds down)
Why Shifts Are Useful

Shifts are extremely fast -- much faster than multiplication or division. The CPU can shift bits in a single clock cycle. This is why you see them in performance-critical code, game engines, and embedded systems. n << 1 is the same as n * 2 but faster.

Shift — Precise Rules:

• Left shift: x << n = x × 2ⁿ (when no overflow)
• Unsigned right shift: x >> n = floor(x / 2ⁿ)
• Arithmetic right shift (signed): preserves sign bit (fills with 1s for negative numbers)
Arithmetic vs Logical Right Shift

Unsigned right shift (logical): fills vacant bits with 0. Always divides by 2ⁿ.

Signed right shift (arithmetic): fills vacant bits with the sign bit. For negative numbers, this rounds toward negative infinity, not toward zero.

In C/C++, right-shifting a negative number is implementation-defined. In Java, >> is arithmetic and >>> is logical. In Python, >> is always arithmetic (sign-preserving).

12. Two's Complement (Negative Numbers in Binary)

How does a computer store -5 when it only has 0s and 1s? It uses a system called two's complement. The leftmost bit is the sign bit: 0 = positive, 1 = negative.

How to Get the Two's Complement (Make a Number Negative)

Two steps: flip all the bits (NOT), then add 1.

Convert 5 to -5 (using 8 bits): Step 1: Start with 5: 0 0 0 0 0 1 0 1 Step 2: Flip all bits (NOT): 1 1 1 1 1 0 1 0 Step 3: Add 1: 1 1 1 1 1 0 1 1 = -5 Verify: Add 5 + (-5), should get 0: 0 0 0 0 0 1 0 1 (5) + 1 1 1 1 1 0 1 1 (-5) ───────────────── 1 0 0 0 0 0 0 0 0 (the 1 overflows and is discarded) = 0 0 0 0 0 0 0 0 = 0 ✓
8-Bit Two's Complement Range

With 8 bits, you can represent -128 to +127:

0111 1111 = +127 (largest positive) 0111 1110 = +126 ... 0000 0001 = +1 0000 0000 = 0 1111 1111 = -1 (all 1s = -1, not 255!) 1111 1110 = -2 ... 1000 0001 = -127 1000 0000 = -128 (largest negative)
Two's Complement — Formal Definition:

The two's complement of an n-bit number x is: 2ⁿ - x

Algorithm (equivalent): Flip all bits, add 1
Why they're the same: Flipping bits gives (2ⁿ - 1 - x). Adding 1 gives 2ⁿ - x.

13. Common Bit Manipulation Tricks

These are the bit tricks that show up in interviews and real systems code. Each one exploits a specific property of binary.

TrickCodeWhat It Does
Check if evenn & 1 == 0Last bit is 0 = even
Check if oddn & 1 == 1Last bit is 1 = odd
Multiply by 2n << 1Shift left = double
Divide by 2n >> 1Shift right = halve
Multiply by 2^kn << kShift left by k positions
Check if power of 2n & (n-1) == 0Powers of 2 have exactly one 1-bit
Get lowest set bitn & (-n)Isolates the rightmost 1
Turn off lowest set bitn & (n-1)Clears the rightmost 1
Toggle bit at position kn ^ (1 << k)Flip the kth bit
Set bit at position kn | (1 << k)Force kth bit to 1
Clear bit at position kn & ~(1 << k)Force kth bit to 0
Check bit at position k(n >> k) & 1Is the kth bit set?
Why n & (n-1) == 0 Detects Powers of 2

Powers of 2 in binary have exactly one 1-bit:

n = 8: 1000 n - 1 = 7: 0111 n & (n-1): 0000 --> equals 0, so 8 IS a power of 2 n = 6: 0110 n - 1 = 5: 0101 n & (n-1): 0100 --> NOT zero, so 6 is NOT a power of 2 The pattern: subtracting 1 from a power of 2 flips all bits below the single 1-bit. ANDing them together always gives 0.

14. How Data is Actually Stored

Everything in your computer -- every photo, song, game, text message, and PDF -- is stored as a sequence of bytes. There is no "text" or "image" at the hardware level. It is all just bytes. The software decides how to interpret those bytes.

The same byte 01000001 (0x41 = 65) could mean: As a number: 65 As a character: 'A' (ASCII code 65) As a pixel: a shade of gray (65 out of 255) As audio: a sample amplitude As a flag set: bits 0 and 6 are set The BYTE doesn't know what it is. The PROGRAM decides.

Endianness: Byte Order Matters

When a number needs more than 1 byte (like a 32-bit int), the question is: which byte comes first in memory? There are two conventions:

Storing the number 0x12345678 (305,419,896): Big-endian (most significant byte first): Address: 0x00 0x01 0x02 0x03 Value: [12] [34] [56] [78] Like reading left to right -- "natural" order Little-endian (least significant byte first): Address: 0x00 0x01 0x02 0x03 Value: [78] [56] [34] [12] Reversed! The "little end" comes first Most modern CPUs (x86, ARM) use little-endian. Network protocols typically use big-endian.
Why Little-Endian Exists

It seems backward, but little-endian has a practical advantage: the first byte always tells you if a number is even or odd (the least significant byte is at the lowest address). It also makes some hardware operations simpler. You do not need to memorize this -- just know it exists and that it explains why bytes sometimes look "reversed" in memory dumps.

15. ASCII & Unicode

ASCII -- The Original Character Encoding

ASCII assigns a number (0-127) to each character. It uses 7 bits (usually stored in 1 byte). Here are the important ranges:

Dec Hex Binary Char Notes --- --- -------- ---- ----- 32 0x20 0010 0000 ' ' Space 48 0x30 0011 0000 '0' Start of digits (0-9 = 48-57) 65 0x41 0100 0001 'A' Start of uppercase (A-Z = 65-90) 97 0x61 0110 0001 'a' Start of lowercase (a-z = 97-122) 10 0x0A 0000 1010 '\n' Newline 13 0x0D 0000 1101 '\r' Carriage return 0 0x00 0000 0000 '\0' Null terminator (end of string in C/C++)
Useful ASCII Tricks for Programming
  • 'a' - 'A' = 32 -- The difference between lowercase and uppercase is always 32 (one bit flip!)
  • char ^ 32 flips the case of a letter (toggles bit 5)
  • char | 32 forces lowercase
  • char & ~32 forces uppercase
  • '5' - '0' = 5 -- Subtract '0' to convert a digit character to its numeric value
Why XOR 32 toggles case: 'A' = 0100 0001 'a' = 0110 0001 ↑ bit 5 is the ONLY difference 'A' ^ 32 = 0100 0001 ^ 0010 0000 = 0110 0001 = 'a' 'a' ^ 32 = 0110 0001 ^ 0010 0000 = 0100 0001 = 'A'

Unicode -- Characters for Every Language

ASCII only covers English (128 characters). Unicode covers every writing system on Earth -- over 150,000 characters including Chinese, Arabic, emoji, and ancient scripts. Unicode assigns each character a "code point" (like U+0041 for 'A').

UTF-8 is the most common encoding. It uses 1-4 bytes per character: ASCII characters use 1 byte (backward compatible), while other characters use 2-4 bytes. This is why a file with only English text is roughly 1 byte per character, but a file with Chinese or emoji is larger.

16. Files as Bytes -- PDFs, Images, and More

Every file on your computer is just a sequence of bytes. The first few bytes usually identify the file type -- this is called a magic number or file signature. Software reads these bytes to know what kind of file it is dealing with.

File signatures (first bytes): File Type Hex Signature ASCII Reading --------- ------------------------- ------------- PDF 25 50 44 46 %PDF PNG image 89 50 4E 47 0D 0A 1A 0A .PNG.... JPEG image FF D8 FF ... ZIP archive 50 4B 03 04 PK.. ELF binary 7F 45 4C 46 .ELF MP3 audio FF FB or 49 44 33 .. or ID3 GIF image 47 49 46 38 GIF8

How a PDF is Structured

A PDF is not magic -- it is a structured byte sequence:

PDF File Structure: +---------------------------+ | Header: %PDF-1.7 | <-- First line, identifies as PDF +---------------------------+ | Body: | | - Object 1 (metadata) | <-- Each "object" is a chunk of data | - Object 2 (page def) | | - Object 3 (font info) | | - Object 4 (text stream)| <-- Actual text, compressed as bytes | - Object 5 (image data) | <-- Images embedded as raw bytes +---------------------------+ | Cross-reference table | <-- Index: "object 1 starts at byte 53" +---------------------------+ | Trailer: startxref 1234 | <-- Points to the cross-ref table +---------------------------+

How Images are Stored

An image is a grid of pixels. Each pixel is typically 3 bytes (RGB): One pixel: [R] [G] [B] [FF][00][00] = pure red (255, 0, 0) [00][FF][00] = pure green (0, 255, 0) [00][00][FF] = pure blue (0, 0, 255) [FF][FF][FF] = white (255, 255, 255) [00][00][00] = black (0, 0, 0) A tiny 3x2 image (3 pixels wide, 2 tall): Row 0: [FF0000] [00FF00] [0000FF] Red Green Blue Row 1: [FFFF00] [FF00FF] [00FFFF] Yellow Magenta Cyan Total size: 3 pixels x 2 rows x 3 bytes/pixel = 18 bytes A 1920x1080 photo: 1920 x 1080 x 3 = 6,220,800 bytes (~6 MB raw) That is why we use compression (JPEG, PNG) to make files smaller.
Reading File Bytes in C++
// Open a file and read its first 16 bytes
#include <fstream>
#include <iostream>
#include <iomanip>

int main() {
    std::ifstream file("document.pdf", std::ios::binary);
    unsigned char buffer[16];
    file.read(reinterpret_cast<char*>(buffer), 16);

    // Print each byte as hex
    for (int i = 0; i < 16; i++) {
        std::cout << std::hex << std::setw(2)
                  << std::setfill('0') << (int)buffer[i] << " ";
    }
    // Output: 25 50 44 46 2d 31 2e 37 ... (%PDF-1.7...)
    return 0;
}C++
Key Insight

When you "open" a file in a program, you are really just reading a stream of bytes. A text editor interprets those bytes as characters. An image viewer interprets them as pixel colors. A PDF reader interprets them as document structure. The bytes are the same -- only the interpretation changes. Understanding this makes you a fundamentally better programmer.

17. Bitwise Operations in C++

Here is every bitwise operator in C++ with examples you can run.

OperatorSymbolExampleResult
AND&12 & 108 (1100 & 1010 = 1000)
OR|12 | 1014 (1100 | 1010 = 1110)
XOR^12 ^ 106 (1100 ^ 1010 = 0110)
NOT~~0-1 (all bits flipped)
Left shift<<5 << 220 (5 x 4)
Right shift>>20 >> 25 (20 / 4)

Practical C++ Examples

// Count set bits (number of 1s in binary representation)
int countBits(int n) {
    int count = 0;
    while (n) {
        n &= (n - 1);  // Turn off the lowest set bit
        count++;
    }
    return count;
}

// Or use the built-in:
int bits = __builtin_popcount(42);  // = 3 (101010 has three 1s)C++
// Use bitmask as a set of flags
const int READ    = 1 << 0;  // 0001 = 1
const int WRITE   = 1 << 1;  // 0010 = 2
const int EXECUTE = 1 << 2;  // 0100 = 4

int perms = READ | WRITE;           // 0011 = can read and write
bool canRead = (perms & READ) != 0; // true
bool canExec = (perms & EXECUTE) != 0; // false
perms |= EXECUTE;                   // 0111 = add execute
perms &= ~WRITE;                    // 0101 = remove writeC++
// Subset enumeration with bitmasks
// Iterate over all subsets of {A, B, C} using a 3-bit mask
for (int mask = 0; mask < (1 << 3); mask++) {
    // mask goes: 000, 001, 010, 011, 100, 101, 110, 111
    if (mask & (1 << 0)) cout << "A ";
    if (mask & (1 << 1)) cout << "B ";
    if (mask & (1 << 2)) cout << "C ";
    cout << endl;
}
// Output: (empty), A, B, AB, C, AC, BC, ABCC++
// Print a number in binary (useful for debugging)
#include <bitset>
std::cout << std::bitset<8>(42) << std::endl;  // 00101010
std::cout << std::bitset<32>(-1) << std::endl; // 11111111111111111111111111111111C++

18. Practice Quiz

Test your understanding. Click an answer to check it.

Q1: What is 1010 in decimal?

Answer: 10
1010 = (1 x 8) + (0 x 4) + (1 x 2) + (0 x 1) = 8 + 2 = 10.

Q2: What is 0xFF in decimal?

Answer: 255
0xFF = (15 x 16) + (15 x 1) = 240 + 15 = 255. This is the maximum value of a single byte.

Q3: What is 12 & 10 (bitwise AND)?

Answer: 8
12 = 1100, 10 = 1010. AND: 1100 & 1010 = 1000 = 8. Only bit 3 is set in both.

Q4: What does n & (n-1) do?

Answer: Turns off the lowest set bit.
Subtracting 1 flips the lowest set bit and all bits below it. ANDing with the original clears just that lowest bit. This is one of the most important bit tricks to know.

Q5: How many bytes does one pixel in an RGB image use?

Answer: 3 bytes
One byte for Red (0-255), one for Green (0-255), one for Blue (0-255). With an alpha (transparency) channel, it is 4 bytes (RGBA).

Q6: What is 5 << 3?

Answer: 40
Left shift by 3 means multiply by 2^3 = 8. So 5 x 8 = 40. In binary: 00101 becomes 00101000.