Chapter 05
How computers run your code
Enough of what happens below your code that memory and number bugs stop surprising you: bits, text, the CPU, memory, compilers and the operating system.
Bits, bytes and binary
Also called binary, hexadecimal, hex
- What
- A bit is a 0 or 1. A byte is 8 bits and holds 256 values. Everything in a computer (numbers, text, images, code) is stored as bytes, and hexadecimal is a compact way to write them.
- Why
- It explains integer limits, file sizes, colours like
#FF8800and why data needs an agreed format to mean anything. - Example
- The byte 01000001 is 65 in decimal and 0x41 in hex. Read as text in ASCII or UTF-8, it is the letter A.
Text encoding and Unicode
Also called UTF-8, ASCII, code points, UTF-16, Unicode
- What
- Unicode gives every character a number (a code point). An encoding such as UTF-8 turns code points into bytes: 1 byte for ASCII letters, up to 4 for others such as emoji.
- Why
- Length, slicing and file reading all depend on it. Mismatched encodings produce garbled text, and string lengths do not always equal visible characters.
var s = "h茅llo 馃榾";
Console.WriteLine(s.Length); // 8 UTF-16 code units
Console.WriteLine(s.EnumerateRunes().Count()); // 7
Console.WriteLine(Encoding.UTF8.GetByteCount(s)); // 11Watch out
Assuming one character is one byte, or one unit of string length, when cutting or measuring text.
How the CPU runs a program
Also called fetch-decode-execute, machine code, CPU cache
- What
- The CPU repeatedly fetches an instruction from memory, decodes it and executes it: simple steps such as add, compare, jump and copy. It keeps recently used data in small, very fast caches.
- Why
- Every line you write ends up as these steps. Cache behaviour explains why looping over an array is much faster than chasing pointers through a linked list.
- Example
total += pricebecomes roughly: loadtotalinto a register, loadprice, add them, store the result back in memory.
Compiled and interpreted code
Also called compiler, interpreter, JIT, bytecode
- What
- A compiler translates source to machine code before running (C++). An interpreter runs code step by step (CPython runs bytecode). Java, C# and JavaScript engines compile to bytecode or directly, then JIT-compile hot code while running.
- Why
- It explains when errors appear (build time or run time), why start-up and speed differ, and why C++ needs a build for each platform.
Watch out
Calling a language "compiled" or "interpreted" as a fixed fact: it describes an implementation, and most modern runtimes mix both.
Stack and heap
Also called call stack, stack frame, heap memory, stack overflow
- What
- The stack holds each running function's local variables in frames that vanish when it returns. The heap holds objects that must outlive a call and is managed by a garbage collector or, in C++, by owners.
- Why
- It explains why deep recursion causes a stack overflow, why returning a pointer to a local is a bug in C++, and where your objects actually live.
Watch out
Recursing thousands of levels deep on large input, which overflows the stack even though the logic is correct.
Passing arguments to functions
Also called pass by value, pass by reference, pass by sharing
- What
- Python, JavaScript and Java always pass a copy of the value, but for objects that value is a reference, so the function can change the object but not rebind your variable. C++ and C# copy unless you pass by reference.
- Why
- It tells you whether a function can modify the list you give it, which is behind many "my data changed" and "my change vanished" bugs.
static void AddItem(List<string> items) => items.Add("milk");
static void Reset(List<string> items) => items = [];
static void Bump(ref int n) => n++;
var basket = new List<string> { "eggs" };
AddItem(basket);
Reset(basket);
int count = 1;
Bump(ref count);
Console.WriteLine($"{basket.Count} {count}"); // 2 2Watch out
Reassigning a parameter inside a function and expecting the caller's variable to change.
Garbage collection
Also called GC, reference counting, memory leak
- What
- The runtime frees heap objects automatically once nothing can reach them. CPython mostly uses reference counting plus a cycle collector; the JVM, .NET and JavaScript engines use tracing collectors.
- Why
- You rarely free memory by hand, but you still control what stays reachable. Leaks in these languages come from references you forgot to drop.
Watch out
Growing a global cache, list or event listener registry forever, which keeps every object in it alive.
Floating point numbers
Also called IEEE 754, float, double, rounding error
- What
- Floats store numbers in binary with limited precision, so most decimal fractions such as 0.1 are approximations. Small errors appear in arithmetic and comparisons.
- Why
- It is why
0.1 + 0.2is not exactly0.3, and why money should use integer cents or a decimal type.
Console.WriteLine(0.1 + 0.2); // 0.30000000000000004
Console.WriteLine(0.1 + 0.2 == 0.3); // False
Console.WriteLine(0.1m + 0.2m); // 0.3Watch out
Comparing floats with ==, or storing money in a float or double.
Integer overflow
Also called wraparound, MAX_VALUE, BigInt
- What
- Fixed-size integers have a maximum. Going past it wraps around in Java and C#, is undefined behaviour for signed integers in C++, and loses precision in JavaScript numbers. Python integers grow without limit.
- Why
- Counters, timestamps in milliseconds, ids and money totals can exceed 32-bit limits; choosing a wide enough type avoids silent wrong answers.
int max = int.MaxValue;
Console.WriteLine(max + 1); // -2147483648
long safe = (long)max + 1;
checked
{
int boom = max + 1; // throws OverflowException
}Watch out
Computing a midpoint as (lo + hi) / 2 with large ints, which can overflow; use lo + (hi - lo) / 2.
Processes and threads
Also called operating system, OS, concurrency
- What
- The operating system runs each program as a process with its own memory. A process can have several threads that share that memory and run at the same time on different CPU cores.
- Why
- It explains why one crashed program does not take down others, how servers handle many requests, and why shared data between threads needs care.
Watch out
Two threads updating the same variable without a lock, which loses updates at random (a race condition).