← Back to C

C Glossary

Key terms from the C course, linked to the lesson that introduces each one.

3,202 terms.

#

__attribute__((packed))
The __attribute__((packed)) directive instructs the compiler to eliminate padding bytes, creating a smaller, dense structure at the cost of potential performance overhead.
Lesson 577Using __attribute__((packed))Lesson 642The `__attribute__((packed))` directive
__builtin_bswap
The __builtin_bswap family of functions tells the CPU to use a specialized hardware instruction (like BSWAP on x86) to flip the bytes in a single clock cycle.
Lesson 956The `__builtin_bswap` compiler intrinsics
__builtin_popcount
In this snippet, __builtin_popcount isn't a function you wrote or linked from a library.
Lesson 964Compiler intrinsics as an alternative
__FILE__
Use __FILE__ and __LINE__ in your error messages to let the compiler tell you exactly where your program is failing.
Lesson 1159Using `__FILE__` and `__LINE__` macros
__HEADER_H
Avoid Reserved Prefixes: Never start your guards with double underscores (e.g., __HEADER_H) or a single underscore followed by a capital letter.
Lesson 1203Header guard best practices
__LINE__
Macros are perfect for this because they can capture the exact line number where a failure occurred using the built-in __LINE__ constant.
Lesson 1159Using `__FILE__` and `__LINE__` macrosLesson 1174Building a minimal custom test harness
__linux__
For example, _WIN32 is typically defined on Windows, while __linux__ is defined on Linux.
Lesson 783Testing for platform-specific code
__thread
When you mark a variable with __thread, the operating system and linker work together to create a new "copy" of that variable every time a new thread starts.
Lesson 1122Thread-local storage basics
__x86_64__
If it sees __x86_64__, it ignores the ARM code entirely.
Lesson 963Platform-specific assembly (x86 vs ARM)
_Alignas
Use _Alignof to discover a type's memory boundary requirements and _Alignas to strictly enforce custom memory positioning.
Lesson 933The `_Alignof` and `_Alignas` keywords
_Alignof
What you'll learn: How to use the _Alignof operator to discover the memory alignment requirements of different data types.
Lesson 453Determining variable alignment in memoryLesson 933The `_Alignof` and `_Alignas` keywords
_Atomic
Use volatile to prevent compiler optimizations on hardware-related memory, and use _Atomic to prevent data corruption in multi-threaded programs.
Lesson 1141Introduction to <stdatomic.h>Lesson 1147Volatile vs Atomic
_Bool
If you wanted to make your code readable, you had to manually define constants or include <stdbool.h> (introduced in C99), which gave you bool as a macro for the slightly clunky _Bool type.
Lesson 647Restrictions on bit-field typesLesson 969C23: The `bool`, `true`, and `false` keywords
_IOFBF
Alternatively, if you were writing a massive 1GB file, you might create a large char array of 64KB and pass it to setvbuf with _IOFBF to make the process much faster by reducing the number of hardware writes.
Lesson 752Setting custom buffers with setvbuf
_IOLBF
_IOLBF (Line Buffering): It flushes the data whenever a newline (\n) is encountered.
Lesson 752Setting custom buffers with setvbuf
_IONBF
_IONBF (No Buffering): Data goes straight to the disk immediately.
Lesson 752Setting custom buffers with setvbuf
_t
Use a consistent naming style like PascalCase or a _t suffix for typedef names to clearly separate type definitions from variable names.
Lesson 86Fixed-width types from `<stdint.h>`Lesson 612Naming conventions for typedef types
_WIN32
For example, _WIN32 is typically defined on Windows, while __linux__ is defined on Linux.
Lesson 783Testing for platform-specific code
--j
To avoid these bugs, never pass expressions that change values (like i++, --j, or func()) into a macro.
Lesson 775Side effects in macro arguments
--x
Prefix decrement (--x) subtracts one from a variable immediately and returns the updated value to the rest of the expression.
Lesson 200Prefix decrement `--x`
-based
C23’s nullptr replaces the ambiguous 0-based NULL macro with a dedicated, type-safe constant specifically designed for pointers.
Lesson 970C23: The `nullptr` constant
-c
The -c flag stops the build process early to create an object file (.o), allowing you to compile individual pieces of a program without linking them into a final executable.
Lesson 804The `-c` flag for compilation
-D
The -D flag allows you to pass configuration settings from your terminal directly into your code, enabling "feature toggles" at compile time.
Lesson 786Feature toggles via command line `-D`
-DDEBUG
By typing -DDEBUG in your terminal, you are telling the compiler, "For this specific build, act as if #define DEBUG is written at the top of every file."
Lesson 786Feature toggles via command line `-D`
-DNDEBUG
If you are using a tool like GCC, you can add the flag -DNDEBUG to your build command.
Lesson 902Disabling assertions with `NDEBUG`
-DPREMIUM
The compiler sees the -DPREMIUM flag, satisfies the #ifdef condition, and includes the premium message in the final executable.
Lesson 786Feature toggles via command line `-D`
-E
What you'll learn: How to use the -E flag to peek behind the curtain and see exactly how the preprocessor modifies your source code before the compiler ever sees it.
Lesson 767How `gcc -E` shows preprocessor output
-fPIC
The -fPIC flag ensures shared library code uses relative memory addresses rather than fixed ones, allowing multiple programs to share the same code regardless of where it is loaded in memory.
Lesson 814Position Independent Code `-fPIC`
-fsanitize=address
Compile with -fsanitize=address to turn memory bugs into immediate, descriptive crashes that tell you exactly where you went wrong.
Lesson 1171Using AddressSanitizer (`-fsanitize=address`)
-fsanitize=undefined
UBSan is a compiler flag (-fsanitize=undefined) that makes your program "scream" the moment it hits Undefined Behavior, turning invisible bugs into readable error reports.
Lesson 941Tools to detect UB: UBSan
-I
However, since the header is no longer in the same folder as the source code, you typically tell your compiler (like GCC) where the "spice rack" is located using the -I (include) flag.
Lesson 63Header search pathsLesson 800Organizing /src and /include foldersLesson 1201Using `clang-format` for automationLesson 1208Adding command line flags (e.g., `-i` for case)
-Iinclude
The -Iinclude part tells the preprocessor: "If you can't find a header file in the current folder, check the /include directory next."
Lesson 800Organizing /src and /include folders
-L
If you forget -L, the compiler will say "library not found." If you forget -l, the compiler will complain about "undefined references" because it saw the function calls in your code but didn't know which library provided the instructions to execute them.
Lesson 812Linking with static librariesLesson 815Linking with shared libraries `-l` and `-L`
-L./libs
-L./libs: Adds the "libs" folder to the search path.
Lesson 815Linking with shared libraries `-l` and `-L`
-lm
On some systems (like Linux), when you use <math.h>, you need to tell the compiler to link the math library manually by adding -lm to the end of your compile command, like this: gcc main.c -lm.
Lesson 861Basic power and square root: `pow` and `sqrt`Lesson 862Exponential and logarithmic functions: `exp`, `log`, `log10`Lesson 863Trigonometric functions in radians
-lmathhelper
For example, to link a library file named libmathhelper.so, you simply write -lmathhelper.
Lesson 815Linking with shared libraries `-l` and `-L`
-lmathutils
-lmathutils tells it to link the library (the lib prefix and .a extension are assumed).
Lesson 812Linking with static libraries
-lphysics
-lphysics: Searches for a file named libphysics.so (or .dylib) inside those paths and links it.
Lesson 815Linking with shared libraries `-l` and `-L`
-o
This is why, as you move toward professional development, you will eventually start using the -o (output) flag to give your programs unique names, like gcc hello.c -o hello.
Lesson 57Basic `gcc` command flagsLesson 58Naming the output with `-o`Lesson 60Understanding the `a.out` defaultLesson 1188Compiler optimization levels (`-O1`, `-O2`, `-O3`)
-O0
By default, the compiler uses -O0 (no optimization).
Lesson 1188Compiler optimization levels (`-O1`, `-O2`, `-O3`)
-O2
On a modern compiler with optimizations turned on (-O2 or -O3), the code above might print 1.000000 instead of the integer bit-pattern you expected.
Lesson 936Strict aliasing rule violationsLesson 958The basic `volatile` asm blockLesson 1188Compiler optimization levels (`-O1`, `-O2`, `-O3`)Lesson 1190Loop unrolling explained
-O3
Only switch to -O3 if you are building something performance-heavy, like a game engine or a physics simulator, and you have tested to ensure the larger file size is worth the speed boost.
Lesson 936Strict aliasing rule violationsLesson 958The basic `volatile` asm blockLesson 1188Compiler optimization levels (`-O1`, `-O2`, `-O3`)Lesson 1190Loop unrolling explained
-pg
The gprof profiler helps you find "hotspots" in your code by recording function execution times, provided you compile with the -pg flag.
Lesson 1182Introduction to the `gprof` profiler
-score
If you hit a "penalty" zone, the game might calculate your new score as -score.
Lesson 156Unary plus and minus
-std=
Use the -std= flag to ensure your compiler follows a specific version of the C standard, making your code predictable and portable across different systems.
Lesson 972Specifying the standard with `-std=` flags
-std=c11
If you write code using -std=c11, you can be confident that another developer using a different compiler (like Clang instead of GCC) will be able to build your project as long as they also use the C11 flag.
Lesson 972Specifying the standard with `-std=` flags
-std=c99
Specifying -std=c99 tells the compiler to allow it.
Lesson 972Specifying the standard with `-std=` flags
-Wall
The -Wall flag stands for "Warnings: all." It tells the compiler to be extra chatty and warn you about potential mistakes, even if they aren't technical errors that stop the compilation.
Lesson 57Basic `gcc` command flagsLesson 69Enabling all warnings with `-Wall`
." Since
The inner loop starts and says, "Run until j reaches i." Since i is 1, it runs once and prints one star.
Lesson 303Controlling the inner loop with outer loop variables
./a.out
On most systems, you do this by typing ./a.out.
Lesson 60Understanding the `a.out` default
./area_calc
To run it, you would simply type ./area_calc.
Lesson 58Naming the output with `-o`
./hello
If you compile this as hello, you can run it normally: ./hello.
Lesson 761Standard stream redirection in shells
./hello < name.txt
From a file: ./hello < name.txt (The program reads the name from the file instead of waiting for you to type).
Lesson 761Standard stream redirection in shells
./hello < name.txt > output.txt
Both: ./hello < name.txt > output.txt (A completely automated process).
Lesson 761Standard stream redirection in shells
./hello > output.txt
To a file: ./hello > output.txt (The screen stays blank; the greeting goes into the file).
Lesson 761Standard stream redirection in shells
./my_program
Up until now, you have likely tested your code by running ./my_program, typing in a value, and checking if the output looks right.
Lesson 32Executing from the command lineLesson 1178Automating tests with a Shell script
./myprog > log.txt
When you run ./myprog > log.txt, the shell forks a process, uses dup2() to swap the child's STDOUT with the file, and then executes your code.
Lesson 1095Redirecting output with dup2()
./myprog hello world
When you run a command like ./myprog hello world, the operating system gathers those words and hands them to your program.
Lesson 506Command line arguments `char **argv`
./program
When you run ./program, the tool will intercept the error and print:
Lesson 941Tools to detect UB: UBSan
./test.sh
To run this, you would give the script permission with chmod +x test.sh and then type ./test.sh.
Lesson 1178Automating tests with a Shell script
.a
The ar tool creates static libraries by bundling .o object files into a single .a archive for easier distribution and linking.
Lesson 810What is a static library `.a`Lesson 811Creating archives with the `ar` toolLesson 812Linking with static libraries
.c
By keeping the "bricks" (the actual memory and logic) in your .c files and only sharing the "blueprints" in your .h files, you ensure that every part of your program is looking at the same map without trying to build the same wall twice.
Lesson 6C as a compiled languageLesson 9Role of the CompilerLesson 11Setting up MinGW on WindowsLesson 15The concept of a Source FileLesson 16Naming conventions for .c filesLesson 30Creating an executable binaryLesson 57Basic `gcc` command flagsLesson 59Compiling multiple source filesLesson 62Automating the build processLesson 63Header search pathsLesson 137Global variables and file scopeLesson 146The `extern` keyword for multi-file codeLesson 326Placement of functions in a fileLesson 368Inline functions in header filesLesson 377Role of the `.h` fileLesson 378Separating interface from implementationLesson 379Using `#include` with quotesLesson 381Compiling multiple `.c` filesLesson 673Opaque types with header filesLesson 788The purpose of header filesLesson 792Forward declarations in headersLesson 793What should NOT go in a headerLesson 795Standard header search pathsLesson 796Splitting code into `.c` and `.h`Lesson 797The `extern` keyword for variablesLesson 798Static functions for file scopingLesson 800Organizing /src and /include foldersLesson 801Naming conventions for large projectsLesson 803From source code to object filesLesson 805What is inside a `.o` fileLesson 806Linking multiple object filesLesson 807Understanding 'undefined reference' errorsLesson 809Symbol tables and visibilityLesson 817Why we need build toolsLesson 822Automatic variables like `$@` and `$<`Lesson 918Internal vs external linkage basicsLesson 919The `static` keyword in global scopeLesson 921Sharing variables across files with `extern`Lesson 925Common linkage errors and 'multiple definition'Lesson 1210Structuring the project into multiple `.c` filesLesson 1211Writing the Makefile for the project
.clang-format
Professional teams use a .clang-format configuration file in their project folders.
Lesson 1201Using `clang-format` for automation
.dat
When the program starts, we want to open our .dat file and fill that array.
Lesson 1218Loading data from a binary file
.dll
When you compile a program using a shared library (.so on Linux or .dll on Windows), the library code isn't actually inside your executable.
Lesson 813What is a shared library `.so` / `.dll`Lesson 816Runtime library loading and `LD_LIBRARY_PATH`
.dylib
There is a small trick here: by convention, shared library files start with lib and end with .so (on Linux) or .dylib (on macOS).
Lesson 815Linking with shared libraries `-l` and `-L`
.gpa
Identify the field: Use the dot operator (e.g., .gpa) to pick the variable inside that struct.
Lesson 632Indexing into a struct array
.grade
Once that specific structure is "selected," we use .grade to drill down into the floating-point value stored there.
Lesson 633Combining array indexing and member accessLesson 635Sorting an array of structs
.jpg
Because they treat everything as a raw byte, you can use the exact same code to copy a .txt file or a .jpg file.
Lesson 1097Reading and writing raw bytes
.lib
In C programming, a static library (ending in .a for "archive" on Linux/macOS or .lib on Windows) is that toolbox.
Lesson 810What is a static library `.a`Lesson 812Linking with static libraries
.member = value
Designated initializers use the .member = value syntax to make structure initialization more readable, flexible, and resistant to changes in the struct's definition.
Lesson 603Designated initializers in C99
.member_name
By default, a union initialization targets the first member, but you can target any specific member using the .member_name syntax.
Lesson 660Initializing a union
.member_name = value
This uses the dot notation (.member_name = value) inside the curly braces.
Lesson 660Initializing a union
.out
The French recipe is the Executable (the .exe or .out file).
Lesson 6C as a compiled language
.PHONY
What you'll learn: How to use the .PHONY attribute to ensure Makefile commands like clean run correctly even if files with those names exist.
Lesson 823Phony targets like `clean` and `all`
.s
Once this phase is finished, you have an Assembly file (usually ending in .s).
Lesson 26Phase 2: Compilation to Assembly
.sh
A Shell script (usually ending in .sh) is just a list of commands your computer executes in order.
Lesson 1178Automating tests with a Shell script
.size
.size: Look inside myApp for a member named size.
Lesson 624Accessing members of nested structs
.so
When you compile a program using a shared library (.so on Linux or .dll on Windows), the library code isn't actually inside your executable.
Lesson 813What is a shared library `.so` / `.dll`Lesson 815Linking with shared libraries `-l` and `-L`Lesson 816Runtime library loading and `LD_LIBRARY_PATH`
.supp
Valgrind allows you to create a "hush list" called a suppression file (usually ending in .supp).
Lesson 570Suppressing known tool warnings
.txt
Because they treat everything as a raw byte, you can use the exact same code to copy a .txt file or a .jpg file.
Lesson 1097Reading and writing raw bytes
.width
.width: Look inside that size object for a member named width.
Lesson 624Accessing members of nested structs
.x
Then, it looks inside that result for .x.
Lesson 632Indexing into a struct array
( *name )
A function pointer is declared by wrapping the pointer name in parentheses, like ( *name ), and it must strictly match the return type and parameter list of the functions it points to.
Lesson 928Declaring pointers to functions
((a++) * (a++))
However, the preprocessor expands SQUARE(a++) into ((a++) * (a++)).
Lesson 775Side effects in macro arguments
((x) * (x))
You might notice the heavy use of parentheses in ((x) * (x)).
Lesson 772Defining function-like macros
(*action)()
While older versions of C sometimes required you to write (*action)();, modern C allows you to use the pointer name directly.
Lesson 516Calling a function via a pointer
(*array_ptr)
By adding parentheses (*array_ptr), we force the compiler to treat array_ptr as a pointer first.
Lesson 927Arrays of pointers vs Pointers to arrays
(*name(args))
To return a function pointer, wrap the function's name and its parameters in a pointer declaration (*name(args)) surrounded by the return type and parameters of the target function.
Lesson 929Returning pointers to functions from functions
(*ptr)
By wrapping (*ptr) in parentheses, you are telling the compiler: "This variable itself is the pointer." Think of it as a protective bubble that ensures the "pointer-ness" attaches to the name before the function arguments do.
Lesson 514Syntax of function pointers
(*ptr).age
You would normally have to "dereference" the pointer first ((*ptr).age), which is clunky to type.
Lesson 239Member access `.` and `->`
(*ptr).health
To change the health through a pointer, you could write (*ptr).health.
Lesson 617Arrow operator vs dot operator
(*ptr).member
Use ptr->member as a readable shortcut for (*ptr).member when working with pointers to structures.
Lesson 616The arrow operator `->` syntaxLesson 618Passing struct pointers to functionsLesson 619Modifying struct members via pointers
(*ptr).speed
Because of how C handles math priority, you would have to write something clunky like (*ptr).speed.
Lesson 616The arrow operator `->` syntax
(A && B)
In an expression like (A && B), C evaluates the conditions from left to right.
Lesson 253Short-circuit evaluation in logical AND
(A && B) || C
When you see (A && B) || C, read it as a story: "The result is true if both A and B happen, OR if C happens on its own." By nesting these logical bricks, you can teach your program to navigate any real-world situation, no matter how many rules are involved.
Lesson 172Building complex logical expressions
(A || B)
In the expression (A || B), the computer evaluates A first.
Lesson 254Short-circuit evaluation in logical OR
(address-of) operator when using
Always use the %d or %f specifiers to match your variable type, and never forget the & (address-of) operator when using scanf.
Lesson 693Scanning integers and floats
(ch = getchar()) != EOF
Notice the line (ch = getchar()) != EOF.
Lesson 682Using while loops with getchar
(char)myInt
Use (int)myChar to find a character's numeric code, or (char)myInt to turn a number back into its symbolic character.
Lesson 131Casting between char and int
(condition)
However, the do-while loop ends with the condition (condition);.
Lesson 274The do-while syntax and the trailing semicolon
(connected) or
When using a 2D array (matrix) to represent our graph, an edge is usually represented by the number 1 (connected) or 0 (not connected).
Lesson 1043Adding edges in undirected graphs
(cookies / people)
In the code above, if C evaluated (cookies / people) when people was 0, the program would crash.
Lesson 170Short-circuit evaluation of `&&`
(double *)
By using (int ) or (double ), we tell C, "Trust me, for this moment, treat this generic address as this specific type." This flexibility is the foundation for advanced C features like generic lists and sorting functions.
Lesson 507The `void *` generic type
(double)(end - start) / CLOCKS_PER_SEC
Use (double)(end - start) / CLOCKS_PER_SEC to turn processor ticks into measurable seconds for performance testing.
Lesson 1180Measuring execution time with `clock()`
(double)my_integer
When you write (double)my_integer, you aren't permanently changing the variable; you are just telling the compiler to treat its value as a double for that specific expression.
Lesson 230The `(type)` cast operator
(double)total_points
In the code above, (double)total_points converts the 15 into 15.0.
Lesson 230The `(type)` cast operator
(double)totalItems
In the example above, (double)totalItems signals your intent.
Lesson 135Readability and intent in casting
(e.g
Instead of using an integer index like i (e.g., str[i]), we can use a pointer to point directly to the current character.
Lesson 481Iterating strings until `\0` with pointers
(false)
In the first example, because engine_running is 0 (false), !engine_running becomes 1 (true).
Lesson 167Logical NOT `!`
(false) and is "raised" to
It typically starts at 0 (false) and is "raised" to 1 (true) when a specific condition is met.
Lesson 315The Flag Variable pattern
(float)
Place (float) before an integer variable during division to prevent C from discarding the decimal remainder.
Lesson 130Forcing floating-point division
(float)sum
By using (float)sum, we tell C to treat the math as a decimal calculation so we don't lose accuracy.
Lesson 403Calculating the sum and average
(float)total_points
In the example above, (float)total_points tells C to treat that 15 as 15.0 just for this specific calculation.
Lesson 129The cast operator `(type)`Lesson 130Forcing floating-point division
(float)totalPoints
In the second example, (float)totalPoints temporarily turns the 5 into 5.0.
Lesson 234Safety with explicit casts
(for
Inside this frame, it stores the value 5 (for n) and space for the result.
Lesson 347What is a Stack Frame?
(gdb)
Once you have started GDB in your terminal (usually by typing gdb ./my_program), you will see a prompt that looks like (gdb).
Lesson 826Starting GDB with an executableLesson 827The `run` and `quit` commands
(greater than) and
Toward the bottom are comparison operators like > (greater than) and == (equal to), and at the very bottom is the assignment operator =.
Lesson 212Operator precedence table
(greater than) or
When you use a relational operator—like > (greater than) or == (equal to)—the computer performs the math and then spits out a 1 if the statement is a fact, or a 0 if it is a lie.
Lesson 164Boolean result of comparisons
(hash << 5) + hash
By adding the original hash to it ((hash << 5) + hash), we effectively multiply by 33.
Lesson 1038String hashing with DJB2
(i - 1) / 2
Their own boss (parent) is at index (i - 1) / 2.
Lesson 1058Heap Sort: Binary heap concept
(index + 1) % SIZE
By using (index + 1) % SIZE, we create a circular loop.
Lesson 1019Modulo operator for circular wrap
(int *)
To "cast away" constness, you tell the compiler: "I know this was marked as constant, but treat it as a regular, modifiable variable for just a moment." We do this by placing the desired pointer type in parentheses, like (int *).
Lesson 499Casting away `const` volatilityLesson 507The `void *` generic typeLesson 509Casting `void *` to specific types
(int)5.5
You aren't converting the number (like casting (int)5.5 to get 5); you are peering at the raw binary 1s and 0s that make up that float.
Lesson 657Using unions for type punning
(int)myChar
While C sometimes performs this conversion automatically (implicit casting), writing (int)myChar tells the world that you are intentionally looking for the numeric "under-the-hood" value.
Lesson 131Casting between char and int
(int)myFloat
You are telling the compiler, "I know what I'm doing; change this data type right now." You perform a cast by placing the desired type in parentheses directly before the value, like (int)myFloat.
Lesson 133Truncation during float-to-int castsLesson 135Readability and intent in castingLesson 234Safety with explicit casts
(int*)
In C++, this "cast" (the (int*) part) is mandatory because C++ is much stricter about types.
Lesson 540Casting malloc return in C vs C++
(multiplication) and
In C, math operators like * (multiplication) and / (division) are "First Class." They have higher precedence than + and -.
Lesson 212Operator precedence table
(n - 1)
In the line return n + sum(n - 1);, the (n - 1) is the magic ingredient.
Lesson 355The Recursive Step
(Not a Number) or
If you try to pass 0 or -1.0 into log(), your program will return a special value called NaN (Not a Number) or -HUGE_VAL, representing negative infinity.
Lesson 862Exponential and logarithmic functions: `exp`, `log`, `log10`
(or
In C, the letter e (or E) stands for "times ten to the power of." It acts like a shortcut for moving the decimal point.
Lesson 100Scientific notation in CLesson 827The `run` and `quit` commands
(or any non-zero number) represents
1 (or any non-zero number) represents True.
Lesson 164Boolean result of comparisons
(PI / 180.0)
C's trigonometric functions require angles in radians, so always multiply degrees by (PI / 180.0) before passing them to sin(), cos(), or tan().
Lesson 863Trigonometric functions in radians
(rand() % range) + min_value
To get a random number within a specific range, use (rand() % range) + min_value.
Lesson 887Scaling `rand` results to a specific range
(total / count)
It never attempts to calculate (total / count).
Lesson 253Short-circuit evaluation in logical AND
(True) if they match and
It looks at the left side and the right side and asks, "Are these currently balanced?" It returns 1 (True) if they match and 0 (False) if they don't.
Lesson 165Common pitfall: `=` vs `==`
(True) or
In C, these operators compare two values and return a result of either 1 (True) or 0 (False).
Lesson 160Greater or equal `>=` and less or equal `<=`
(type *)
To cast a pointer, you put the desired pointer type in parentheses—(type *)—immediately before the pointer variable.
Lesson 134Casting pointers (Introductory look)
(type)
Use (type) to manually convert data, especially to prevent integer division or to acknowledge that you are intentionally narrowing a large value into a smaller type.
Lesson 129The cast operator `(type)`Lesson 230The `(type)` cast operatorLesson 234Safety with explicit casts
(unsigned char)
Always cast your argument to (unsigned char) when calling <ctype.h> functions to avoid undefined behavior caused by negative signed values.
Lesson 846The importance of casting to `unsigned char` in `ctype` functions
(void*)
Note: The (void) in the code above is a small technical requirement to tell printf to treat the address as a generic location.*
Lesson 450Printing addresses with `%p`
(which C interprets as
If the two sides are equal, the expression evaluates to 1 (which C interprets as True).
Lesson 161The equality operator `==`
(which is what
When you use the * operator, you are telling the CPU: "Go to this specific coordinate in memory." If that coordinate is 0 (which is what NULL usually represents), the CPU hits a hardware wall.
Lesson 525Dereferencing the NULL pointer
(x + 1) = 5
For example, if you try to write (x + 1) = 5;, the computer complains because x + 1 is just a temporary result (a letter), not a permanent mailbox where the 5 can live.
Lesson 191L-values vs R-values
(x > y) + z
Using parentheses—(x > y) + z—tells the compiler (and other humans) exactly what you intended.
Lesson 219Common precedence errors
(x)
Think of these parentheses as "safety bubbles." By wrapping (x), you ensure that no matter what complex math the user plugs in—whether it’s a + b or 10 - 2—it stays grouped together as a single unit during the substitution process.
Lesson 773Why parenthesize macro argumentsLesson 974The syntax of a generic selectionLesson 977Type-based function overloading simulationLesson 979Mathematical macros using `_Generic`
* const
A constant pointer (* const) is a fixed arrow: it can change the data it points to, but it can never be reassigned to point to a different memory location.
Lesson 494Constant pointer to a value (`int * const p`)
*(a + i)
In C, a[i] and *(a + i) are functionally identical; the brackets are just "syntactic sugar" to make pointer arithmetic easier to read.
Lesson 473The equivalence of `a[i]` and `*(a + i)`
*(numbers + 0)
numbers[0] is the same as *(numbers + 0)
Lesson 472Accessing arrays with pointer notation
*(numbers + 1)
numbers[1] is the same as *(numbers + 1)
Lesson 472Accessing arrays with pointer notation
*(numbers + 2)
numbers[2] is the same as *(numbers + 2)
Lesson 472Accessing arrays with pointer notation
*(prices + 1)
You might wonder why we would use *(prices + 1) when prices[1] is easier to read.
Lesson 472Accessing arrays with pointer notation
*box
If you change where *box points, you are effectively swapping the map inside the box.
Lesson 502Visualizing pointer chains
*endPtr
If *endPtr is not a null terminator (\0) or a newline, you know there’s "garbage" left over in the string, allowing you to reject the input instead of proceeding with a broken value.
Lesson 872Robust string-to-number conversion with `strtol`
*f
The compiler "remembered" that *f was 1.0 and decided it didn't need to look at the memory again after the integer assignment.
Lesson 936Strict aliasing rule violations
*numbers
Writing *numbers is exactly the same as writing numbers[0].
Lesson 472Accessing arrays with pointer notation
*p
int p creates a pointer variable that stores an address, while p follows that address to access or modify the actual value.
Lesson 456The difference between `int *p` and `*p`
*paper
If you only used one asterisk (*paper), you would be looking at the memory address of the box, not the gold itself.
Lesson 505Accessing data through double dereference
*pointer_name = new_value
When you want to assign a new value to a variable through a pointer, you follow a specific syntax: *pointer_name = new_value;.
Lesson 458Assigning values via pointers
*ptr = 100
If you compile this with debugging symbols (-g) and it crashes, loading the core dump in GDB will point directly to the line *ptr = 100;.
Lesson 837Debugging a Segfault from a core dump
*ptr = 20
In the example above, *ptr = 20 doesn't change the address stored in ptr.
Lesson 457The Dereference operator `*`
*ptr = 25
In the example above, *ptr = 25 doesn't change the address stored in the pointer; it reaches through the pointer and changes the cookies variable to 25.
Lesson 238The Indirection operator `*`
*ptr = value
Before you use a pointer to write data (*ptr = value), ensure the address it currently holds is still within the original array's starting and ending memory addresses.
Lesson 524Buffer overflows via pointers
*ptr.member
You might wonder why we don't just use *ptr.member.
Lesson 619Modifying struct members via pointers
*ptrToPtr
The Modification: Inside the function, ptrToPtr means "go to the box located at this address." The assignment ptrToPtr = secretMessage puts a new address inside that box.
Lesson 500Concept of double indirectionLesson 503Modifying a pointer inside a function
*ptrToPtr = secretMessage
The Modification: Inside the function, ptrToPtr means "go to the box located at this address." The assignment ptrToPtr = secretMessage puts a new address inside that box.
Lesson 503Modifying a pointer inside a function
*result
If you try to do *result, the compiler will complain because it doesn't know how many bytes to read.
Lesson 881Handling the `void*` return of `bsearch`
*src
Dereferencing: *src looks at the actual character at the current memory address.
Lesson 482Pointer-based `strcpy` implementation
/ fall-through /
If you intentionally leave it out to group cases, it is a good habit to add a comment like / fall-through / so other programmers know it wasn't a mistake.
Lesson 261Fall-through behavior: intentional and accidental
/ MYGAME_ENTITIES_PLAYER_STATS_H /
Comment the End: Notice the comment / MYGAME_ENTITIES_PLAYER_STATS_H / after the #endif.
Lesson 1203Header guard best practices
// This prints Hello
Writing // This prints Hello next to a print command is like putting a label on a toaster that says "Toaster." It’s redundant.
Lesson 33Single-line comments `//`
/include
As your C programs grow from a single file into a collection of multiple .c and .h files, your project folder can quickly become a cluttered "junk drawer." To stay organized, professional C developers use a standardized folder structure: they put logic in a /src folder and definitions in an /include folder.
Lesson 800Organizing /src and /include folders
/libs
Imagine you have a project where your library is stored in a folder named /libs and the file is named libphysics.so.
Lesson 815Linking with shared libraries `-l` and `-L`
/src
As your C programs grow from a single file into a collection of multiple .c and .h files, your project folder can quickly become a cluttered "junk drawer." To stay organized, professional C developers use a standardized folder structure: they put logic in a /src folder and definitions in an /include folder.
Lesson 800Organizing /src and /include folders
/src (source)
The /src (source) folder is the stove area.
Lesson 800Organizing /src and /include folders
/tmp/fileA3bZ
When you call tmpnam(filename), the function fills your character array with a string like /tmp/fileA3bZ.
Lesson 759Generating temp filenames with tmpnam
/tmp/my_data.txt
If your program always creates a temporary file named /tmp/my_data.txt, a malicious user can create a "symbolic link" with that exact name before your program runs.
Lesson 763Temporary file security risks
/tmp/my_messenger
Once you run this code, you will see a new file at /tmp/my_messenger.
Lesson 1107Creating FIFOs with mkfifo()
/usr/include
The compiler looks exclusively in "System Folders"—pre-defined directories on your hard drive where C libraries are installed (like /usr/include on Linux).
Lesson 795Standard header search paths
/usr/lib
If the spice is in a standard cupboard like /usr/lib, the chef finds it instantly.
Lesson 815Linking with shared libraries `-l` and `-L`Lesson 816Runtime library loading and `LD_LIBRARY_PATH`
\n
Progress Bars or Prompts: If you print a message like "Loading..." without a \n because you want the percentage to appear on the same line, the user might see nothing but a blank screen for seconds because the text is stuck in the buffer.
Lesson 44Newline character `\n`Lesson 45Horizontal tab `\t`Lesson 47Escaping the backslashLesson 108Escape sequences like `\n` and `\t`Lesson 300Using nested loops to print 2D gridsLesson 301Nested loops for multiplication tablesLesson 412Printing a 2D matrix to the consoleLesson 434Removing newlines from `fgets` resultsLesson 691The return value of printfLesson 694Reading characters with ' %c' spacingLesson 697How scanf leaves trailing newlinesLesson 698Using scansets with %[...]Lesson 700Printing strings with putsLesson 701Reading safe strings with fgetsLesson 702Removing the newline from fgetsLesson 717String I/is with fgets and fputsLesson 724Text mode vs Binary mode (b flag)Lesson 739Risks of seeking in text modeLesson 748How C buffers I/O for speedLesson 749Full buffering vs Line bufferingLesson 751Forcing a write with fflushLesson 752Setting custom buffers with setvbufLesson 755When to use fflush(stdout)Lesson 842Identifying whitespace with `isspace`Lesson 946Handling line endings across OSsLesson 1153Using `fgets()` instead of `scanf()` for stringsLesson 1158Flushing `stdout` for accurate logsLesson 1206Opening and reading files line-by-line
\n\n
You can even use multiple \n\n in a row to create a blank line between your text, just like hitting the Enter key twice in a word processor.
Lesson 44Newline character `\n`
\r
In C, "whitespace" isn't just the spacebar key; it is a category that includes horizontal tabs (\t), newlines (\n), carriage returns (\r), and vertical tabs.
Lesson 842Identifying whitespace with `isspace`Lesson 946Handling line endings across OSs
\r\n
When writing: Even if you just write \n, the C library will automatically convert it to \r\n if it detects it is running on a Windows system.
Lesson 739Risks of seeking in text modeLesson 946Handling line endings across OSs
\t
Instead of just printing a single space, \t tells the computer to jump the cursor forward to the next "tab stop." Think of it like a train that doesn't stop at every single house along the track, but only at specific stations spaced at equal intervals.
Lesson 45Horizontal tab `\t`Lesson 108Escape sequences like `\n` and `\t`Lesson 820The importance of Tab charactersLesson 842Identifying whitespace with `isspace`
\t- Milk\n
In the example above, \t- Milk\n tells the computer: "Indent, print a dash and the word Milk, then jump to a new line." Without these sequences, all your output would be crammed together in one long, unreadable string.
Lesson 108Escape sequences like `\n` and `\t`
\W
If you type printf("C:\Windows");, C will see the \W and wonder what secret command you are trying to trigger.
Lesson 47Escaping the backslash
&age
If age is the person, &age is their home address.
Lesson 237The Address-of operator `&`
&arrayName[i]
To fill an array, use a for loop where the counter matches the array index, passing &arrayName[i] to scanf to store each input.
Lesson 390Reading array values from user input
&i
If you pass the address of a loop counter (&i) and the loop continues to increment, the thread might read the "new" value of i before it even starts.
Lesson 924The `register` keyword and its modern relevanceLesson 1118Passing arguments to threads
&numbers
When using the address-of operator: &numbers provides a pointer to the entire array, not just the first element.
Lesson 475Array decay explained
&p
Just as you pass a variable's address (&x) to a function if you want to change its value, you must pass a pointer's address (&p) if you want to change which memory location that pointer is looking at.
Lesson 501Declaring `int **pp`
&ptr
To change where a pointer points from inside a function, you must pass the address of that pointer (&ptr) and receive it as a double pointer (ptr).
Lesson 503Modifying a pointer inside a function
&sayHello
Whether you use sayHello or the explicit "address of" operator &sayHello, C treats them as the same thing: a pointer to the code.
Lesson 515Taking the address of a function
&scores[i]
Think of scores[i] as the box itself, while &scores[i] is the GPS coordinate of that box.
Lesson 390Reading array values from user input
&var
To let a function modify a variable from the caller, pass the variable's address (&var) and use the dereference operator (*ptr) inside the function to update it.
Lesson 488Modifying caller variables
&variable
By passing addresses (&variable) to a function, you allow that function to modify multiple variables in the caller's memory, effectively "returning" as many values as you need.
Lesson 487Passing addresses to functionsLesson 489Returning multiple values via pointers
&x
Just as you pass a variable's address (&x) to a function if you want to change its value, you must pass a pointer's address (&p) if you want to change which memory location that pointer is looking at.
Lesson 501Declaring `int **pp`
#0000FF
You could describe a deep blue as "red: 0, green: 0, blue: 255," but programmers often prefer the shorthand #0000FF.
Lesson 689Printing hex and octal values
#define DEBUG
By typing -DDEBUG in your terminal, you are telling the compiler, "For this specific build, act as if #define DEBUG is written at the top of every file."
Lesson 786Feature toggles via command line `-D`
#define GREETING
Notice that #define GREETING is gone, and the word GREETING has been physically replaced by its value.
Lesson 767How `gcc -E` shows preprocessor output
#define NAME VALUE
Use #define NAME VALUE at the top of your code to create easy-to-read, unchangeable labels for fixed numbers or text.
Lesson 115Defining constants with `#define`
#define NDEBUG
In real-world projects, programmers rarely type #define NDEBUG directly into their C files.
Lesson 902Disabling assertions with `NDEBUG`
#define PI 3.14
This works fine for simple shortcuts like #define PI 3.14, but what happens when you want a macro that performs a complex calculation or checks multiple conditions?
Lesson 772Defining function-like macrosLesson 779Multi-line macros with backslashes
#define SQUARE(x) (x * x)
If you define #define SQUARE(x) (x * x), the preprocessor literally rewrites your code before the compiler even sees it.
Lesson 778Macros vs inline functions
#define SQUARE(x) x * x
Because macros are just text substitution, they can be "tricky." If you defined it as #define SQUARE(x) x x and then wrote SQUARE(2 + 2), the preprocessor would give you 2 + 2 2 + 2.
Lesson 772Defining function-like macros
#define STRING char*
If you write #define STRING char*, and then try to declare two strings on one line like this:
Lesson 613Typedef vs. #define macros
#elif
The defined() operator is a more powerful tool that lives inside an #if or #elif statement.
Lesson 782The `defined()` operator
#else
Conditional compilation uses #if and #else to physically include or exclude code from your program before the actual compilation begins.
Lesson 781Basic logic with `#if` and `#else`
#error
It tells the preprocessor: "If you reach this line, stop everything, display a specific message, and fail the build." Unlike a standard printf which happens while your program is running, #error happens while your program is being created.
Lesson 784Using `#error` to stop compilation
#FF5733
You will rarely use Octal in modern programming, but you will use Hex and Binary constantly when working with colors (like #FF5733), memory addresses, or hardware pins.
Lesson 119Integer literals (Hex, Octal, Binary)
#if
Size: If you have a massive chunk of code meant only for testing, using #if ensures that code isn't shipped to your customers, making your final file smaller and more secure.
Lesson 781Basic logic with `#if` and `#else`Lesson 782The `defined()` operatorLesson 784Using `#error` to stop compilationLesson 785Temporary code disabling with `#if 0`
#if 0
Use #if 0 and #endif to safely disable blocks of code that contain nested comments or complex logic.
Lesson 785Temporary code disabling with `#if 0`
#if defined
Inline assembly is platform-locked; always use preprocessor guards (#if defined) to provide specific implementations for different CPU architectures.
Lesson 963Platform-specific assembly (x86 vs ARM)
#ifdef
Use #ifdef and platform macros to ensure your code runs smoothly across different operating systems without writing multiple versions of the same program.
Lesson 780Using `#ifdef` and `#ifndef`Lesson 782The `defined()` operatorLesson 783Testing for platform-specific codeLesson 786Feature toggles via command line `-D`Lesson 787Managing debug prints with macros
#ifdef NAME
Include the following code only if NAME has been defined.
Lesson 780Using `#ifdef` and `#ifndef`
#ifndef
If the compiler encounters this file again in the same run, the #ifndef check fails (because the name now exists), and the compiler skips straight to the #endif, preventing any duplicate definitions.
Lesson 380Header Guards: `#ifndef` and `#define`Lesson 780Using `#ifdef` and `#ifndef`Lesson 784Using `#error` to stop compilationLesson 791How `#pragma once` worksLesson 925Common linkage errors and 'multiple definition'
#ifndef HEADER_H
In professional code, a simple #ifndef HEADER_H isn't enough.
Lesson 1203Header guard best practices
#ifndef NAME
Include the following code only if NAME has not been defined.
Lesson 780Using `#ifdef` and `#ifndef`
#ifndef PLAYER_H
If the compiler encounters player.h again later in the same build process, it hits the #ifndef PLAYER_H line.
Lesson 790Creating basic include guards
#include "enemy.h"
You only need to perform the actual #include "enemy.h" inside your .c source file, where you finally need to access the enemy's specific fields (like target->health).
Lesson 792Forward declarations in headers
#include "file_a.h"
In C, a circular dependency happens when file_a.h contains #include "file_b.h", but file_b.h also contains #include "file_a.h".
Lesson 794Circular dependency issues
#include "file_b.h"
In C, a circular dependency happens when file_a.h contains #include "file_b.h", but file_b.h also contains #include "file_a.h".
Lesson 794Circular dependency issues
#include "filename.h"
Use #include "filename.h" for files you created in your project folder, and #include <filename.h> for standard built-in libraries.
Lesson 379Using `#include` with quotesLesson 766The `#include` directive for local files
#include "math_utils.h"
To use this in your main program, you simply #include "math_utils.h".
Lesson 378Separating interface from implementation
#include "my_functions.h"
To tell the compiler to look in your current folder first, you swap the angle brackets for double quotes: #include "my_functions.h".
Lesson 379Using `#include` with quotes
#include "my_header.h"
This is like putting a .h file in the same folder as your .c file; you simply write #include "my_header.h" and the compiler finds it.
Lesson 63Header search paths
#include "my_settings.h"
When it sees #include "my_settings.h", it pauses, opens your file named my_settings.h, copies everything inside it, and pastes it directly into your main file at that exact spot before the compiler even starts reading the code.
Lesson 63Header search pathsLesson 766The `#include` directive for local files
#include "player.h"
When you use #include "player.h", the preprocessor literally copies and pastes the entire content of that file into your code.
Lesson 789The 'duplicate definition' error
#include "tools.h"
When you include a header with #include "tools.h", you are telling the compiler: "I'm going to use a function defined elsewhere; trust me, it exists."
Lesson 382Sharing functions across modules
#include <ctype.h>
Use isupper(c) to check for capital letters and islower(c) to check for small letters, ensuring you #include <ctype.h> first.
Lesson 840Testing for alphabetic characters with `isalpha`Lesson 842Identifying whitespace with `isspace`Lesson 844Case testing with `isupper` and `islower`
#include <filename.h>
Use #include "filename.h" for files you created in your project folder, and #include <filename.h> for standard built-in libraries.
Lesson 379Using `#include` with quotes
#include <header.h>
The #include <header.h> directive tells the preprocessor to copy-paste standard library definitions into your file so you can use built-in functions.
Lesson 765The `#include` directive for standard headers
#include <stdbool.h>
You no longer need to remember #include <stdbool.h> at the top of every file just to perform simple logic.
Lesson 969C23: The `bool`, `true`, and `false` keywords
#include <stdlib.h>
Always include #include <stdlib.h> at the top of your program to unlock the functions required for dynamic memory allocation and deallocation.
Lesson 536Header file stdlib.h for allocation
#pragma once
Even if ten different files in your project include player.h, the #pragma once directive ensures the struct Player definition is only processed once.
Lesson 791How `#pragma once` works
#pragma pack
Using #pragma pack is like vacuum-sealing your clothes: you remove all the air to make everything fit in a specific, tight order.
Lesson 948The `#pragma pack` directive
#pragma pack(1)
Use #pragma pack(1) to remove invisible padding bytes when your struct must match a specific external data format or hardware register.
Lesson 948The `#pragma pack` directive
#undef
If you only need a macro for a tiny section of code and want to prevent it from accidentally messing up other parts of your program, you "wrap" your code with a #define at the top and an #undef at the bottom.
Lesson 769Removing definitions with `#undef`
#x
If you pass PRINT_INT(10 + 5), the #x will literally become the string "10 + 5".
Lesson 776The stringizing operator `#`
%-10s
In the second block, the %-10s flag ensures "Apples" starts immediately after the pipe character, making the list much easier to read—just like a professionally printed receipt or a table in a book.
Lesson 686Left-aligning with the minus flag
%.0f
%.0f means "show no decimal places" (rounding to the nearest whole number).
Lesson 102Formatting decimals with `%.nf`Lesson 687Precision for floating-point numbers
%.1f
%.1f shows one decimal place (e.g., 3.5)
Lesson 102Formatting decimals with `%.nf`
%.2f
If you print 3.148 using %.2f, the screen will show 3.15, but the variable inside the computer's memory still holds the precise 3.148 for future calculations.
Lesson 102Formatting decimals with `%.nf`Lesson 687Precision for floating-point numbers
%.4f
If you use %.4f, it sees the 9 and rounds the previous digit up, giving you 3.1416.
Lesson 687Precision for floating-point numbers
%.nf
Use %.Nf (where N is a number) to round and limit floating-point output to a specific number of decimal places.
Lesson 102Formatting decimals with `%.nf`Lesson 687Precision for floating-point numbers
%[a-z]
You can also use ranges, like %[a-z] to only accept lowercase letters, or %[0-9] for digits.
Lesson 698Using scansets with %[...]
%[abcd]
For example, %[abcd] will only accept the letters a, b, c, or d.
Lesson 698Using scansets with %[...]
%#x
Using %#x would turn ff into 0xff, which is the standard way programmers write hexadecimal in their code.
Lesson 689Printing hex and octal values
%02d
It will never cut off your data; if you try to print 100 with %02d, C will still print all three digits because the data itself is always prioritized over the padding.
Lesson 688Zero-padding numerical output
%03d
For example, %03d means "format this integer to be 3 digits wide, padding with zeros if necessary."
Lesson 688Zero-padding numerical output
%04d
If you change a %d to a %04d, fprintf will even add leading zeros for you, making your file output look professional and consistent.
Lesson 718Formatted file output with fprintf
%0nd
Use %0nd (where n is the width) to add leading zeros and keep your numerical output perfectly aligned.
Lesson 688Zero-padding numerical output
%10d
By default, when you tell printf to reserve a specific amount of space for a value (like %10d), it acts like a shy person in an elevator.
Lesson 686Left-aligning with the minus flag
%19s
While you can limit this by using %19s (telling it to stop after 19 characters to leave room for the null terminator), standard scanf is generally considered unsafe for professional applications where users might enter unpredictable text.
Lesson 422Scanning strings with `%s` and `scanf` limitations
%49[^\n]
In the code above, %49[^\n] does two important jobs:
Lesson 698Using scansets with %[...]
%5d
If the number you are printing is smaller than the width (like the number 42 in a %5d spot), C will pad the left side with empty spaces.
Lesson 56Basic field width formattingLesson 685Specifying field width for alignment
%9s
Always use a width limit (like %9s) with scanf to ensure the input never exceeds the size of your character array.
Lesson 438Handling the `scanf` buffer overflowLesson 695Limiting string length in scanf
%d
Printing just %d is confusing if you have five different variables; printing count: %d tells you exactly what you are looking at.
Lesson 48The percent sign `%%` literalLesson 49Introduction to Format SpecifiersLesson 50Printing integers with `%d`Lesson 51Printing characters with `%c`Lesson 52Printing decimals with `%f`Lesson 53The `%s` specifier for stringsLesson 54Multiple specifiers in one lineLesson 55Argument-specifier matchingLesson 87Printing integers with `%d` and `%ld`Lesson 90The `unsigned` keywordLesson 94Format specifiers for unsigned intsLesson 96The `<limits.h>` header fileLesson 109The ASCII encoding schemeLesson 112Printing chars with `%c`Lesson 164Boolean result of comparisonsLesson 376How `printf` works internallyLesson 412Printing a 2D matrix to the consoleLesson 421Printing strings with `%s` and `printf`Lesson 422Scanning strings with `%s` and `scanf` limitationsLesson 450Printing addresses with `%p`Lesson 679Basic character output with putcharLesson 684Format specifiers recapLesson 688Zero-padding numerical outputLesson 689Printing hex and octal valuesLesson 690Escaping the percent sign %%Lesson 693Scanning integers and floatsLesson 700Printing strings with putsLesson 718Formatted file output with fprintfLesson 719Formatted file input with fscanfLesson 894Formatting time strings with `strftime`Lesson 975Implementing a generic 'Print' macroLesson 1163Inspecting variable values at runtime
%e
In the code above, %f prints the number in standard decimal format, while %e tells C to print the output back in scientific notation.
Lesson 100Scientific notation in C
%hd
Notice that we use different "format specifiers" like %hd and %ld to tell the printf function which size we are using.
Lesson 82Short vs. Long integers
%ld
Notice that we use different "format specifiers" like %hd and %ld to tell the printf function which size we are using.
Lesson 82Short vs. Long integersLesson 87Printing integers with `%d` and `%ld`Lesson 735Getting current position with ftell
%Lf
Similarly, when printing the value using printf, you use the %Lf specifier (the capital 'L' is critical).
Lesson 99The `long double` type
%lld
Use long long and the %lld specifier when your whole numbers exceed 2 billion.
Lesson 83The `long long` type
%o
If you have the number 255, the %d filter shows you "255." The %x filter shows you "ff," and the %o filter shows you "377."
Lesson 689Printing hex and octal values
%u
To see these values, you simply include the header at the top of your file and print the constants using the %d (for signed) or %u (for unsigned) format specifiers.
Lesson 90The `unsigned` keywordLesson 94Format specifiers for unsigned intsLesson 96The `<limits.h>` header file
%x
If you have the number 255, the %d filter shows you "255." The %x filter shows you "ff," and the %o filter shows you "377."
Lesson 689Printing hex and octal values
%zu
You might notice the %zu formatter in the code above.
Lesson 847Finding string length with `strlen`
<angle brackets>
We use quotes for files we created ourselves, while <angle brackets> are reserved for built-in system libraries like stdio.h.
Lesson 382Sharing functions across modules
<assert.h>
Defining NDEBUG before including <assert.h> disables all assertions, allowing your final production code to run at maximum speed without safety checks.
Lesson 901Using `assert` for internal debuggingLesson 902Disabling assertions with `NDEBUG`Lesson 1172Principles of Unit TestingLesson 1173Writing a simple `assert()` check
<errno.h>
Most Standard Library functions use a simple return value (like -1 or NULL) to signal that something went wrong, while errno (a global variable found in <errno.h>) stores a specific code explaining what went wrong.
Lesson 742The strerror functionLesson 745Handling 'Permission Denied' errorsLesson 897The global `errno` variableLesson 903When to use `errno` vs return codes
<file>
The difference between <file> and "file" tells the preprocessor where to look on your computer's "map":
Lesson 766The `#include` directive for local files
<float.h>
The <float.h> header provides predefined constants that reveal the maximum values and decimal precision of your computer's floating-point hardware.
Lesson 103The `<float.h>` header fileLesson 907Precision and epsilon in `float.h`Lesson 908Checking `FLT_DIG` and `DBL_DIG` for precision limits
<my_file.h>
If you try to include your own file with <my_file.h>, the compiler will skip your project folder entirely and search only the system libraries, failing to find your code.
Lesson 795Standard header search paths
<netinet/in.h>
This structure is defined in the <netinet/in.h> header.
Lesson 1109The sockaddr_in structure
<signal.h>
You tell the operating system to use your function instead of the default behavior by using the signal() function from the <signal.h> library.
Lesson 1087Basic signal handling with signal()
<stdalign.h>
Note: While the keyword is _Alignof, including <stdalign.h> allows you to use the cleaner, lowercase alignof.
Lesson 453Determining variable alignment in memoryLesson 572The alignof operator
<stdarg.h>
Use va_list, va_start, va_arg, and va_end from <stdarg.h> to process functions with a variable number of arguments indicated by the ... ellipsis.
Lesson 371Functions with unknown argumentsLesson 372The `stdarg.h` libraryLesson 373Using `va_list` and `va_start`Lesson 376How `printf` works internally
<stdbool.h>
If you wanted to make your code readable, you had to manually define constants or include <stdbool.h> (introduced in C99), which gave you bool as a macro for the slightly clunky _Bool type.
Lesson 271Infinite loops: while(1) and while(true)Lesson 966C99: Variable declarations and `bool`Lesson 969C23: The `bool`, `true`, and `false` keywords
<stddef.h>
Instead, use the headers provided by the C standard, like <stdint.h> and <stddef.h>.
Lesson 949Writing code for 32-bit vs 64-bit
<stdint.h>
If you absolutely need a variable that is exactly 32 bits regardless of the computer it runs on, advanced C programmers use specific headers like <stdint.h>, but understanding the fluid nature of the basic int is the first step toward writing software that works everywhere.
Lesson 86Fixed-width types from `<stdint.h>`Lesson 942Limits of `limits.h` and `stdint.h`Lesson 944Sizes of `int` across different architecturesLesson 945The significance of `char` signnessLesson 949Writing code for 32-bit vs 64-bit
<sys/socket.h>
To create one, we use the socket() function from the <sys/socket.h> library:
Lesson 1110Creating a socket with socket()
<threads.h>
C11 introduced <threads.h>, providing a standardized way to write concurrent code.
Lesson 967C11: Multi-threading and Anonymous structures
<unistd.h>
The access() function lives in the <unistd.h> library.
Lesson 762Checking if a file existsLesson 1067What is a process ID (PID)
0 to (Size - 1)
Because C won't stop you from crossing the boundary, you must build your own "fence." Before you ever access an array using a variable as an index, you must verify that the index is within the valid range: 0 to (Size - 1).
Lesson 437Checking bounds before array access
0.7 or 0.75
To keep operations lightning-fast, we set a threshold—usually a load factor of 0.7 or 0.75.
Lesson 1037Load factor and rehashing
0b
Use 0x for Hexadecimal, 0b for Binary, and a leading 0 for Octal to write numbers in the format that best fits your specific problem.
Lesson 119Integer literals (Hex, Octal, Binary)
0b1010
Using 0b1010 is much more intuitive for a "pattern" of switches than writing 10.
Lesson 119Integer literals (Hex, Octal, Binary)
0x
Hexadecimal (0x) is a shorthand that makes long binary memory addresses readable for humans while staying mathematically aligned with how hardware functions.
Lesson 119Integer literals (Hex, Octal, Binary)Lesson 451Hexadecimal notation for memoryLesson 689Printing hex and octal values
0x0
You’ll see it was 0x0, confirming why the crash happened.
Lesson 837Debugging a Segfault from a core dump
0x00
The three 0x00 bytes that follow are the rest of that 4-byte integer.
Lesson 839Examining raw memory with `x`
0x00000001
If the number is 0x00000001, where does that 1 go?
Lesson 951Checking system endianness at runtime
0x0000FF00
In this example, 0x0000FF00 is our "stained glass." It blocks out the Red, Blue, and Alpha values.
Lesson 955Using masks for cross-platform bit logic
0x0001
If a Little-Endian computer sends the integer 0x0001 to a Big-Endian computer without any changes, the receiver might interpret it as 0x0100.
Lesson 952Network byte order and `htons`/`ntohs`
0x0100
If a Little-Endian computer sends the integer 0x0001 to a Big-Endian computer without any changes, the receiver might interpret it as 0x0100.
Lesson 952Network byte order and `htons`/`ntohs`
0x0A
If the C library tries to "fix" line endings in a binary file, it will corrupt the data because the byte 0x0A might be part of a pixel color, not a new line.
Lesson 946Handling line endings across OSs
0x0a 0x00 0x00 0x00
You might see output like 0x0a 0x00 0x00 0x00.
Lesson 839Examining raw memory with `x`
0x10
In C, we prefix hexadecimal numbers with 0x to tell the compiler, "Hey, don't read this as a decimal!" For example, 0x10 isn't ten; it's sixteen.
Lesson 451Hexadecimal notation for memory
0x1000
If two libraries both demanded to live at memory address 0x1000, your system would crash.
Lesson 814Position Independent Code `-fPIC`
0x12
Big Endian: Stores the "most significant byte" (0x12) at the lowest memory address.
Lesson 950Big Endian vs Little Endian explained
0x12345678
If a Little Endian computer sends 0x12345678 to a Big Endian computer without converting it, the second computer will read the bytes in reverse and think the number is 0x78563412!
Lesson 730Endianness and binary portabilityLesson 950Big Endian vs Little Endian explained
0x40021000
Here is how we map a pointer to a specific hardware address (e.g., 0x40021000) and ensure the compiler treats it correctly.
Lesson 913The `volatile` qualifier for hardware mapping
0x4005d1
Without -g, if your program crashes, the computer can only tell you it crashed at a cryptic memory address like 0x4005d1.
Lesson 1160Compiling with `-g` for debug symbols
0x56
Because we used a logical mask rather than a memory pointer, this code will extract 0x56 correctly on every single computer in existence, regardless of its endianness.
Lesson 955Using masks for cross-platform bit logic
0x78
Little Endian: Stores the "least significant byte" (0x78) at the lowest memory address.
Lesson 950Big Endian vs Little Endian explained
0x78563412
If a Little Endian computer sends 0x12345678 to a Big Endian computer without converting it, the second computer will read the bytes in reverse and think the number is 0x78563412!
Lesson 730Endianness and binary portabilityLesson 950Big Endian vs Little Endian explained
0x7ff
Printing ptr shows you a messy memory address (like 0x7ff...).
Lesson 238The Indirection operator `*`
0x7ff7bfeff4a8
When you run this, you’ll see an output like 0x7ff7bfeff4a8.
Lesson 451Hexadecimal notation for memory
0x7ffcc822
Instead, it will look like a strange string of numbers and letters, such as 0x7ffcc822.
Lesson 450Printing addresses with `%p`
0x7ffd5e32
If you run this code, you will see a strange-looking code like 0x7ffd5e32.
Lesson 80Variables in memory addresses
0x7ffe
It tells the debugger: "Hey, this chunk of machine code at address 0x7ffe actually belongs to the variable userAge on line 12 of main.c."
Lesson 825Compiling with debug symbols `-g`
0x7ffee45b8
When you run this, you’ll see the number 12, followed by a complex code like 0x7ffee45b8.
Lesson 449The Address-of operator `&`
0x7ffee495
When you run this, you will see a hexadecimal number (like 0x7ffee495).
Lesson 237The Address-of operator `&`
0x7ffeed
The p stands for "pointer," and it tells C to print a memory address, which usually looks like a strange string of numbers and letters (hexadecimal), such as 0x7ffeed.
Lesson 449The Address-of operator `&`
0x7ffeed3c
When you run this, you will see a strange-looking code like 0x7ffeed3c.
Lesson 448How variables are stored in RAM
0xff
Imagine you have a data buffer and you need to find where a specific "header" byte (like 0xFF) starts so you can process the data following it.
Lesson 689Printing hex and octal valuesLesson 859Searching memory bytes with `memchr`
0xFF000000
In this code, 0xFF000000 acts like a spotlight, highlighting only the first 8 bits.
Lesson 953Manual byte swapping techniques
1 << exponent
While 1 << exponent is a clever trick, using it everywhere before you know if your program is actually slow makes your code a minefield for other developers.
Lesson 1187The trap of premature optimization
1 | anything
Because 1 | anything is always 1, we use this operator to "set" bits (turn them to 1).
Lesson 182Setting bits with `|`
1.0e-4
1.0e-4 means $1.0 \times 10^{-4}$ (which is $0.0001$)
Lesson 100Scientific notation in C
1.2e3
Unlike simpler functions, strtod handles scientific notation (like 1.2e3) and tells you exactly where the "non-numeric" part of a string begins.
Lesson 873Converting strings to doubles with `strtod`
10 * sizeof(int)
Instead of asking for "40 bytes," we ask for 10 * sizeof(int).
Lesson 538Calculating size with sizeof
100 / x
C stops there and never performs the 100 / x calculation.
Lesson 171Short-circuit evaluation of `||`
11 bytes of internal fragmentation
If a user asks for 5 bytes, but your allocator only hands out 16-byte chunks, you have 11 bytes of internal fragmentation.
Lesson 582Internal vs External fragmentation
12 bytes
Even though you only defined 6 bytes of data (1 + 4 + 1), sizeof(struct FastFood) will likely return 12 bytes.
Lesson 638Understanding memory alignmentLesson 640Structure holes and performance
12abc
The result of a paste must be a valid C "token." If you try to glue 12 and abc to get 12abc, your compiler will complain because a variable name cannot start with a number.
Lesson 777The token-pasting operator `##`
1player
Start with a letter: You can use numbers inside the name, but never at the start (e.g., player1 is fine, but 1player will cause an error).
Lesson 75The syntax of a declaration
2.0f
In the code above, the computer stores 2.0f in memory.
Lesson 657Using unions for type punning
2.5e3
2.5e3 means $2.5 \times 10^3$ (which is $2500$)
Lesson 100Scientific notation in C
20 steps
For a sorted list of one million items, Binary Search will find your target (or prove it isn't there) in just about 20 steps.
Lesson 1062Binary Search: Iterative approach
2D array
Use a 2D array if you need to modify the characters frequently and you know all your strings will be roughly the same length.
Lesson 414Introduction to 3D and higher arraysLesson 483Array of strings vs 2D char array
2D char array
Think of a 2D char array as a structured parking lot.
Lesson 483Array of strings vs 2D char array
2i + 1
Their left child is always at index 2i + 1.
Lesson 1058Heap Sort: Binary heap concept
2i + 2
Their right child is always at index 2i + 2.
Lesson 1058Heap Sort: Binary heap concept
2nd_Place
While you might be tempted to name a variable Total $ Amount or 2nd_Place, the C compiler will throw an error.
Lesson 74Naming rules and identifiers
2points
For example, points2 is fine, but 2points is illegal.
Lesson 74Naming rules and identifiers
3.14f
When you assigned 3.14f to u.decimal, the program went to the union's memory address and wrote the binary pattern for that float.
Lesson 655Accessing union members
3.4028e+38
Instead of hard-coding a limit like 3.4028e+38, you use FLT_MAX, and your program will automatically adapt if it is ever moved to a different type of processor.
Lesson 103The `<float.h>` header file
32-bit system
On a 32-bit system, every pointer is typically 4 bytes.
Lesson 452The size of a pointer variable
3D array
A 3D array is the entire book—a collection of multiple pages.
Lesson 414Introduction to 3D and higher arrays
3D arrays
This is where 3D arrays (and beyond) come into play.
Lesson 414Introduction to 3D and higher arrays
3x3
When you work with a 2D array, like a 3x3 grid, it is helpful to think of it as a spreadsheet.
Lesson 413Summing rows and columns individually
4 bytes
On a 32-bit system, every pointer is typically 4 bytes.
Lesson 452The size of a pointer variable
4096 bytes (4 KB)
In most modern systems, a single page is 4096 bytes (4 KB).
Lesson 597Virtual memory pages and offsets
4D array
A 4D array could be thought of as a shelf of books.
Lesson 414Introduction to 3D and higher arrays
5 + -10u
This conversion happens automatically if you mix types in math (like 5 + -10u).
Lesson 232Signed to unsigned conversion
5 cards
If you have 5 cards, you can sort them in seconds.
Lesson 1050Time complexity of O(n^2) sorts
50 cards
If you have 50 cards, it takes significantly longer, but you can still do it at your desk.
Lesson 1050Time complexity of O(n^2) sorts
5D array
A 5D array could be a room full of shelves, and so on.
Lesson 414Introduction to 3D and higher arrays
64-bit system
On a 64-bit system, every pointer is typically 8 bytes.
Lesson 452The size of a pointer variable
7e7
Sometimes, looking at a raw hex number like 7e7 is confusing—is that a word or a number?
Lesson 689Printing hex and octal values
8 bytes
Even though a char is 1 byte and an int is 4 bytes, sizeof(Resource) will likely return 8 bytes, not 5.
Lesson 452The size of a pointer variableLesson 573Struct padding for alignment
9 characters
If you tell fgets the limit is 10, it will read at most 9 characters from the user, then add the \0 at the end to keep things tidy.
Lesson 706The buffer size argument in fgets

A

A && B
When C evaluates A && B, it follows a two-step process:
Lesson 223Sequence points in logic `&&` and `||`
a + b
Think of these parentheses as "safety bubbles." By wrapping (x), you ensure that no matter what complex math the user plugs in—whether it’s a + b or 10 - 2—it stays grouped together as a single unit during the substitution process.
Lesson 773Why parenthesize macro argumentsLesson 905Using `INT_MAX` and `INT_MIN` for overflow checks
a + b * c
In the expression a + b c, precedence tells the compiler: "Group b and c together for multiplication, then add the result to a." It defines the mathematical grouping, not the timing*.
Lesson 227Order of evaluation vs Precedence
a + b > MAX
Instead of checking if a + b > MAX, which might overflow during the check itself, we check if a > MAX - b.
Lesson 1154Safe integer arithmetic and overflow checks
a <= b
Is a smaller than b OR exactly the same as b?
Lesson 160Greater or equal `>=` and less or equal `<=`
a = b
In C, when you work with basic variables like integers, copying is easy: a = b;.
Lesson 604Copying structs with the assignment operator
a = b = 5
In the expression a = b = 5;, the computer doesn't look at a first.
Lesson 196Chained assignments `a = b = c`
a == b
In the example above, a == b would be false because 0.1 + 0.2 results in a value slightly higher than 0.3 deep in the decimal places.
Lesson 166Comparing floating-point numbers
a > MAX - b
Instead of checking if a + b > MAX, which might overflow during the check itself, we check if a > MAX - b.
Lesson 1154Safe integer arithmetic and overflow checks
a >= b
Is a larger than b OR exactly the same as b?
Lesson 160Greater or equal `>=` and less or equal `<=`
A negative value
string1 comes before string2 (e.g., "apple" vs "banana").
Lesson 850Lexicographical comparison with `strcmp`
A non-zero value
The strings are different.
Lesson 426Comparing strings with `strcmp`
A positive value
string1 comes after string2 (e.g., "cherry" vs "banana").
Lesson 850Lexicographical comparison with `strcmp`
A->next
A common mistake is updating A->next to point to B as your very first step.
Lesson 1005Common pointer update pitfalls
A->next = B
If you set A->next = B, but forget to set B->prev = A, your list works fine when reading forward, but your program will crash or behave unpredictably the moment you try to traverse the list backward.
Lesson 1005Common pointer update pitfalls
a.exe
By default, if you run gcc hello.c, the compiler creates a generic executable file named a.out (or a.exe on Windows).
Lesson 57Basic `gcc` command flags
a.out
The name a.out stands for "assembler output." In the early days of Unix in the 1970s, programmers decided that if you were lazy and didn't provide a name, the computer would just use this generic placeholder.
Lesson 28Phase 4: The LinkerLesson 31How the OS runs a programLesson 32Executing from the command lineLesson 57Basic `gcc` command flagsLesson 58Naming the output with `-o`Lesson 60Understanding the `a.out` defaultLesson 68Warnings vs Fatal errors
a[3]
If you want the mail in the 4th box, you might say a[3].
Lesson 473The equivalence of `a[i]` and `*(a + i)`
a[i]
I better reload a[i] from memory every single loop iteration just in case the last write to result[i] changed it."* With restrict, the compiler can load values much more efficiently, knowing they won't change unexpectedly.
Lesson 473The equivalence of `a[i]` and `*(a + i)`Lesson 915The `restrict` pointer qualifier
AAA
A good unit test follows a simple pattern often called AAA:
Lesson 1172Principles of Unit Testing
abc
The result of a paste must be a valid C "token." If you try to glue 12 and abc to get 12abc, your compiler will complain because a variable name cannot start with a number.
Lesson 777The token-pasting operator `##`
abs
By having a dedicated function for floating-point numbers (fabs) and another for integers (abs), the computer can process the math using the most efficient instructions for that specific data type.
Lesson 866Absolute values for floats with `fabs`
abs()
If you try to use abs() on a number like -5.5, you might get an unexpected result or a compiler warning because abs() expects a whole number.
Lesson 866Absolute values for floats with `fabs`
abstract
C was designed to be a "Hardware Abstraction Layer." To abstract something means to hide the messy, complex details and replace them with a simpler interface.
Lesson 8Hardware abstraction in C
accept()
You can keep the client_fd in a list of active customers while your main loop goes back to accept() on the server_fd to wait for the next person in line.
Lesson 1113Accepting client connections
access()
While you could simply try to open a file and check if it returns NULL, there is a more specialized tool for "peeking" at a file without actually touching its contents: the access() function.
Lesson 762Checking if a file exists
Account
Whichever style you choose—whether it is Account_t or Account—the golden rule is to pick one and stick to it throughout your entire project.
Lesson 612Naming conventions for typedef types
Account_t
Whichever style you choose—whether it is Account_t or Account—the golden rule is to pick one and stick to it throughout your entire project.
Lesson 612Naming conventions for typedef types
accountBalance
Imagine changing a user's accountBalance just because you looped too far through a name string.
Lesson 938Accessing out-of-bounds memory
accumulator
In the second example, the "work" is passed forward through the accumulator parameter.
Lesson 362Tail call optimization basics
Accumulator Pattern
In programming, we call this the Accumulator Pattern.
Lesson 316The Accumulator pattern (summing values)
action
In mathematics, the equals sign (=) usually means "these two things are the same." However, in C programming, the = symbol is an action.
Lesson 22Semicolons as statement terminatorsLesson 78Assigning values with `=`Lesson 255Common mistake: assignment (=) vs equality (==)Lesson 516Calling a function via a pointer
action()
If you have a pointer named action, you call it by writing action();.
Lesson 516Calling a function via a pointer
active calls
The downward slope represents the active calls being added to the stack.
Lesson 363Visualizing recursive depth
add_and_print
Instead of writing separate functions for add_and_print and multiply_and_print, we write one compute function that accepts a "math rule" as an argument.
Lesson 518Passing functions as arguments
add_numbers
The resulting my_program now contains a copy of add_numbers.
Lesson 812Linking with static libraries
add()
This allows your main.c to call the add() function while staying blissfully ignorant of the underlying code.
Lesson 378Separating interface from implementationLesson 381Compiling multiple `.c` files
addEdge
Always ensure that every malloc used during your addEdge or createGraph functions has a corresponding free in your cleanup logic.
Lesson 1046Graph memory management
adder
Suppose you have a program called adder that adds two numbers.
Lesson 1178Automating tests with a Shell script
addFive(num)
When addFive(num) is called, the value 10 is copied into a new local variable also called num that belongs exclusively to the function.
Lesson 339Local scope of parameters
Address-of operator
You can actually ask C to show you the "mailbox number" where a variable is living by using the Address-of Operator (&).
Lesson 237The Address-of operator `&`Lesson 448How variables are stored in RAMLesson 449The Address-of operator `&`
addresses
Instead of the function trying to shove two items through the narrow "return" slot, the caller hands the function the addresses of two variables.
Lesson 489Returning multiple values via pointers
AddressSanitizer
Modern compilers like GCC and Clang offer a faster, "built-in" alternative called AddressSanitizer (or ASan).
Lesson 1171Using AddressSanitizer (`-fsanitize=address`)
addScore
The addScore function is only aware of its own bucket.
Lesson 486Pass-by-value limitations
addScore(myScore)
When addScore(myScore) is called, C looks at the value inside myScore (which is 50) and copies that number into a brand new memory bucket named localScore.
Lesson 486Pass-by-value limitations
Adjacency List
Think of an Adjacency List like a row of mailboxes at an apartment complex.
Lesson 1041Adjacency List implementationLesson 1042Adding edges in directed graphs
Adjacency Matrix
In computer science, this grid is called an Adjacency Matrix.
Lesson 1040Adjacency Matrix implementation
advisory locking
By default, fcntl() provides advisory locking.
Lesson 1099File locking with fcntl()
AF_INET
By changing AF_INET to AF_UNIX or SOCK_STREAM to SOCK_DGRAM, you completely change how your program interacts with the world without changing the rest of your logic.
Lesson 1108Socket domains and typesLesson 1109The sockaddr_in structureLesson 1110Creating a socket with socket()
AF_UNIX
Also called "Local" sockets. This is used for two programs running on the same physical machine. It’s faster because the data never actually hits the network hardware.
Lesson 1108Socket domains and types
after
It is like a restaurant where you sit down and start eating, and the waiter only checks if you have enough money to stay for the next course after you've finished the first one.
Lesson 199Postfix increment `x++`Lesson 278Converting a while loop to a do-whileLesson 721Detecting the end of a file with feofLesson 722Why feof inside a loop condition is badLesson 752Setting custom buffers with setvbuf
after every lap
The final part runs after every lap is finished.
Lesson 282The three parts of a for loop header
age < 18
You could technically write two separate if statements (one for age >= 18 and one for age < 18), but using else is much cleaner and safer.
Lesson 245The else clause for alternative paths
age < 25
In this example, the computer first checks age < 25.
Lesson 168Logical AND `&&`
age >= 18
You could technically write two separate if statements (one for age >= 18 and one for age < 18), but using else is much cleaner and safer.
Lesson 245The else clause for alternative paths
ages
You can use ages to find the start of your data, but you cannot reassign ages to point to a different array later on.
Lesson 476Pointer to the start of an array
alarm()
What you'll learn: How to use the alarm() function to schedule a "wake-up call" that interrupts your program after a specific amount of time.
Lesson 1091Handling alarms with alarm()
alarm(0)
Canceling: If you want to turn the timer off before it rings, you call alarm(0).
Lesson 1091Handling alarms with alarm()
alarm(10)
If you call alarm(10) and then call alarm(5) two seconds later, the first alarm is canceled and replaced by the new 5-second timer.
Lesson 1091Handling alarms with alarm()
alarm(5)
When you call alarm(5), you are telling the Operating System: "Hey, let me go back to my work, but please poke me in exactly five seconds." When the time is up, the OS sends a SIGALRM signal to your process.
Lesson 1091Handling alarms with alarm()
Alert.Red
You can have a Color.Red and a Alert.Red without any issues.
Lesson 666Scoped enum limitations in C
alias
In C, the typedef keyword stands for "type definition." Despite the name, it doesn’t actually create a new type of data; instead, it creates an alias or a nickname for a type that already exists.
Lesson 608Using typedef with primitive types
aligned_alloc
While malloc provides memory aligned for any standard data type, aligned_alloc gives you the power to specify a custom boundary.
Lesson 576The aligned_alloc function
alignment
Use aligned_alloc to place memory at specific address boundaries, ensuring the size is a multiple of the alignment.
Lesson 453Determining variable alignment in memoryLesson 576The aligned_alloc functionLesson 948The `#pragma pack` directive
alignof
The alignof operator reveals the byte-boundary requirements of a type, helping you understand how the CPU organizes data for maximum speed.
Lesson 453Determining variable alignment in memoryLesson 572The alignof operator
alignof(int)
By passing the alignment requirement (like alignof(int)) to your allocator, you ensure the pointer returned is always "legal" for the data type you intend to store there.
Lesson 572The alignof operatorLesson 589Handling alignment within an arena
all
While a struct allocates enough memory to hold all its members simultaneously, a union only allocates enough memory to hold its largest member.
Lesson 653Defining a union with the union keywordLesson 823Phony targets like `clean` and `all`Lesson 1103Closing unused pipe ends
ALL_CAPS
By convention, we write these names in ALL_CAPS to signal to other programmers that this isn't a regular variable—it’s a constant defined by the preprocessor.
Lesson 771Avoiding magic numbers with macros
ALL_CAPS_WITH_UNDERSCORES
Use ALL_CAPS_WITH_UNDERSCORES for constants to clearly signal that their values are permanent and should not be modified.
Lesson 117Naming conventions for constants
Allman
In C, two "dialects" rule the landscape: K&R and Allman.
Lesson 1196Consistency: K&R vs. Allman style
allocated
A "definitely lost" report points to the line where memory was allocated, reminding you that you failed to provide a corresponding free for that specific pointer.
Lesson 566Reading 'definitely lost' reports
already locked
If the mutex is already locked, the function does not wait.
Lesson 1129Using pthread_mutex_trylock
also has a
Because the input has a 1 in the third position and the mask also has a 1 there, the result becomes 4 (00000100).
Lesson 180Masking bits with `&`
Alt + Tab
Think of the Alt + Tab switcher on your computer.
Lesson 1004Circular doubly linked lists
always prefer inline functions
In modern C, always prefer inline functions. They provide the same performance benefits as macros but include the "safety net" of the compiler’s type-checking and predictable logic.
Lesson 370Macros vs. Inline functions
Always verify before you dereference
Safe C programming relies on a simple rule: Always verify before you dereference. Since functions like malloc() or custom search functions might return NULL if they fail, you should wrap your pointer logic in an if statement.
Lesson 525Dereferencing the NULL pointer
Amortized Time Complexity
This "averaged-out" cost is what we call Amortized Time Complexity.
Lesson 985Amortized time complexity
amount
If a float and an int are both 4 bytes, the amount section only takes up 4 bytes total.
Lesson 670Combining structs and unions
analysis.txt
Inside analysis.txt, you will see a table.
Lesson 1182Introduction to the `gprof` profiler
and a false one to
In C, a true comparison evaluates to 1 and a false one to 0.
Lesson 1189Reducing branching in tight loops
and a very large constant called
The rand() function returns an integer between 0 and a very large constant called RAND_MAX (usually at least 32,767).
Lesson 883Generating pseudo-random numbers with `rand`
and any false value (0) to
The ! operator flips a true value to 0 and any false value (0) to 1.
Lesson 167Logical NOT `!`
and change it to
If you add two more numbers to your array later, you have to find every place you wrote 7 and change it to 9.
Lesson 388Calculating array size with `sizeof`
and ends at index
Because C arrays are zero-indexed, a 10-element array starts at index 0 and ends at index 9.
Lesson 405Avoiding off-by-one errors in loops
and falsehood to
The ! operator flips truth to 0 and falsehood to 1, while !! scales any non-zero value down to a clean 1.
Lesson 174Operator `!` and boolean normalization
and increment
In i = i++, you are asking the computer to assign a value to i and increment i at the same time.
Lesson 937Sequence point violations
and it calls
If main() has a variable named x and it calls calculate(), which also has a variable named x, they do not clash.
Lesson 349Storage of local variables
and once for
If this were an undirected graph, you would have to call the logic twice (once for 0 to 1 and once for 1 to 0).
Lesson 1042Adding edges in directed graphs
and only catches the
While isgraph() catches everything from A to &, ispunct() ignores the A and only catches the &.
Lesson 843Distinguishing `ispunct` and `isgraph`
and the
By adding a 0 followed by a number between the % and the d, you tell C: "I want this number to be at least this many characters wide, and if it isn't, fill the empty space with zeros."
Lesson 43Printing literal stringsLesson 102Formatting decimals with `%.nf`Lesson 438Handling the `scanf` buffer overflowLesson 688Zero-padding numerical outputLesson 695Limiting string length in scanf
and the number
Computers see the number 5 as 0101 and the number 6 as 0110.
Lesson 176Bitwise OR `|`
and the second
In the code above, the compiler looks at the first pair {0, 0} and knows that 0 goes to x and the second 0 goes to y because that is the order defined in the struct Point.
Lesson 631Initializing arrays of structs
Angle Brackets <filename.h>
Angle Brackets <filename.h>: These are for Standard Headers.
Lesson 795Standard header search paths
anonymous
While typedef allows us to create a nickname for an existing struct, we can actually go one step further: creating a "nameless" or anonymous struct that exists only through its alias.
Lesson 611Anonymous structs with typedef
Anonymous mapping
Anonymous mapping is like renting an empty unit just to have the open floor space to build something new from scratch.
Lesson 594Anonymous memory mappings
anonymous union
An anonymous union allows you to skip that middle name.
Lesson 671Anonymous unions inside structs
Any non-zero value
Any non-zero value (1 to 255) means Failure.
Lesson 1072Process termination and exit codes
any non-zero value is considered true
Instead, any non-zero value is considered true.
Lesson 163Truthiness: 0 vs non-zero
Any other number
Any other number (like 1, 2, or -1) usually indicates that an error occurred.
Lesson 24The `return 0;` statement
Anything else
Anything else (usually 1) is considered True.
Lesson 167Logical NOT `!`
Anything else (non-zero)
Anything else (non-zero) is considered True.
Lesson 243Truthiness: 0 is false, non-zero is true
app
When you run make, Make sees $@ and thinks, "The target is app, so I'll put that there." It sees $< and thinks, "The first dependency is main.c, so I'll put that there." The actual command executed remains gcc main.c -o app.
Lesson 819Targets, dependencies, and recipesLesson 822Automatic variables like `$@` and `$<`
Apple
Identifiers must start with a letter or underscore and contain only alphanumeric characters, remembering that Apple and apple are two different variables.
Lesson 23Case sensitivity in CLesson 25Phase 1: The PreprocessorLesson 74Naming rules and identifiers
Apple clang version
If you see a wall of text that includes a version number (like gcc 11.4.0 or Apple clang version...), you are successful!
Lesson 10Installing GCC on Linux/macOS
apples
After the addition, your apples bucket still technically has "12" in it; you’ve simply used a copy of that value to create a new sum.
Lesson 150The addition operator `+`
applesInBasket
You can also do both steps at once, as shown with applesInBasket.
Lesson 81The `int` keyword
ar
The ar tool creates static libraries by bundling .o object files into a single .a archive for easier distribution and linking.
Lesson 810What is a static library `.a`Lesson 811Creating archives with the `ar` tool
ar rcs libutils.a
In the command ar rcs libutils.a ..., the flags mean:
Lesson 811Creating archives with the `ar` tool
archive/old.txt
Moving Files: You can use rename to move a file across directories on the same disk drive (e.g., from data/old.txt to archive/old.txt).
Lesson 756Renaming files with rename
archiver
With a static library, you use a tool called an archiver (ar) to glue those .o files together.
Lesson 810What is a static library `.a`
area_calc
You want the final, runnable file to be named area_calc so you know exactly what it does when you look at your files later.
Lesson 58Naming the output with `-o`
Arena
Use an Arena when objects share the same "lifetime" (they all die at the same time).
Lesson 591Trade-offs of arena vs malloc
arena_reset
Because you only call arena_reset, it is impossible to have a memory leak within your loop.
Lesson 587Resetting an arena in one stepLesson 588Arena allocation for frame-based tasks
arena_reset()
If you are writing a game or a web request handler, you can allocate everything you need for one frame or one request, then call arena_reset() at the end.
Lesson 586Linear or Bump allocators
argc
By narrowing our scope to just these three steps, we can focus on mastering file I/O (fopen), string searching (strstr), and command-line arguments (argc and argv).
Lesson 1204Project scope: A custom `grep` cloneLesson 1205Handling `argc` and `argv` robustly
args
If you forget to put NULL at the end of your args array, execv() will keep reading past your variables into random parts of your computer's memory.
Lesson 1082Passing arguments to execv()
Argument
Think of the Parameter as a parking spot and the Argument as the car.
Lesson 335Parameters vs. Arguments
Arguments
These specific items are your Arguments.
Lesson 335Parameters vs. Arguments
argv[0]
Since argv[0] is always the name of the program itself, a program expecting one additional argument (like a filename) needs argc to be exactly 2.
Lesson 1205Handling `argc` and `argv` robustly
arithmetic operator
It is an arithmetic operator used to add two values together.
Lesson 150The addition operator `+`
arithmetic shift
To prevent this, C often uses an arithmetic shift for signed integers.
Lesson 185Logical vs Arithmetic shifts
ARM
The two main titans you will encounter are x86 (Complex Instruction Set) and ARM (Reduced Instruction Set).
Lesson 963Platform-specific assembly (x86 vs ARM)
arr[0]
First or Last: Picking arr[0] or arr[n-1].
Lesson 1057Quick Sort: Pivot selection
arr[index]
Without this sequence point, the compiler might try to evaluate arr[index] at the same time it is trying to increment index.
Lesson 223Sequence points in logic `&&` and `||`
arr[n-1]
First or Last: Picking arr[0] or arr[n-1].
Lesson 1057Quick Sort: Pivot selection
array of function pointers
In C, an array of function pointers is that row of buttons.
Lesson 517Arrays of function pointers
array of pointers
Use an array of pointers when your strings have very different lengths (like a dictionary) or when you want to easily "swap" the order of strings.
Lesson 483Array of strings vs 2D char arrayLesson 927Arrays of pointers vs Pointers to arrays
array of structures
An array of structures is the entire spreadsheet.
Lesson 632Indexing into a struct array
array_ptr
By adding parentheses (*array_ptr), we force the compiler to treat array_ptr as a pointer first.
Lesson 927Arrays of pointers vs Pointers to arrays
array_ptr++
If you increment this pointer (array_ptr++), it jumps forward by the size of five integers at once, rather than just one.
Lesson 927Arrays of pointers vs Pointers to arrays
array[0]
If you write array[0] = array[1], the original value in array[0] is overwritten and lost forever.
Lesson 439Swapping elements in an array
array[0] = array[1]
If you write array[0] = array[1], the original value in array[0] is overwritten and lost forever.
Lesson 439Swapping elements in an array
array[0][0]
When you access array[0][0] and then array[0][1], the CPU likely already loaded the second value into the cache because they are neighbors.
Lesson 1191The impact of Cache Locality
array[0][1]
When you access array[0][0] and then array[0][1], the CPU likely already loaded the second value into the cache because they are neighbors.
Lesson 1191The impact of Cache Locality
array[1][0]
If you jump from array[0][0] to array[1][0], you are jumping over an entire row, potentially missing the cache entirely.
Lesson 1191The impact of Cache Locality
array[49]
Instead of remembering that "Employee 50" is stored at array[49], we can create a relationship where the ID string "EMP50" directly maps to a struct containing the employee's name and salary.
Lesson 1031Key-Value pair concept
array[i]
Even though your array is technically a pointer, C treats array[i] as shorthand for "start at the pointer address and move i steps forward."
Lesson 986Accessing elements by index
array[index]
Access dynamic array elements using the array[index] syntax just like static arrays, as the compiler automatically calculates the memory offset for you.
Lesson 386Accessing elements with the `[]` operatorLesson 986Accessing elements by index
array[j + 1]
The outer loop tracks how many passes we need to make, while the inner loop performs the actual comparisons and swaps between adjacent elements (array[j] and array[j + 1]).
Lesson 445Sorting an array using Bubble Sort
array[j]
The outer loop tracks how many passes we need to make, while the inner loop performs the actual comparisons and swaps between adjacent elements (array[j] and array[j + 1]).
Lesson 445Sorting an array using Bubble Sort
array[last_index]
Unlike an array where you can jump to array[last_index], a linked list requires a "walk." The while (last->next != NULL) loop is the heartbeat of this process.
Lesson 994Appending nodes to the tail
array[row][col]
To traverse a 2D array, use an outer loop for the rows and an inner loop for the columns to access each element at array[row][col].
Lesson 410Accessing elements using `[row][col]`Lesson 411Nested `for` loops for 2D traversal
arrayName[i]
Inside the loop, you access the current element using the syntax arrayName[i].
Lesson 399Using `for` loops for array traversal
arrayName[index] = newValue
To modify an array element, use the syntax arrayName[index] = newValue;, making sure your index is within the array's bounds.
Lesson 387Modifying individual array elements
arrayName[index].member
Use arrayName[index].member to reach inside a specific structure within an array.
Lesson 632Indexing into a struct array
arrayName[index].memberName
Access a specific member within an array of structures by using the format arrayName[index].memberName.
Lesson 633Combining array indexing and member access
arrayName[rowIndex][columnIndex]
It looks like this: arrayName[rowIndex][columnIndex].
Lesson 410Accessing elements using `[row][col]`
arrow
Use the arrow (->) when you are holding a map (the pointer) that tells you where the box is.
Lesson 165Common pitfall: `=` vs `==`Lesson 239Member access `.` and `->`Lesson 616The arrow operator `->` syntax
arrow operator (->)
C provides the arrow operator (->) as a shortcut.
Lesson 617Arrow operator vs dot operator
as
It realizes that a double is more precise, so it treats the 5 as 5.0.
Lesson 176Bitwise OR `|`Lesson 229Usual arithmetic conversions
as "gets" and
To keep them straight, try reading = as "gets" and == as "is equal to."
Lesson 165Common pitfall: `=` vs `==`
ASCII
To solve this, computer scientists created a secret codebook called ASCII (American Standard Code for Information Interchange).
Lesson 106Characters as small integersLesson 109The ASCII encoding scheme
ASCII table
To represent text, C uses a standard "translation manual" called the ASCII table.
Lesson 105The `char` type
asm volatile
What you'll learn: How to use the asm volatile syntax to prevent the C compiler from optimizing away or moving your manual assembly instructions.
Lesson 957The `asm` keyword syntaxLesson 958The basic `volatile` asm block
asm volatile ("instruction")
Use asm volatile ("instruction") to force the compiler to respect and execute your assembly code exactly where you placed it.
Lesson 958The basic `volatile` asm block
asm()
When you inject assembly using asm(), the compiler doesn't actually read your assembly string to see what you're doing.
Lesson 960The 'Clobber' list explained
Assembler
Phase 3 is handled by a tool called the Assembler.
Lesson 27Phase 3: Assembly to Object Code
Assembly
This is the "brain" of the operation, where your logic is translated from a language humans like (C) into a language that hardware understands, but can’t quite run yet: Assembly.
Lesson 26Phase 2: Compilation to AssemblyLesson 27Phase 3: Assembly to Object Code
assert
You use assert to check for things that should never happen if your logic is correct.
Lesson 901Using `assert` for internal debuggingLesson 1174Building a minimal custom test harness
assert()
Instead of manually deleting every assert() line from your source code—which would be a nightmare to put back later if you found a bug—the C Standard Library provides a "kill switch" called NDEBUG (which stands for "No Debug").
Lesson 901Using `assert` for internal debuggingLesson 902Disabling assertions with `NDEBUG`Lesson 1173Writing a simple `assert()` check
assert(expression)
The syntax is straightforward: assert(expression);.
Lesson 1173Writing a simple `assert()` check
assign
A single equals sign (=) is used to assign a value (e.g., "put the number 5 into this box").
Lesson 71Common beginner typos
assignment
You have already used the single equals sign (=) for assignment, which acts like a bucket loader, dumping a value into a variable.
Lesson 161The equality operator `==`Lesson 220Definition of a side effect
assignment operator
However, a specific group of operators—most notably the assignment operator (=)—prefers to drive on the other side of the road.
Lesson 214Right-to-left associativity
async-signal-safe
To stay safe, signal handlers should only call functions explicitly labeled as async-signal-safe.
Lesson 1089Signal safety and reentrant functions
at
If you ever find a 1 at [2][4] but a 0 at [4][2], your undirected graph is broken!
Lesson 1043Adding edges in undirected graphs
at least one
In C, if you provide at least one value during initialization, the compiler steps in to help.
Lesson 172Building complex logical expressionsLesson 392Partial initialization and default zeros
at the root
Imagine a tree with 5 at the root, 3 on the left, and 7 on the right.
Lesson 1025In-order traversal (Sorted output)
at the very end of the
Notice the semicolon ; at the very end of the while statement.
Lesson 277Using do-while for input re-prompting
atexit
Instead of trying to catch every possible return statement or exit point in your code, C provides a convenient "automatic reminder" system called atexit.
Lesson 875Cleaning up at exit with `atexit`
atexit()
The atexit() function, found in <stdlib.h>, allows you to register a specific function to be called automatically when your program terminates normally.
Lesson 875Cleaning up at exit with `atexit`
atof
While you might have heard of atof, it is often considered dangerous because it doesn't tell you if the conversion failed.
Lesson 873Converting strings to doubles with `strtod`
atoi
What you'll learn: How to reliably convert strings to integers while detecting errors that simpler functions like atoi ignore.
Lesson 871Converting strings to integers with `atoi` and `atol`Lesson 872Robust string-to-number conversion with `strtol`
atoi("hello")
If you try to convert a string that doesn't start with a number (like atoi("hello")), the function will simply return 0.
Lesson 871Converting strings to integers with `atoi` and `atol`
atol
The <stdlib.h> library provides two simple functions for this: atoi (ASCII to integer) and atol (ASCII to long integer).
Lesson 871Converting strings to integers with `atoi` and `atol`
atol("2147483648")
atol("2147483648") returns a long integer, which is necessary for numbers too large for a standard int.
Lesson 871Converting strings to integers with `atoi` and `atol`
atomic
In computer science, atomic means "indivisible." When you perform an atomic load or store, the CPU guarantees the operation happens as a single, uninterruptible step.
Lesson 1141Introduction to <stdatomic.h>Lesson 1142Atomic types like atomic_intLesson 1143Atomic load and storeLesson 1147Volatile vs Atomic
Atomic Fetch and Add
To fix this without the "heavy lifting" of a mutex (which is like putting the notebook in a locked safe), we use Atomic Fetch and Add.
Lesson 1144Atomic fetch and add
atomic_
You declare these types using the atomic_ prefix.
Lesson 1142Atomic types like atomic_int
atomic_compare_exchange_strong
In C11, we use atomic_compare_exchange_strong.
Lesson 1145Compare and swap (CAS) basics
atomic_fetch_add
atomic_fetch_add allows multiple threads to update a shared counter simultaneously and safely without the performance overhead of traditional locks.
Lesson 1144Atomic fetch and add
atomic_int
Atomic types like atomic_int ensure that simple operations are thread-safe at the hardware level, preventing data races without the overhead of Mutexes.
Lesson 1141Introduction to <stdatomic.h>Lesson 1142Atomic types like atomic_int
attackPower
You need to take the current attackPower, multiply it by 2, and then save that new total back into attackPower.
Lesson 194Compound multiplication and division
attributes
The C23 standard officially introduced a standardized way to give the compiler "sticky notes" called attributes.
Lesson 971C23: Attributes like `[[maybe_unused]]`
auto
The auto keyword represents the default storage behavior where variables are created at the start of a block and automatically destroyed at the end.
Lesson 140Automatic duration variablesLesson 143The `auto` keywordLesson 923Storage class specifier precedenceLesson 924The `register` keyword and its modern relevance
auto int score = 0
In the early days of C, programmers could explicitly write auto int score = 0; to tell the compiler that this variable should be created when the function starts and destroyed when the function ends.
Lesson 143The `auto` keyword
auto int x = 5
While you can still write auto int x = 5;, nobody does because C assumes any variable declared inside a function is automatic by default.
Lesson 140Automatic duration variables
automatic duration variables
They are known as automatic duration variables (often just called "local variables").
Lesson 140Automatic duration variables
Automatic memory
Think of Automatic memory (The Stack) like a small, organized desk.
Lesson 533Manual vs automatic memory management
automatic storage duration
Normally, when you declare a variable inside a function, it has automatic storage duration.
Lesson 529Automatic storage duration on the stackLesson 920The `static` keyword inside functions
automatic variables
This is why local variables are called automatic variables—their lifecycle is managed for you.
Lesson 530Stack frame lifecycle and local variablesLesson 822Automatic variables like `$@` and `$<`
automatically re-locks the mutex
Re-acquire: Once another thread signals that things are ready, your thread wakes up and automatically re-locks the mutex before the function returns.
Lesson 1133Waiting with pthread_cond_wait

B

b = 5
So, the expression b = 5 actually "results" in the value 5.
Lesson 196Chained assignments `a = b = c`
B->next
If you haven't already pointed B->next to C, node C (and everything following it) is now floating away in memory.
Lesson 1005Common pointer update pitfalls
B->prev = A
If you set A->next = B, but forget to set B->prev = A, your list works fine when reading forward, but your program will crash or behave unpredictably the moment you try to traverse the list backward.
Lesson 1005Common pointer update pitfalls
back into a
The second ! flips that 0 back into a 1.
Lesson 174Operator `!` and boolean normalization
backlog
When you call listen(), you provide two arguments: the socket file descriptor and a backlog.
Lesson 1112Listening for connections
backtrace
What you'll learn: How to use the backtrace command to see the sequence of function calls that led to a specific point or a crash in your program.
Lesson 833Inspecting the call stack with `backtrace`Lesson 837Debugging a Segfault from a core dump
backward
To understand what is actually being protected, the best trick is to read the declaration backward (from right to left).
Lesson 911Difference between `const int *` and `int * const`
bake_cake
When bake_cake finishes, the memory it used for temperature is instantly freed up for the next function call.
Lesson 349Storage of local variables
bake_cake()
Readability: It is much easier to read bake_cake(); than to read fifty lines of code about flour, eggs, and oven temperatures.
Lesson 322What is a function?
Baker
You have a Baker (the Producer) and a Cashier (the Consumer).
Lesson 1136The producer-consumer problem
balance
By inserting printf() statements, you leave "breadcrumbs." If you expect a variable named balance to be 100, but the console shows it is -500, you have successfully narrowed down exactly where the logic failed.
Lesson 1163Inspecting variable values at runtime
balance -= 10
Adjusting by a specific amount: balance -= 10;
Lesson 268Updating the loop variable to avoid infinite loops
balance = balance + 50
Standard addition looks like this: balance = balance + 50;.
Lesson 192Compound addition `+=`
barrier
In systems programming, a barrier is that restaurant door.
Lesson 1137Thread barriers
base
By reserving a large chunk of virtual address space (e.g., 1GB) upfront, the base pointer never changes.
Lesson 590Growing an arena with virtual memory
base case
To fix this, you must ensure every recursive function has a base case: a conditional statement that allows the function to return without making another call, finally allowing the stack of trays to be cleared.
Lesson 352Identifying a Stack OverflowLesson 353Concept of self-calling functionsLesson 354Importance of the Base CaseLesson 355The Recursive StepLesson 357Factorial as a recursive exampleLesson 360Infinite recursion hazards
batching
High-performance C code often uses a technique called batching.
Lesson 1185Profiling memory allocation frequency
becomeRich(mySavings)
When becomeRich(mySavings) is called, the value 50 is copied into the parameter money.
Lesson 343Why changing a parameter doesn't affect the caller
becomes
Because the input has a 1 in the third position and the mask also has a 1 there, the result becomes 4 (00000100).
Lesson 167Logical NOT `!`Lesson 174Operator `!` and boolean normalizationLesson 180Masking bits with `&`
before every lap
The middle part is a true/false question asked before every lap.
Lesson 282The three parts of a for loop header
before it even touches
In many cases, the compiler can decide to evaluate a before it even touches b or c.
Lesson 227Order of evaluation vs Precedence
begin
You can't call it start, begin, or entry.
Lesson 20The `main()` function entry point
Best-fit
The Best-fit strategy, however, looks at all available gaps and realizes the 10-inch gap is the "tightest" fit.
Lesson 584Allocation strategies: Best-fit
between nodes
Imagine you are inserting a new node B between nodes A and C.
Lesson 1005Common pointer update pitfalls
Big Endian
Big Endian is like writing naturally: you put "CO" in the first box, "FF" in the second, and "EE" in the third.
Lesson 950Big Endian vs Little Endian explained
Big-Endian
A Big-Endian system acts like we read English; it puts the "big end" (the 00) in the first mailbox and saves the 01 for the very last one.
Lesson 951Checking system endianness at runtimeLesson 952Network byte order and `htons`/`ntohs`Lesson 953Manual byte swapping techniques
billing.c
If another programmer working on billing.c also creates a function named calculate_total(), the compiler will throw a "duplicate symbol" error when it tries to link the files together.
Lesson 798Static functions for file scoping
billsToPay
C looks at billsToPay, sees a 0, and immediately skips the block.
Lesson 243Truthiness: 0 is false, non-zero is true
Binary
You will rarely use Octal in modern programming, but you will use Hex and Binary constantly when working with colors (like #FF5733), memory addresses, or hardware pins.
Lesson 119Integer literals (Hex, Octal, Binary)Lesson 156Unary plus and minus
binary format
Because we saved our data in binary format, we don't need to parse text or convert strings back into numbers.
Lesson 1218Loading data from a binary file
Binary Heap
Before we can sort data using Heap Sort, we need to understand the Binary Heap.
Lesson 1058Heap Sort: Binary heap concept
Binary mode
While we could use text (like a .txt file), saving in binary mode is faster and more efficient because it writes the data exactly as it looks in your computer's memory.
Lesson 724Text mode vs Binary mode (b flag)Lesson 1217Saving the data store to a binary file
Binary Search
When data is sorted, we can use much smarter algorithms, like Binary Search.
Lesson 1064Importance of sorted dataLesson 1065Time complexity: O(n) vs O(log n)
Binary Writing
Think of Binary Writing like taking a snapshot of a drawer in your desk.
Lesson 725Writing raw bytes with fwrite
bind()
bind() assigns a specific identity (IP address and port) to your socket, transforming it from a generic communication tool into a reachable destination.
Lesson 1109The sockaddr_in structureLesson 1111Binding to a port with bind()Lesson 1113Accepting client connectionsLesson 1114Client-side connect()
bit-field
C allows you to specify exactly how many bits a structure member should occupy using a bit-field.
Lesson 648The colon operator and bit width
bit-fields
C allows you to define bit-fields within a struct.
Lesson 954Bit-fields in structures and portability
Bitwise AND (&)
Bitwise AND (&): To isolate a specific byte (masking).
Lesson 953Manual byte swapping techniques
bitwise operators
Use bitwise operators when you are doing low-level programming, like setting specific flags in hardware or compressing data.
Lesson 173Logical vs Bitwise distinction
Bitwise OR (|)
Bitwise OR (|): To combine the moved bytes into a new result.
Lesson 953Manual byte swapping techniques
Bitwise XOR
In C, the ^ symbol represents the Bitwise XOR (Exclusive OR) operator.
Lesson 181Toggling bits with `^`
blueprint
Imagine a header file as a blueprint for a house.
Lesson 793What should NOT go in a header
body
A function definition consists of a header and a body.
Lesson 323Anatomy of a function definition
book
If you have a Book struct, you might have one compartment for the price and another for the page count.
Lesson 414Introduction to 3D and higher arraysLesson 602Initializing structs with brace notationLesson 623Defining a struct inside another struct
Book Number
To find a specific piece of information, you need two numbers: the Book Number and the Page Number within that book.
Lesson 597Virtual memory pages and offsets
bool
If you wanted to make your code readable, you had to manually define constants or include <stdbool.h> (introduced in C99), which gave you bool as a macro for the slightly clunky _Bool type.
Lesson 163Truthiness: 0 vs non-zeroLesson 189Using bitwise operators for flagsLesson 966C99: Variable declarations and `bool`Lesson 969C23: The `bool`, `true`, and `false` keywords
bool is_running = true
In older versions of C, if you typed bool is_running = true; without including a header file, the compiler would throw a tantrum, claiming it had no idea what a "bool" was.
Lesson 969C23: The `bool`, `true`, and `false` keywords
boolean normalization
Sometimes you don't care that a variable equals 42; you just want a clean 1 to represent that the variable "has a value." This is where boolean normalization comes in.
Lesson 174Operator `!` and boolean normalization
Boolean values
When you ask a question in English like "Is 5 greater than 3?", the answer is a simple "Yes." In programming, we call these Yes/No answers Boolean values (True or False).
Lesson 164Boolean result of comparisons
boots
If you put a pair of boots in the locker, the locker contains boots.
Lesson 659The danger of reading the wrong union member
both
If both sides are true, the entire expression results in 1 (true).
Lesson 168Logical AND `&&`Lesson 247Logical AND (&&) for combined conditions
bottles
Once bottles hits 0, the loop "breaks," the program "exits" the curly braces, and moves on to the final print statement.
Lesson 268Updating the loop variable to avoid infinite loops
bottles > 0
By changing bottles inside the loop, we are actively working toward the moment when bottles > 0 becomes false.
Lesson 268Updating the loop variable to avoid infinite loops
box
If you change box, you are reaching all the way through the chain to modify the original treasure.
Lesson 502Visualizing pointer chainsLesson 505Accessing data through double dereferenceLesson 616The arrow operator `->` syntax
box containing a marble
Think of a string literal (using double quotes) as a box containing a marble.
Lesson 418Difference between `'a'` and `"a"`
Brace notation
Brace notation allows you to "stock the shelves" all at once, the very moment you declare the variable.
Lesson 602Initializing structs with brace notation
brand new socket
Instead, it creates a brand new socket specifically for that one client.
Lesson 1113Accepting client connections
break [function name]
Use break [line number] or break [function name] to pause your program's execution at a specific spot for inspection.
Lesson 829Setting breakpoints with `break`
break [line number]
Use break [line number] or break [function name] to pause your program's execution at a specific spot for inspection.
Lesson 829Setting breakpoints with `break`
break [location] if [condition]
Use break [location] if [condition] to ignore irrelevant iterations and jump straight to the moment a bug occurs.
Lesson 835Setting conditional breakpoints
breakpoints
When you are debugging a program in GDB, you often use breakpoints to freeze time.
Lesson 829Setting breakpoints with `break`Lesson 831Continuing execution with `continue`
brk
The program break is the boundary of your heap, and brk/sbrk are the system-level tools used to push that boundary forward to claim more memory.
Lesson 592Introduction to brk and sbrk
brk(addr)
This sets the "fence" to a specific, absolute address.
Lesson 592Introduction to brk and sbrk
broadcast
But if the driver shouts "The bus is here, everyone get in line!", that’s a broadcast.
Lesson 1135Broadcasting to all threads
bsearch
By providing a pointer to the key and a comparison rule, bsearch handles the logic of jumping through the memory, making your search lightning-fast without you having to write the complex "divide and conquer" logic yourself.
Lesson 880Using `bsearch` on sorted arraysLesson 881Handling the `void*` return of `bsearch`Lesson 882Common pitfalls in comparison function logicLesson 1066Using C library 'bsearch' function
bsearch()
When you use bsearch() to find an item in an array, the function doesn't know what kind of data you are searching through.
Lesson 881Handling the `void*` return of `bsearch`
BSWAP
The __builtin_bswap family of functions tells the CPU to use a specialized hardware instruction (like BSWAP on x86) to flip the bytes in a single clock cycle.
Lesson 956The `__builtin_bswap` compiler intrinsics
bt
Use backtrace (or bt) in GDB to view the list of active function calls and trace the path your program took to reach its current state.
Lesson 833Inspecting the call stack with `backtrace`Lesson 837Debugging a Segfault from a core dump
Bubble Sort
While algorithms like Bubble Sort and Insertion Sort are typically stable, Quick Sort is often unstable because it swaps elements over long distances.
Lesson 1051In-place sorting vs extra memoryLesson 1052Stability in sorting algorithms
bucket
Instead of making the mailbox a tiny slot that only fits one envelope, we turn each mailbox into a bucket.
Lesson 1034Implementing the bucket array
buffer[-1]
Without the semaphores, the Consumer might try to access buffer[-1] because it didn't know the rack was empty, causing a segmentation fault.
Lesson 1136The producer-consumer problem
buffered
By default, stdout (standard output) is buffered.
Lesson 1158Flushing `stdout` for accurate logs
BUFSIZ
To use a custom buffer: Pass an array of a specific size (BUFSIZ).
Lesson 753The setbuf shorthand
Bump Allocator
A Linear Allocator (also called a Bump Allocator) is like a tall stack of clean trays.
Lesson 586Linear or Bump allocators
busy-waiting
In programming, this is called busy-waiting.
Lesson 1132Introduction to condition variables
but a
If you ever find a 1 at [2][4] but a 0 at [4][2], your undirected graph is broken!
Lesson 1043Adding edges in undirected graphs
by
If you divide 5 by 2, C gives you 2, throwing the .5 in the trash.
Lesson 234Safety with explicit casts
byte by byte
It is vital to remember that memset works byte by byte.
Lesson 855Setting memory blocks with `memset`

C

C _ A R T
If you have the word "CHART" and want to remove the 'H', you can't just delete it; you would be left with C _ A R T.
Lesson 444Removing a character from a string
c = 10
Instead of writing three separate lines, the "return value" of c = 10 is handed off to b, and so on.
Lesson 197Assignment expression return value
c = 50
In the code above, the expression c = 50 happens first.
Lesson 214Right-to-left associativity
C:\msys64\ucrt64\bin
Add the location where MinGW was installed (usually C:\msys64\ucrt64\bin).
Lesson 11Setting up MinGW on Windows
C:\Users\Bob\Documents
Instead of hard-coding a folder path like C:\Users\Bob\Documents, you can ask the system for HOME.
Lesson 1071Environment variables in C
c11
Common values include c89, c99, c11, and c17.
Lesson 972Specifying the standard with `-std=` flags
c17
Common values include c89, c99, c11, and c17.
Lesson 972Specifying the standard with `-std=` flags
c89
Common values include c89, c99, c11, and c17.
Lesson 972Specifying the standard with `-std=` flags
c99
Common values include c89, c99, c11, and c17.
Lesson 972Specifying the standard with `-std=` flags
Cache
Because RAM is relatively slow, the CPU pulls "chunks" of nearby memory into a high-speed storage area called the Cache.
Lesson 1191The impact of Cache Locality
Cake -> Frosting -> Butter
This is a dependency chain: Cake -> Frosting -> Butter.
Lesson 802Dependency graphing in your head
calculate
When you compile this code, the compiler sees the declaration and says, "Okay, I'll leave a placeholder here for calculate." However, once the compiler finishes, the Linker steps in to connect the placeholder to the actual logic.
Lesson 70The 'undefined reference' linker errorLesson 348Pushing and popping framesLesson 805What is inside a `.o` file
calculate_cubes
If calculate_cubes takes up 70% of the total time, gprof will tell you exactly that.
Lesson 1182Introduction to the `gprof` profiler
calculate_discount
By extracting calculate_discount, you can now write a separate "test script"—another small C program—that calls that function with dozens of different numbers (like negative prices or 100% discounts) to see if it breaks.
Lesson 1175Separating logic from `main()` for testability
calculate_gravity
It essentially says, "I don't know where the calculate_gravity function is yet, but I trust the linker to find it later."
Lesson 806Linking multiple object files
calculate_log
In the example above, calculate_log is where the error happened, but process_data is the function that provided the data.
Lesson 833Inspecting the call stack with `backtrace`
calculate_physics()
Instead, it leaves a "placeholder" or a note that says, "When this program starts, go find the code for calculate_physics() in physics_lib.so."
Lesson 813What is a shared library `.so` / `.dll`
calculate_squares
You now know that even if you make calculate_squares twice as fast, it will barely impact the overall speed because the "bottleneck" is in the cubes function.
Lesson 1182Introduction to the `gprof` profiler
calculate_tax
If calculate_tax shows up as 92%, you’ve found your hot spot.
Lesson 1183Identifying 'Hot Spots' in your code
calculate_tax()
In older versions of C, if you called a function named calculate_tax() before defining it, the compiler would shrug and say, "I'll assume this function exists somewhere and that it returns an integer." This is an implicit declaration.
Lesson 329Implicit vs. explicit declarationsLesson 1172Principles of Unit Testing
calculate_total
The Typo: You named the function calculate_total in your header but accidentally wrote calculate_totals (with an 's') in your .c file.
Lesson 530Stack frame lifecycle and local variablesLesson 807Understanding 'undefined reference' errorsLesson 809Symbol tables and visibility
calculate_total()
If another programmer working on billing.c also creates a function named calculate_total(), the compiler will throw a "duplicate symbol" error when it tries to link the files together.
Lesson 798Static functions for file scopingLesson 809Symbol tables and visibility
calculate_total(const int *prices)
If you write a function to calculate_total(const int *prices), you are telling the world that this function is a "pure" observer.
Lesson 1198The role of `const` in documentation
calculate_totals
The Typo: You named the function calculate_total in your header but accidentally wrote calculate_totals (with an 's') in your .c file.
Lesson 807Understanding 'undefined reference' errors
Calculate()
This allows you to create "Generic Macros." You can create a single macro called calculate() that calls a math function for integers or a completely different one for floats, all while keeping your main code clean and readable.
Lesson 70The 'undefined reference' linker errorLesson 348Pushing and popping framesLesson 973Introduction to the `_Generic` keyword
calculateTax
Like variable names, these should be descriptive verbs, like calculateTax or printHello.
Lesson 323Anatomy of a function definition
calculateTotal()
This is why a variable created inside main() cannot be seen by a variable inside calculateTotal()—they exist on different parts of the workbench at different times.
Lesson 149Memory segments: Stack vs. Data
Calculation.C
Bad: Calculation.C (Some systems treat .C as C++, which is a different language!).
Lesson 16Naming conventions for .c files
calculator.c
In this example, main.c doesn't know how to add numbers (that logic lives in calculator.c), but it knows when to ask for the result.
Lesson 799The role of the 'main' file
calculator.h
If you had a file named calculator.h, it might look like this:
Lesson 788The purpose of header files
Calibration
If you decide to change the Calibration struct later—perhaps adding a "last_serviced" date—you only have to change the functions that directly handle the sensor.
Lesson 674Information hiding using void pointers
call before returning to
Typing step (or just s) will jump the debugger into the greet() function, allowing you to walk through the printf call before returning to main.
Lesson 830Stepping through code with `next` and `step`
calloc()
However, we often prefer calloc(), which guarantees every single bit is set to zero.
Lesson 545Zero-initialization overhead
camelCase
In C, the two most common styles are snake_case (all lowercase with underscores) and camelCase (capitalizing the first letter of each word except the first).
Lesson 1197Meaningful variable naming conventions
can_
Booleans should be questions: If a variable is true or false, prefix it with is_, has_, or can_.
Lesson 1197Meaningful variable naming conventions
CAN_FLY
If you OR your status with CAN_FLY, that specific bit flips to 1, regardless of what else is there.
Lesson 189Using bitwise operators for flags
cannot
The first character: A name cannot start with a digit.
Lesson 74Naming rules and identifiers
cannot create an array of bit-fields
Because you cannot take the address of a bit-field, you also cannot create an array of bit-fields.
Lesson 647Restrictions on bit-field types
capacity
To do this, we track two values: the size (how many items are currently inside) and the capacity (how many items the memory can actually hold).
Lesson 981Structure for dynamic arraysLesson 983Pushing elements and capacity checksLesson 984Geometric resizing with reallocLesson 1214Implementing a dynamic array for storage
Car
When you see Car myRide;, your brain immediately understands that Car is a custom type, without the distracting struct keyword reminding you of the underlying implementation details.
Lesson 610Combining struct definition and typedefLesson 624Accessing members of nested structs
Car myRide
When you see Car myRide;, your brain immediately understands that Car is a custom type, without the distracting struct keyword reminding you of the underlying implementation details.
Lesson 610Combining struct definition and typedef
case 1
Readability: case RUNNING is instantly understandable, whereas case 1 is not.
Lesson 668Using enums for state machines
case 2
If you forgot the break after case 2, the console would print "Preparing a Medium coffee" AND "Preparing a Large coffee." By including break, you ensure that the program performs only the specific task you intended.
Lesson 259The role of the break statement in switch
case PAUSED
Clarity: Anyone reading your code knows exactly what case PAUSED means.
Lesson 665Using enums in switch statements
case RUNNING
Readability: case RUNNING is instantly understandable, whereas case 1 is not.
Lesson 668Using enums for state machines
case-sensitive
One important note: strcmp is case-sensitive.
Lesson 426Comparing strings with `strcmp`
Cashier
You have a Baker (the Producer) and a Cashier (the Consumer).
Lesson 1136The producer-consumer problem
cast
Before you can use the data, you must cast it back to its original type.
Lesson 507The `void *` generic typeLesson 509Casting `void *` to specific types
cast operator
The cast operator is a set of parentheses containing a data type name, placed directly before a value or variable.
Lesson 230The `(type)` cast operator
CC
Here is a Makefile that uses variables for the compiler (CC), the compiler flags (CFLAGS), and the final executable name (TARGET).
Lesson 821Using variables in Makefiles
ceil
Even if you have 5.01, ceil will push it up to 6.0.
Lesson 864Rounding with `ceil`, `floor`, and `round`
ceil()
If a user buys 2.1 containers of flour, you can't ship a partial container, so you use ceil() to charge them for 3.
Lesson 864Rounding with `ceil`, `floor`, and `round`
CFLAGS
Here is a Makefile that uses variables for the compiler (CC), the compiler flags (CFLAGS), and the final executable name (TARGET).
Lesson 821Using variables in Makefiles
ch
First, it assigns the result of getchar() to our variable ch.
Lesson 682Using while loops with getchar
chained assignment
However, C allows a shortcut called chained assignment.
Lesson 196Chained assignments `a = b = c`
Chaining
Chaining solves this by turning every mailbox into a "hook" for a linked list.
Lesson 1033Handling collisions with Chaining
char
If you add two char variables together, C doesn't perform "char math." Instead, it takes the value of the first char, converts it to an int, does the same for the second, and then adds the two int values together.
Lesson 51Printing characters with `%c`Lesson 89The `signed` keywordLesson 105The `char` typeLesson 106Characters as small integersLesson 107Single quotes vs. double quotesLesson 109The ASCII encoding schemeLesson 110Arithmetic with charactersLesson 111Signed vs. Unsigned charsLesson 112Printing chars with `%c`Lesson 121What is type promotion?Lesson 122Integer promotion rulesLesson 126The 'Usual Arithmetic Conversions'Lesson 131Casting between char and intLesson 132Safe downcasting techniquesLesson 155The modulo operator `%` with integersLesson 157Basic arithmetic overflowLesson 178Bitwise NOT `~` (Complement)Lesson 179Understanding binary representationLesson 183Left shift `<<` mechanicsLesson 228Implicit promotion to `int`Lesson 229Usual arithmetic conversionsLesson 231Truncation during castingLesson 233Promotion of `char` and `short`Lesson 235The `sizeof` operator with typesLesson 262Switch restrictions: integral types onlyLesson 324The `void` return typeLesson 332Matching prototypes with definitionsLesson 383Declaring an array with `type name[size]`Lesson 415Declaring arrays of type `char`Lesson 418Difference between `'a'` and `"a"`Lesson 430Setting memory blocks with `memset`Lesson 447Memory as a linear sequence of bytesLesson 452The size of a pointer variableLesson 453Determining variable alignment in memoryLesson 455Declaring pointer variables with `*`Lesson 456The difference between `int *p` and `*p`Lesson 461Implicit vs explicit pointer typesLesson 463Incrementing pointers with `++`Lesson 468Scaling factor in pointer mathLesson 472Accessing arrays with pointer notationLesson 479String literals as `char` pointersLesson 512The `memcpy` function signatureLesson 573Struct padding for alignmentLesson 576The aligned_alloc functionLesson 577Using __attribute__((packed))Lesson 589Handling alignment within an arenaLesson 599Defining a struct with the struct keywordLesson 609Creating a shorthand for struct namesLesson 630Declaring an array of structsLesson 637The sizeof operator on structs
char *
In C, if you try to modify a string literal—for example, by writing message[0] = 'J';—your program will likely crash with a "Segmentation Fault." The compiler won't always warn you about this danger because char * looks like any other pointer that is allowed to change data.
Lesson 461Implicit vs explicit pointer typesLesson 479String literals as `char` pointersLesson 480Mutable vs immutable string memoryLesson 485Using `const char *` for safetyLesson 506Command line arguments `char **argv`Lesson 507The `void *` generic typeLesson 510Implicit conversion to `void *`Lesson 878Writing a string comparison function for `qsort`
char *message = "Hello"
When you write a line like char *message = "Hello";, you are pointing a variable at a piece of text stored in a "read-only" section of your computer's memory.
Lesson 479String literals as `char` pointersLesson 485Using `const char *` for safety
char *myMessage
The Setup: char *myMessage is a box that holds a memory address.
Lesson 503Modifying a pointer inside a function
char *myPtr
When we declare a pointer like int myPtr or char myPtr, we are being explicit.
Lesson 461Implicit vs explicit pointer types
char *myPtr = "Alice"
However, when you create a string pointer like char *myPtr = "Alice";, the behavior changes.
Lesson 484Memory layout of string pointers
char *myStr = "Hello"
When you declare a string using a pointer, like char *myStr = "Hello";, something different happens.
Lesson 480Mutable vs immutable string memory
char *names[]
The array of pointers (often called an "array of strings") is declared using char *names[].
Lesson 483Array of strings vs 2D char array
char a
It leaves a 3-byte gap after char a so that b stays aligned.
Lesson 571CPU word size and alignment
char argv
By using char argv, C gives you a flexible way to handle any number of words of any length without knowing how many there will be until the program actually starts.
Lesson 506Command line arguments `char **argv`
char buffer[100]
By explicitly setting a size larger than your text (like char buffer[100]), you create a "safety zone." You can put a short word in a big box, but you can never put a big word in a small box.
Lesson 419Initializing strings with sizes
char buffer[1024]
In previous lessons, we built arenas using a fixed-size array (like char buffer[1024]).
Lesson 590Growing an arena with virtual memory
char message[20]
When you declare char message[20], the computer sets aside exactly 20 bytes of memory.
Lesson 415Declaring arrays of type `char`
char myName[] = "Alice"
In C, when you create a string using an array like char myName[] = "Alice";, you are building a house.
Lesson 484Memory layout of string pointers
char myStr[] = "Hello"
When you declare a string as an array, like char myStr[] = "Hello";, C treats it as a local variable.
Lesson 480Mutable vs immutable string memory
char myString[20]
If you declare char myString[20];, the size is 20.
Lesson 420Length vs Size of a string array
char name[10]
When you initialize a string with a specific size, like char name[10], you are essentially building a row of lockers.
Lesson 419Initializing strings with sizes
char name[20]
Up until now, you have used variables like int x or char name[20].
Lesson 868Allocating memory with `malloc` and `free`
char name[50]
You might wonder why we use char name[50] instead of a flexible pointer.
Lesson 1213Defining the Record struct
char names[5][20]
In C, it looks like this: char names[5][20];.
Lesson 483Array of strings vs 2D char array
char str1[] = "Hello"
If you have char str1[] = "Hello"; and char str2[10];, writing str2 = str1; will cause a compiler error.
Lesson 424Copying strings with `strcpy`
char str2[10]
If you have char str1[] = "Hello"; and char str2[10];, writing str2 = str1; will cause a compiler error.
Lesson 424Copying strings with `strcpy`
char temp[size]
By using char temp[size], we create a workspace just big enough for the data, regardless of whether it’s a tiny character or a massive structure.
Lesson 511Generic functions in C
CHAR_BIT
Think of CHAR_BIT as a label on a container; instead of guessing how much the container holds, you simply read the label provided by the manufacturer (the compiler).
Lesson 96The `<limits.h>` header fileLesson 906Platform-specific character sizesLesson 942Limits of `limits.h` and `stdint.h`
CHAR_MIN
Instead of wondering if a char is signed or unsigned by default, you can check CHAR_MIN.
Lesson 904Integer ranges in `limits.h`
char[]
If you need to change the content of your string during the program, always use the array syntax (char[]).
Lesson 480Mutable vs immutable string memory
char[5]
If you try to cram a 5-letter word into a char[5] array, there is no room left for the \0 terminator.
Lesson 419Initializing strings with sizes
char*
If you truly need to look at the raw bits of a variable (for example, to send them over a network), the only safe way is to use a pointer to a character type (char or unsigned char) or the memcpy function.
Lesson 527Pointer type-punning dangersLesson 951Checking system endianness at runtimeLesson 975Implementing a generic 'Print' macroLesson 976Handling the `default` case in `_Generic`Lesson 980Limitations of C generics
characters
strlen tells you how many characters are currently inside that space.
Lesson 847Finding string length with `strlen`
charactersPrinted
Then, it hands the number 14 to the variable charactersPrinted.
Lesson 691The return value of printf
charPtr
However, because charPtr is explicitly typed as a char, it only looks at a tiny slice of the data.
Lesson 461Implicit vs explicit pointer types
chars
By putting the int first and the chars at the end, the compiler can often pack them more tightly.
Lesson 640Structure holes and performance
check_system_health()
But if you are inside a function called check_system_health() (which is called by main()), a return would only take you back to main().
Lesson 313Exiting the program with exit()
chef in the kitchen
The Source file (.c) is the chef in the kitchen.
Lesson 796Splitting code into `.c` and `.h`
child
This new process (the child) gets a copy of the original process's (the parent) variables, file descriptors, and code.
Lesson 1075Process duplication and copy-on-write
chmod
It requires two arguments: the path where the pipe should live and the file permissions (similar to chmod).
Lesson 1107Creating FIFOs with mkfifo()
chmod +x test.sh
To run this, you would give the script permission with chmod +x test.sh and then type ./test.sh.
Lesson 1178Automating tests with a Shell script
choice
In this example, using a regular while loop would be clunky because you’d have to initialize choice to a "dummy" value just to get past the first check.
Lesson 275Guaranteed execution: why do-while is differentLesson 276Using do-while for menu-driven programsLesson 279Comparing while vs do-while use cases
Cinnamon
You might have one jar labeled Cinnamon and another labeled Salt.
Lesson 667Type safety concerns with enums
Circle
We have a Point (x, y), and a Circle that uses a Point to define its center.
Lesson 625Initializing nested structures
circular dependency
In C, a circular dependency happens when file_a.h contains #include "file_b.h", but file_b.h also contains #include "file_a.h".
Lesson 794Circular dependency issues
clang
To bridge the gap between your text file and a running program, you use a tool called a compiler (like gcc or clang).
Lesson 6C as a compiled languageLesson 30Creating an executable binaryLesson 821Using variables in Makefiles
clang-format
clang-format is a command-line tool that reads your C files and automatically reformats them according to a specific style guide (like Google, LLVM, or Microsoft).
Lesson 1201Using `clang-format` for automation
clang-format -i main.c
By running a simple command like clang-format -i main.c, the tool automatically rewrites the file to look like this:
Lesson 1201Using `clang-format` for automation
classList[i].id
When you access classList[i].id, you are telling C: "Go to the $i$-th folder in the cabinet, open it, and look at the ID written inside."
Lesson 636Searching through struct arrays
classroom
You cannot write classroom.grade[1], because classroom is the name of the entire array, and the array itself doesn't have a member named "grade"—only the individual structures inside it do.
Lesson 630Declaring an array of structsLesson 633Combining array indexing and member access
classroom.grade[1]
You cannot write classroom.grade[1], because classroom is the name of the entire array, and the array itself doesn't have a member named "grade"—only the individual structures inside it do.
Lesson 633Combining array indexing and member access
classroom[0]
Each element in the array (classroom[0], classroom[1], etc.) is a complete structure with its own name, id, and gpa.
Lesson 630Declaring an array of structs
classroom[1]
Each element in the array (classroom[0], classroom[1], etc.) is a complete structure with its own name, id, and gpa.
Lesson 630Declaring an array of structsLesson 633Combining array indexing and member access
clean
When you type make clean to sweep away your old .o files, make will look at that file and say: "clean is up to date." It won't run your cleanup commands because it thinks the "job" of creating a file named clean is already finished.
Lesson 823Phony targets like `clean` and `all`
clean_up()
Safety: You can reuse common names like setup() or clean_up() in every single file of your project without them ever bumping into each other.
Lesson 798Static functions for file scoping
cleanup
Most C programmers only tolerate goto in one specific scenario: cleanup.
Lesson 308Why goto is generally discouraged
clearerr
If you detect an error using ferror, handle the problem (like asking the user to plug a USB drive back in), you must call clearerr before the stream will let you work with it again.
Lesson 744Clearing file error indicators with clearerr
client_fd
You can keep the client_fd in a list of active customers while your main loop goes back to accept() on the server_fd to wait for the next person in line.
Lesson 1113Accepting client connections
clock_gettime()
What you'll learn: How to use the Linux-standard clock_gettime() function to measure code execution speed with nanosecond precision.
Lesson 1181Precise timing with `clock_gettime()`
CLOCK_MONOTONIC
Use clock_gettime() with CLOCK_MONOTONIC to capture execution times down to the nanosecond for accurate performance profiling.
Lesson 1181Precise timing with `clock_gettime()`
CLOCK_REALTIME
Unlike CLOCK_REALTIME (which tracks the actual time of day), a monotonic clock is a simple counter that never goes backward.
Lesson 1181Precise timing with `clock_gettime()`
clock_t
The clock() function returns a value of type clock_t.
Lesson 896Measuring CPU ticks with `clock`
clock()
In C, we use clock() from the <time.h> library to measure "CPU time." Think of this as a specialized stopwatch that only ticks when the processor is actually working on your specific instructions.
Lesson 896Measuring CPU ticks with `clock`Lesson 1180Measuring execution time with `clock()`Lesson 1184Understanding CPU cycles vs. Wall time
CLOCKS_PER_SEC
However, because different computers process ticks at different speeds, we divide that number by a constant called CLOCKS_PER_SEC to convert those abstract ticks into human-readable seconds.
Lesson 896Measuring CPU ticks with `clock`Lesson 1180Measuring execution time with `clock()`
Clockwise/Spiral Rule
To solve this, programmers use a mental trick called the Clockwise/Spiral Rule.
Lesson 498The 'Clockwise/Spiral' rule for declarations
Close-on-Exec
Instead of manually closing every file before calling exec(), you can tag a file descriptor with the Close-on-Exec flag.
Lesson 1096The close-on-exec flag
coffee_cups > 0
In the example above, the loop checks the condition coffee_cups > 0.
Lesson 267The loop condition: when to stop
col
The break successfully stopped the col loop for row 2, but the row loop kept right on ticking.
Lesson 305Breaking out of nested loops: the limitation of break
collision
This is called a collision, and while the modular function is a great start, handling those overlaps is the next step in mastering hash tables.
Lesson 1032A simple modular hash functionLesson 1033Handling collisions with ChainingLesson 1035Hash table insertion
color
If you accidentally pass a Color enum into a function expecting a Direction enum, C will treat them both as integers and run the code.
Lesson 337Positional matching of argumentsLesson 667Type safety concerns with enums
Color.Red
You can have a Color.Red and a Alert.Red without any issues.
Lesson 666Scoped enum limitations in C
colSum
By declaring rowSum or colSum inside the outer loop, you ensure each line starts with a clean slate.
Lesson 413Summing rows and columns individually
column
The first set of brackets selects the row, and the second set selects the column.
Lesson 410Accessing elements using `[row][col]`
Columns
In programming terms, we call these Rows and Columns.
Lesson 407Declaring 2D arrays: Rows and Columns
comma operator
Instead of cluttering the body of your loop with extra manual increments, C allows you to use the comma operator to pack multiple expressions into the initialization and increment sections of the loop header.
Lesson 240The comma operator in `for` loopsLesson 288The comma operator in for loop headers
command
In the example above, if command is 'Q', the program enters at the first case.
Lesson 261Fall-through behavior: intentional and accidental
Command Line Interface (CLI)
Programming in C, however, requires you to step into the pilot’s cockpit: the Command Line Interface (CLI).
Lesson 12Introduction to the CLI
Commands
It uses a specific syntax of Targets, Dependencies, and Commands.
Lesson 61Introduction to `make` and Makefiles
commas
When you declare multiple variables on one line, you state the data type once, followed by your chosen names separated by commas, and ending with a single semicolon.
Lesson 76Multiple declarations in one line
commenting out code
In C programming, commenting out code is exactly like that.
Lesson 38Commenting out code for testing
commit
When you finish with your first table and need more space, you ask the librarian to commit the next table by turning on the lights.
Lesson 590Growing an arena with virtual memory
company.department.employee.salary
You can chain these as deeply as you need (e.g., company.department.employee.salary).
Lesson 624Accessing members of nested structs
compar
compar: This is the "callback." Since qsort can't compare your data using < or >, you must provide a pointer to a function that takes two items and returns an integer indicating which is "larger."
Lesson 876The generic signature of `qsort`
Comparator
To make it work, you must provide a Comparator.
Lesson 1060Writing a custom comparator for qsort
compare
A double equals sign (==) is used to compare two values (e.g., "is the number in this box equal to 5?").
Lesson 71Common beginner typosLesson 165Common pitfall: `=` vs `==`
Compare and Swap (CAS)
Compare and Swap (CAS) is a "lock-free" alternative built directly into the CPU hardware.
Lesson 1145Compare and swap (CAS) basics
compare_ints
By changing just the logic inside compare_ints (for example, returning val2 - val1), you can flip the entire sorting order to descending without ever touching the complex sorting algorithm itself.
Lesson 519The `qsort` callback mechanism
Compare-and-Swap (CAS)
The most common is Compare-and-Swap (CAS).
Lesson 1146Lock-free programming concepts
comparison and swap
In C, we call this a comparison and swap.
Lesson 1047Bubble Sort: The swap logic
comparison function
It handles the complex logic of sorting, but it requires you to provide a comparison function.
Lesson 877Writing an integer comparison functionLesson 1059Using C library 'qsort' function
Compilation
There is a crucial middle step: Compilation.
Lesson 803From source code to object files
compilation speed
Second, compilation speed: if you change the logic inside one .c file, the compiler usually only needs to reprocess that specific file, rather than your entire project.
Lesson 796Splitting code into `.c` and `.h`
compile-time
The "static" part of the name means the connection happens at compile-time.
Lesson 810What is a static library `.a`
compiled file
The timestamp of your compiled file (main.o or the final app).
Lesson 824Incremental builds and file timestamps
compiled language
C is a compiled language, which means it acts as a bridge between these two worlds.
Lesson 6C as a compiled language
compiler intrinsics
Think of compiler intrinsics as the "middle ground." They look and act like regular C functions, but the compiler treats them as special commands.
Lesson 964Compiler intrinsics as an alternative
compiling
Building a C program is usually a two-step dance: compiling (translating code into machine language) and linking (stitching those translations into a final app).
Lesson 804The `-c` flag for compilation
complex_calculation
If we had put complex_calculation inside the lock, every other thread would be forced to wait 100ms per turn, destroying the benefits of using threads in the first place.
Lesson 1127Critical section best practices
compound bitwise assignment operators
Just like you can use += as a shortcut for addition, C provides compound bitwise assignment operators.
Lesson 195Compound bitwise assignments
compute
Instead of writing separate functions for add_and_print and multiply_and_print, we write one compute function that accepts a "math rule" as an argument.
Lesson 518Passing functions as arguments
conciseness
The primary goal of the ternary operator is conciseness.
Lesson 206Syntax of `? :`
condition
When a programmer looks at a while or for loop, the first place they look to understand the logic is the condition inside the parentheses.
Lesson 268Updating the loop variable to avoid infinite loopsLesson 296Readability: when to avoid excessive breaksLesson 405Avoiding off-by-one errors in loops
condition ? result_a : result_b
When the compiler sees condition ? result_a : result_b, it decides the "shape" of the answer before the program even runs.
Lesson 209Type consistency in ternary branches
condition ? true : false
In the previous lesson, we learned that a basic ternary operator (condition ? true : false) is like a simple fork in the road.
Lesson 208Nesting ternary operators
condition ? value_if_true : value_if_false
condition ? value_if_true : value_if_false;
Lesson 206Syntax of `? :`Lesson 252The ternary operator (?:) as a shortcut
Condition Check (True/False)
To trace a loop, draw three columns: Iteration Number, Variable Values, and Condition Check (True/False).
Lesson 273Tracing while loop execution on paper
Condition Variable (CV)
A Condition Variable (CV) is the solution.
Lesson 1132Introduction to condition variables
Condition Variables
Instead of a loop that constantly checks a variable, we use Condition Variables.
Lesson 1133Waiting with pthread_cond_wait
conditional breakpoint
A conditional breakpoint allows you to stay hands-off until a specific logical condition is met, such as i == 500 or error_code != 0.
Lesson 835Setting conditional breakpoints
Conditional Compilation
Instead of writing separate programs for every OS, you can use conditional compilation to tell the compiler: "If this is a Windows machine, use this code; otherwise, use that code."
Lesson 780Using `#ifdef` and `#ifndef`Lesson 783Testing for platform-specific code
configuration
Variables separate the configuration of your build from the logic of your build.
Lesson 821Using variables in Makefiles
connect()
In the networking world, this initiates the "TCP Three-Way Handshake." It’s a polite exchange where your computer says "Hello," the server says "Hello back, ready?", and your computer says "Great, let’s talk." Once connect() returns successfully, the virtual circuit is open.
Lesson 1109The sockaddr_in structureLesson 1114Client-side connect()
const char *
You should use const char * every time you point to a string literal (text inside double quotes) or when you write a function that needs to read a string without changing it.
Lesson 485Using `const char *` for safety
const int
Unlike a collection of const int variables, an enum groups related constants together logically.
Lesson 116Macros vs. Const variablesLesson 664Enums vs constant integers
const int *
Use const int * when passing a large array or structure to a function if you want to ensure the function doesn't accidentally modify your original data.
Lesson 493Pointer to a constant (`const int *p`)Lesson 911Difference between `const int *` and `int * const`
const int * const ptr
Constant Pointer to Constant Data (const int const ptr)*
Lesson 496When to use `const` with pointers
const int *p
A const int *p lets you read the value it points to, but prevents you from modifying that value through the pointer.
Lesson 493Pointer to a constant (`const int *p`)
const int *ptr
Pointer to Constant Data (const int ptr)*
Lesson 496When to use `const` with pointers
const int RED = 0
Even if you use const int RED = 0;, the compiler still views that variable as just another number.
Lesson 664Enums vs constant integers
const int x = 10
If the variable was originally declared as const int x = 10;, the computer might store it in a read-only hardware segment.
Lesson 499Casting away `const` volatility
const type * const name
A const type * const name declaration creates a dual-layer lock, ensuring neither the data nor the pointer's destination can ever be modified.
Lesson 495Constant pointer to a constant
const void *
It takes two const void * pointers (which basically mean "pointers to something, I don't know what") and returns an integer:
Lesson 519The `qsort` callback mechanism
const void *a
You'll notice the comparison function uses const void *a.
Lesson 1059Using C library 'qsort' function
const void *src
const void src*: This is the source address.
Lesson 512The `memcpy` function signature
const volatile
const volatile creates a read-only variable that the compiler is forced to re-examine every time it is accessed because an external source (like hardware) can change it.
Lesson 917Combining `const` and `volatile`
constant pointer
However, there is one small catch: while a pointer is a variable that can be changed to point elsewhere, an array name is a constant pointer.
Lesson 471Array names as constant pointersLesson 476Pointer to the start of an arrayLesson 494Constant pointer to a value (`int * const p`)
constant pointer to a constant
This is a constant pointer to a constant.
Lesson 495Constant pointer to a constant
constants
It defines a list of constants (names that represent fixed numbers) that tell you the minimum and maximum values for every integer type.
Lesson 96The `<limits.h>` header file
constants.h
If physics.c includes constants.h, and graphics.c includes constants.h, and then main.c includes both physics.h and graphics.h, the contents of constants.h will be pasted into your main file multiple times.
Lesson 789The 'duplicate definition' errorLesson 802Dependency graphing in your head
Contact Name
Think of it like a Contact Name in your phone.
Lesson 614Improving code readability with typedef
container
Think of it this way: you use a semicolon at the end of an action, but not at the end of a heading or a container.
Lesson 22Semicolons as statement terminators
content
Each mailbox has two important parts: the content inside it (the mail) and a unique address painted on the outside (like 123 Main St).
Lesson 449The Address-of operator `&`
contiguous allocation
calloc (short for contiguous allocation) is the more polite sibling.
Lesson 869Contiguous allocation with `calloc`
converts any non-zero number into
The first ! converts any non-zero number into 0.
Lesson 174Operator `!` and boolean normalization
cookie_jar_locked
Corruption: You accidentally change the value of other variables (like the cookie_jar_locked flag above).
Lesson 431The danger of Buffer Overflow
cookies++
In the example above, cookies++ has the side effect of adding one to the variable.
Lesson 222Sequence points at `;`
cookiesInJar
If the condition is false right at the start (for example, if cookiesInJar was already 0), the code inside the loop will never execute at all.
Lesson 269Using while for indeterminate iterations
coordinate and a
If you are building a game, you might need an x coordinate and a y coordinate.
Lesson 76Multiple declarations in one line
copies
When you then call fork(), the child process inherits copies of both.
Lesson 1103Closing unused pipe ends
copy
In C, when you pass a variable to a function, it is like walking over to a photocopy machine, making a copy of your paper, and handing your friend the copy.
Lesson 344Visualizing the stack frame copyLesson 346Preparing for pass by referenceLesson 487Passing addresses to functionsLesson 488Modifying caller variables
Copy-on-Write (COW)
To solve this, Linux and Unix systems use a clever optimization called Copy-on-Write (COW).
Lesson 1075Process duplication and copy-on-write
copyOfNum
Because parameters are allocated their own unique space in memory, the function’s internal logic is "sandboxed." Even if the function reassigns a value to copyOfNum, it is only flipping bits in its own temporary workspace.
Lesson 342Memory allocation for parameters
core
This creates a file (often named core or something similar).
Lesson 837Debugging a Segfault from a core dump
Core Dump
A Core Dump is like a high-resolution photo of the crime scene.
Lesson 837Debugging a Segfault from a core dump
cos
What you'll learn: How to use the sin, cos, and tan functions from the math library and why you must convert degrees to radians first.
Lesson 863Trigonometric functions in radians
cos()
C's trigonometric functions require angles in radians, so always multiply degrees by (PI / 180.0) before passing them to sin(), cos(), or tan().
Lesson 863Trigonometric functions in radians
count != 0
When the if statement runs, it checks the first part: count != 0.
Lesson 253Short-circuit evaluation in logical AND
count = count + 1
If you accidentally wrote count = count + 1, your paper trace would quickly show the number getting larger and larger, alerting you to an "infinite loop" before you even touch your keyboard.
Lesson 268Updating the loop variable to avoid infinite loopsLesson 273Tracing while loop execution on paper
count == capacity
When count == capacity, we are out of room.
Lesson 1214Implementing a dynamic array for storage
count: %d
Printing just %d is confusing if you have five different variables; printing count: %d tells you exactly what you are looking at.
Lesson 1163Inspecting variable values at runtime
count(2)
It hasn't finished yet; it's waiting for count(2) to finish.
Lesson 359The call stack in recursion
count(3)
When count(3) is called, a frame for n=3 is pushed onto the stack.
Lesson 359The call stack in recursion
count++
In C, a standard integer operation like count++ isn't actually one step; the CPU must read the value, modify it, and write it back.
Lesson 268Updating the loop variable to avoid infinite loopsLesson 1140The need for atomic operationsLesson 1141Introduction to <stdatomic.h>Lesson 1147Volatile vs Atomic
countDown
Observe how this countDown function tracks its own progress:
Lesson 363Visualizing recursive depth
countdown = countdown - 1
Decrementing: countdown = countdown - 1;
Lesson 268Updating the loop variable to avoid infinite loops
counter
While the computer doesn’t care if you call a variable counter or q, your future self and your teammates definitely do.
Lesson 148Default initialization of static variablesLesson 320Floating point precision issues in loop conditionsLesson 1197Meaningful variable naming conventions
counter != 1.0
Since the condition counter != 1.0 remains true, the loop never stops.
Lesson 320Floating point precision issues in loop conditions
Counter pattern
In C programming, the Counter pattern works exactly like that handheld clicker.
Lesson 317The Counter pattern (counting occurrences)
counter variable
A counter variable initialized to 0 (this is your clicker).
Lesson 317The Counter pattern (counting occurrences)
counter++
In standard C, counter++ actually performs three steps: load the value, add one, and store it back.
Lesson 1124Understanding race conditionsLesson 1144Atomic fetch and addLesson 1145Compare and swap (CAS) basics
Countertop
If you need a "Secret Sauce" you just made, you look on your Countertop (the Current Directory) first.
Lesson 795Standard header search paths
CPU time
One thing to keep in mind: clock() measures CPU time.
Lesson 1180Measuring execution time with `clock()`
cpu_time_used
If you put a sleep(5) command in the middle of that code, the Wall Time would increase by five seconds, but the cpu_time_used would remain almost exactly the same.
Lesson 1184Understanding CPU cycles vs. Wall time
createGraph
Always ensure that every malloc used during your addEdge or createGraph functions has a corresponding free in your cleanup logic.
Lesson 1046Graph memory management
Critical Section
The code between these two functions is called the Critical Section.
Lesson 1126Locking and unlocking mutexes
Ctrl + Z
On Windows, press Ctrl + Z and then hit Enter.
Lesson 270Reading input until EOF with while
Ctrl+D
To leave the debugger and return to your normal terminal, type quit or press Ctrl+D.
Lesson 826Starting GDB with an executable
Ctrl+Z
On Windows, you press Ctrl+Z and then hit Enter.
Lesson 681EOF (End Of File) explained
ctype
Whenever you pass a character variable to a ctype function, always apply a cast.
Lesson 846The importance of casting to `unsigned char` in `ctype` functions
cubicle
Instead, they assign that task to a specific cubicle.
Lesson 347What is a Stack Frame?
current
When you use x++ (Postfix), the current value is used for the math first, and the variable updates only after the expression is finished.
Lesson 202Differences in expression resultsLesson 992Traversing the list with a while loop
current = current->next
To traverse a list, initialize a temporary pointer to the head and use current = current->next inside a while (current != NULL) loop to move from node to node.
Lesson 992Traversing the list with a while loop
current folder
Double Quotes " ": These are for "Local Files." The preprocessor starts looking in the current folder where your source code lives.
Lesson 766The `#include` directive for local files
current_health = 100
In the example above, the compiler sees current_health = 100;;.
Lesson 770The danger of semicolon in `#define`
current_stock
If current_stock becomes -5, your program is in a "corrupt" state.
Lesson 1173Writing a simple `assert()` check
current->next
If we called free(current) before assigning nextNode = current->next, we would be attempting to access current->next from memory that we no longer own.
Lesson 994Appending nodes to the tailLesson 997Memory cleanup for linked lists
currentTemp
In this example, even though currentTemp isn't higher than maxLimit, the >= operator returns true because they are equal.
Lesson 160Greater or equal `>=` and less or equal `<=`
cycles
However, your CPU doesn't think in seconds; it thinks in cycles.
Lesson 1184Understanding CPU cycles vs. Wall time

D

dashboard_lights
You have a variable called dashboard_lights where each bit represents a different light.
Lesson 182Setting bits with `|`
data loss
In programming, this "mess" is called data loss or overflow.
Lesson 132Safe downcasting techniques
data race
If both threads load the value before either has finished storing it, one increment is "lost." This is a classic data race.
Lesson 1142Atomic types like atomic_int
Data Segment
The actual text "Alice" is stored in a special, read-only neighborhood of your computer's memory called the Data Segment.
Lesson 149Memory segments: Stack vs. DataLesson 480Mutable vs immutable string memoryLesson 484Memory layout of string pointers
data type
When you declare multiple variables on one line, you state the data type once, followed by your chosen names separated by commas, and ending with a single semicolon.
Lesson 75The syntax of a declarationLesson 76Multiple declarations in one lineLesson 235The `sizeof` operator with types
data.bin
If you try to open data.bin in a normal text editor like Notepad, it will look like gibberish.
Lesson 725Writing raw bytes with fwrite
data/old.txt
Moving Files: You can use rename to move a file across directories on the same disk drive (e.g., from data/old.txt to archive/old.txt).
Lesson 756Renaming files with rename
database_save()
In C, this means testing how different modules or files interact—for example, checking if your database_save() function correctly handles the data passed to it by your user_input() function.
Lesson 1177Integration testing vs. Unit testing
database.h
If you ever find yourself wanting to include main.h inside database.h, stop!
Lesson 802Dependency graphing in your head
DataPacket
In the example below, we want our DataPacket to be exactly 8 bytes.
Lesson 575Manual padding in structures
day
Since that is true, it doesn't even bother checking if day is 7.
Lesson 248Logical OR (||) for combined conditions
days_until_expiration
By using days_until_expiration instead of d, you eliminate the need for a comment explaining what the number 30 means.
Lesson 1197Meaningful variable naming conventions
daysUntilEvent
If you are storing the number of days until an event, call it daysUntilEvent.
Lesson 37Readability best practices
daysUntilVacation
Instead of remembering a cryptic memory address, you give that space a friendly name, like playerScore or daysUntilVacation.
Lesson 73What is a variable?
DBL_DIG
Use FLT_DIG and DBL_DIG from <float.h> to know the maximum number of reliable decimal digits your variables can store before rounding errors occur.
Lesson 908Checking `FLT_DIG` and `DBL_DIG` for precision limits
deadlock
Since you are the one holding the lock, you’re stuck in a permanent stalemate called a deadlock.
Lesson 1130Recursive mutexes
deadlocks
It is also a powerful tool for avoiding deadlocks.
Lesson 1129Using pthread_mutex_trylock
Dear [NAME]
If you have a template for a formal letter that says Dear [NAME], and you tell your editor that [NAME] is "Alice," the editor simply swaps the text.
Lesson 772Defining function-like macros
DEBUG
Debug Modes: Running specific logs only when DEBUG is on and VERBOSE is also enabled.
Lesson 782The `defined()` operator
debug symbols
The -g flag tells the compiler to create debug symbols.
Lesson 825Compiling with debug symbols `-g`Lesson 1160Compiling with `-g` for debug symbols
DEBUG_FULL
Flexibility: You can create different "levels" of debugging (e.g., DEBUG_LOW, DEBUG_FULL) to control exactly how much information you see at any given time.
Lesson 787Managing debug prints with macros
DEBUG_LOW
Flexibility: You can create different "levels" of debugging (e.g., DEBUG_LOW, DEBUG_FULL) to control exactly how much information you see at any given time.
Lesson 787Managing debug prints with macros
DEBUG_MODE
Zero Overhead: When DEBUG_MODE is undefined, the printf isn't just hidden—it is physically removed from the compiled binary.
Lesson 780Using `#ifdef` and `#ifndef`Lesson 787Managing debug prints with macros
Decimal
In our daily lives, we use the decimal system (base-10), likely because we have ten fingers.
Lesson 119Integer literals (Hex, Octal, Binary)Lesson 179Understanding binary representation
declaration
However, if you want to use function A (defined in file_1.c) inside file_2.c, file_2.c needs a declaration so it knows the function's name, return type, and parameters.
Lesson 70The 'undefined reference' linker errorLesson 79Declaration vs. InitializationLesson 922Using `extern` with functions
declarations
In C, header files are meant for declarations (blueprints), not definitions (the actual bricks).
Lesson 377Role of the `.h` fileLesson 788The purpose of header filesLesson 793What should NOT go in a header
declare
You declare the variable in the header using extern (the "promise"), and you define it in exactly one .c file (the "reality").
Lesson 146The `extern` keyword for multi-file codeLesson 789The 'duplicate definition' errorLesson 925Common linkage errors and 'multiple definition'
declaring
When you see the asterisk paired with a data type (like int, char, or float), you are declaring a pointer.
Lesson 456The difference between `int *p` and `*p`
default
By including a default case that prints an error or logs a message, you turn a "silent ignore" into an "active notification." This makes debugging much faster because the code tells you exactly where it encountered an unexpected value.
Lesson 258Basic switch syntax and casesLesson 260The default case for unhandled valuesLesson 265Switch statement best practicesLesson 976Handling the `default` case in `_Generic`Lesson 977Type-based function overloading simulationLesson 1151Handling the 'Default' case in switch statements
Defensive Programming
In C programming, Defensive Programming is the art of building that vending machine.
Lesson 1148The philosophy of 'Trust No One'
define
Just remember: you can declare extern as many times as you want in different files, but you must define the variable (without extern) exactly once in only one file.
Lesson 146The `extern` keyword for multi-file codeLesson 789The 'duplicate definition' errorLesson 925Common linkage errors and 'multiple definition'
defined()
The defined() operator allows you to combine multiple macro existence checks into a single #if statement using logical operators like AND, OR, and NOT.
Lesson 782The `defined()` operator
defines
One file defines the variable (buys the book and puts it on the shelf).
Lesson 146The `extern` keyword for multi-file code
definitely lost
In Valgrind terms, the suitcase is definitely lost because you lost the direct pointer to it.
Lesson 566Reading 'definitely lost' reportsLesson 567Identifying 'indirectly lost' memory
definition
To understand this, you must know the difference between a declaration (a promise) and a definition (the delivery).
Lesson 70The 'undefined reference' linker errorLesson 330Function prototype syntaxLesson 797The `extern` keyword for variables
definitions
The source file (ending in .c) is the kitchen; it contains the definitions, which is the actual logic and code that performs the work.
Lesson 788The purpose of header filesLesson 793What should NOT go in a header
Department
For example, if you want to sort a list of employees by Department and then by Last Name, you would first sort by name, then perform a stable sort by department.
Lesson 1052Stability in sorting algorithms
Dependencies
A Makefile rule uses targets (the goal), dependencies (the requirements), and recipes (the commands) to automate your build process efficiently.
Lesson 61Introduction to `make` and MakefilesLesson 818Structure of a Makefile RuleLesson 819Targets, dependencies, and recipes
dependency
A Makefile consists of "rules." Each rule has a target (what you want to create), a dependency (what is needed to create it), and a recipe (the command to run).
Lesson 1211Writing the Makefile for the project
depth
If your recursion is behaving strangely, adding a depth parameter is the fastest way to see if you are stuck in an infinite loop or if your base case is being triggered too late.
Lesson 363Visualizing recursive depth
Depth-First Search (DFS)
In C, Depth-First Search (DFS) uses recursion to act as that string.
Lesson 1045Depth-First Search (DFS) recursion
Dequeue
If you think of a Queue like a line of people waiting for coffee, the Dequeue operation represents the person at the very front finishing their order and leaving the shop.
Lesson 1015Front and Rear pointersLesson 1017Dequeue operation logicLesson 1020Linked list queue implementationLesson 1044Breadth-First Search (BFS) logic
dereference
It represents "nowhere." If you try to dereference it (using the * operator to access the value at that address), your program doesn't just get confused—it crashes immediately with a "Segmentation Fault."
Lesson 462Checking for NULL before dereferencing
dereference operator
However, when you see *ptr inside a regular line of code (an expression), it is the dereference operator acting as a "go to" command.
Lesson 457The Dereference operator `*`Lesson 458Assigning values via pointers
dereferencing
In programming, this act of "following the map" to see what’s inside the house is called dereferencing, and we use the indirection operator (*) to do it.
Lesson 238The Indirection operator `*`Lesson 456The difference between `int *p` and `*p`Lesson 457The Dereference operator `*`
dereferencing a NULL pointer
In C, trying to access or modify the value at a NULL address is called dereferencing a NULL pointer.
Lesson 525Dereferencing the NULL pointer
designated initializers
C99 introduced designated initializers, which allow you to pick and choose which members to set using a period followed by the member name (the . operator).
Lesson 394Designated initializers (C99)Lesson 603Designated initializers in C99Lesson 625Initializing nested structuresLesson 660Initializing a union
dest = src
Because the assignment expression dest = src actually evaluates to the character being copied, the loop naturally ends when the null terminator (0) is reached.
Lesson 482Pointer-based `strcpy` implementation
dest[i] = src[i]
In earlier lessons, you likely copied strings using array indexing, like dest[i] = src[i].
Lesson 482Pointer-based `strcpy` implementation
destination
The function takes two arguments: the destination (where the text is going) and the source (where the text is coming from).
Lesson 424Copying strings with `strcpy`
destination must be large enough
The most important thing to remember is that the destination must be large enough to hold the incoming text.
Lesson 424Copying strings with `strcpy`
destroy
If you allocated your mutex on the heap (using malloc) or as a local variable in a function, calling destroy is mandatory to keep your system's memory footprint clean and predictable.
Lesson 1131Cleaning up mutex resources
detach
When you are done, use the detach command.
Lesson 838Attaching GDB to a running process
Device Drivers
In an OS, C is used to write Device Drivers.
Lesson 3C's role in Operating Systems
DeviceStatus
In the example above, the entire DeviceStatus struct technically fits into just 8 bits (1 byte).
Lesson 646Syntax for declaring bit-fields
DFS
When DFS calls itself, the computer puts the current node on the "Stack." It stays there, waiting, while the code explores the new branch.
Lesson 1045Depth-First Search (DFS) recursion
difftime
By using difftime, your code becomes "portable." This means it will work correctly whether the underlying system counts time in seconds, milliseconds, or even something more exotic.
Lesson 890Measuring intervals with `difftime`
dir
If you want to see what files are in your current folder, you don't look for icons; you type a command like ls (on Mac/Linux) or dir (on Windows).
Lesson 12Introduction to the CLI
directed
Edges can be undirected (like a two-way street where both people are friends) or directed (like a one-way street or a "follower" relationship on Twitter).
Lesson 1039Vertices and Edges definition
Direction
If you accidentally pass a Color enum into a function expecting a Direction enum, C will treat them both as integers and run the code.
Lesson 667Type safety concerns with enums
directives
It looks for specific instructions called directives.
Lesson 25Phase 1: The Preprocessor
directly into
When it comes time to build your final application, the linker looks inside this archive, finds the functions you actually used, and copies their binary code directly into your executable file.
Lesson 810What is a static library `.a`
directly into the
You might wonder why we don't just type A directly into the printf statement.
Lesson 51Printing characters with `%c`
display_age
The sequence point ensures that by the time display_age starts, my_age has officially become 26 in the computer's memory.
Lesson 226Function call sequence points
displayAddress
In the example below, notice that we don't need to use parentheses to cast our pointers when calling the displayAddress function.
Lesson 510Implicit conversion to `void *`
displayStats
In the code above, the variable names myLevel and myHealth don't actually matter to the displayStats function.
Lesson 337Positional matching of arguments
distance
Instead of counting the number of doors you’ve passed, C thinks about the distance (or offset) from the very beginning of the hallway.
Lesson 385Array indexing starting from zero
Divide and Conquer
The Divide and Conquer strategy suggests you shouldn't try to sort the whole pile at once.
Lesson 1053Divide and Conquer strategy
DJB2
While there are many ways to do this, the DJB2 algorithm, created by Dan Bernstein, is a classic favorite because it is incredibly fast and does an excellent job of spreading different strings across the table to avoid "collisions."
Lesson 1038String hashing with DJB2
document it
In C, the standard allows compiler creators (like GCC, Clang, or MSVC) to choose how certain operations work, provided they document it.
Lesson 943Implementation-defined behavior vs UB
Domains
Before you can send data over a network, you have to decide two things: "Where is this going?" and "How should the data behave?" In C, we define these using Domains and Types.
Lesson 1108Socket domains and types
Don't optimize yet
This impulse is what legendary computer scientist Donald Knuth warned against when he said, "Premature optimization is the root of all evil." In the practice of performance profiling, the first rule is actually a paradox: Don't optimize yet.
Lesson 1187The trap of premature optimization
door number
You need to know which floor the unit is on and then which door number it is on that specific floor.
Lesson 407Declaring 2D arrays: Rows and Columns
dot operator
To get something out of a backpack you are physically holding, you use the dot operator.
Lesson 601The dot operator for member accessLesson 617Arrow operator vs dot operator
double
While a double usually occupies 8 bytes of memory, a long double often uses 12 or 16 bytes (depending on your computer’s architecture), allowing it to store many more digits after the decimal point with significantly less rounding error.
Lesson 97Single precision `float`Lesson 98Double precision `double`Lesson 99The `long double` typeLesson 100Scientific notation in CLesson 101Precision loss and rounding errorsLesson 102Formatting decimals with `%.nf`Lesson 103The `<float.h>` header fileLesson 104Comparing floats for equalityLesson 120Floating-point suffixes (f, L)Lesson 123Hierarchy of types in expressionsLesson 125Risks of narrowing conversionsLesson 126The 'Usual Arithmetic Conversions'Lesson 128Common conversion pitfallsLesson 130Forcing floating-point divisionLesson 133Truncation during float-to-int castsLesson 150The addition operator `+`Lesson 151The subtraction operator `-`Lesson 153Integer division `/` truncationLesson 154Floating-point divisionLesson 155The modulo operator `%` with integersLesson 158Mixing int and float in arithmeticLesson 166Comparing floating-point numbersLesson 194Compound multiplication and divisionLesson 203Incrementing pointers (preview)Lesson 229Usual arithmetic conversionsLesson 230The `(type)` cast operatorLesson 231Truncation during castingLesson 234Safety with explicit castsLesson 235The `sizeof` operator with typesLesson 236The `sizeof` operator with variablesLesson 320Floating point precision issues in loop conditionsLesson 332Matching prototypes with definitionsLesson 334Common errors with missing prototypesLesson 338Type checking in function callsLesson 371Functions with unknown argumentsLesson 372The `stdarg.h` libraryLesson 374Extracting arguments with `va_arg`Lesson 452The size of a pointer variableLesson 463Incrementing pointers with `++`Lesson 509Casting `void *` to specific typesLesson 511Generic functions in CLesson 512The `memcpy` function signatureLesson 589Handling alignment within an arenaLesson 608Using typedef with primitive typesLesson 641Reordering members to reduce paddingLesson 643Alignment requirements for different typesLesson 647Restrictions on bit-field typesLesson 654Memory layout of a unionLesson 673Opaque types with header filesLesson 778Macros vs inline functions
double calculate_tax(double price)
If you changed the definition to double calculate_tax(double price), the compiler would throw a "conflicting types" error because the "Promise" at the top no longer matches the "Fulfillment" at the bottom.
Lesson 332Matching prototypes with definitions
double dereferencing
This process is called double dereferencing.
Lesson 505Accessing data through double dereference
double get_precision()
By adding double get_precision(); above the main function, you ensure the compiler knows exactly how much space to allocate for the return value.
Lesson 334Common errors with missing prototypes
Double indirection
Double indirection (or a pointer-to-pointer) adds one more layer.
Lesson 500Concept of double indirection
double quotation marks
To tell C, "This is just text, don't try to run it," you must wrap your words in double quotation marks (" ").
Lesson 43Printing literal strings
Double Quotes "filename.h"
Double Quotes "filename.h": These are for User Headers.
Lesson 795Standard header search paths
doubleNumber
When doubleNumber is called, C creates a new spot in the computer's memory specifically for the parameter x.
Lesson 341Understanding 'Pass by Value'
doubleNumber(myAmount)
When doubleNumber(myAmount) is executed, the program jumps to the function, calculates 50, and then essentially "replaces" the function call in your code with that value.
Lesson 328Returning values from functions
doubles
A crucial detail to remember is that pow() works with doubles (floating-point numbers).
Lesson 861Basic power and square root: `pow` and `sqrt`
Doubly Linked List
A Doubly Linked List changes this by adding a second door.
Lesson 999Updating the node struct
Doubly Linked List (DLL)
In a Doubly Linked List (DLL), every node has a prev pointer.
Lesson 1003Deleting without head traversal
down
Use up to inspect the variables of the function that called your current position, and down to return toward the active line of code.
Lesson 834Moving between frames with `up` and `down`
downcasting
In C, downcasting is the act of converting a larger data type (like a long) into a smaller one (like an int or char).
Lesson 132Safe downcasting techniques
Doxygen
In professional C programming, Doxygen is the tool that turns your comments into that polished instruction booklet (usually as an HTML website or PDF).
Lesson 1199Writing effective Doxygen comments
draw_circle() + draw_square()
If you write draw_circle() + draw_square(), precedence says you must add their results.
Lesson 227Order of evaluation vs Precedence
Drawer (the single pointer)
The Key opens a Drawer (the single pointer).
Lesson 500Concept of double indirection
dup()
The dup() system call creates a new file descriptor that points to the same open file as an existing one, sharing the same read/write position.
Lesson 1094Duplicating descriptors with dup()
dup2
If we want to redirect output to a file, we first open that file to get its descriptor, then use dup2 to copy that descriptor onto slot 1.
Lesson 1095Redirecting output with dup2()Lesson 1105Redirecting stdout to a pipe
dup2()
What you'll learn: How to use the dup2() system call to point a standard file descriptor toward a file, effectively "silencing" the terminal and sending data elsewhere.
Lesson 1095Redirecting output with dup2()Lesson 1105Redirecting stdout to a pipe
dynamic
Calling a function via a pointer allows your code to be dynamic.
Lesson 516Calling a function via a pointer
dynamic linker
The moment you launch the app, a specialized tool called the dynamic linker rushes to find those files and plug them into your program’s memory.
Lesson 816Runtime library loading and `LD_LIBRARY_PATH`
Dynamic Memory Allocation
Think of Dynamic Memory Allocation like renting a hotel room.
Lesson 868Allocating memory with `malloc` and `free`

E

EACCES
By specifically checking for EACCES, you can give your users helpful feedback—like "Please run this as an Administrator"—rather than leaving them staring at a generic "File not found" message or a sudden crash.
Lesson 740Introduction to errnoLesson 745Handling 'Permission Denied' errorsLesson 903When to use `errno` vs return codes
EAGAIN
If it equals EAGAIN, it means the resource is temporarily unavailable.
Lesson 1098Non-blocking I/O basics
eax
If you need to move data into a specific register (like the eax register on x86 processors) to talk to hardware, you use Inline Assembly.
Lesson 960The 'Clobber' list explainedLesson 961Direct register access
EBUSY
It immediately returns a specific error code (EBUSY).
Lesson 1129Using pthread_mutex_trylock
Edge
An Edge is the link between two vertices.
Lesson 1039Vertices and Edges definition
Edges
A Graph is a collection of Vertices (the data points) connected by Edges (the relationships between them).
Lesson 1039Vertices and Edges definition
Edit
Click Environment Variables, find the Path variable under "System variables," and click Edit.
Lesson 11Setting up MinGW on Windows
efficiency
The main reason developers still reach for C is efficiency.
Lesson 5Why C remains relevant today
either
The rule is simple: if either the first bit OR the second bit is a 1, the resulting bit is a 1.
Lesson 176Bitwise OR `|`
eject button
Think of a return statement as an eject button.
Lesson 312Function returns as a control flow mechanism
elements
Instead of giving you the raw number of bytes between two addresses, C tells you how many elements (lockers) fit between them.
Lesson 466Subtracting two pointersLesson 470Navigating memory blocks manuallyLesson 731Verifying bytes read vs expected
ELF
On Linux and most Unix-like systems, this file uses the ELF format, which stands for Executable and Linkable Format.
Lesson 808The executable ELF format
else if
If you find yourself writing more than three else if statements to check the value of the same integer or character, it is usually time to switch to a switch.
Lesson 246Else-if ladders for multiple conditionsLesson 264Comparing switch-case vs else-if ladders
else if (temp > 10)
If we had put else if (temp > 10) at the very top, a temperature of 25 would have triggered that block immediately, and the computer would have never checked if it was also over 20 or 30.
Lesson 246Else-if ladders for multiple conditions
else-if ladder
An else-if ladder is like walking down the hall and knocking on every single door: "Is this Room 1?
Lesson 264Comparing switch-case vs else-if ladders
emergency_system()
In the example above, if emergency_system() runs, every other function using speed is suddenly affected.
Lesson 142The dangers of global variables
emory
But for dynamic allocation, we use a function called malloc (short for memory allocation).
Lesson 537The malloc function signature
employee.homeAddress.city
It also makes your code "self-documenting." When you type employee.homeAddress.city, the path to the data is perfectly clear, following a logical flow from the container to the specific detail.
Lesson 623Defining a struct inside another struct
Empty
You might also use Peek (to look at the top item without removing it) or check if the stack is Empty.
Lesson 1006Stack abstract data type concept
encapsulation
This is a core pattern for encapsulation, ensuring that the "internals" of a module stay private.
Lesson 674Information hiding using void pointersLesson 918Internal vs external linkage basicsLesson 919The `static` keyword in global scope
end
We swap the characters at these positions using a temporary variable, increment start, decrement end, and repeat until they cross paths.
Lesson 283Initialization, condition, and increment flowLesson 440Reversing an array in placeLesson 441Reversing a string in place
End-of-File (EOF)
The most critical reason to close unused ends is to trigger the End-of-File (EOF) condition.
Lesson 1103Closing unused pipe ends
Endianness
When you move into advanced C, you’ll encounter Endianness: the order in which a CPU stores a multi-byte number in memory.
Lesson 952Network byte order and `htons`/`ntohs`Lesson 955Using masks for cross-platform bit logicLesson 956The `__builtin_bswap` compiler intrinsics
endPtr
The endPtr now points to the space right before "samples." If the string was "abc," input would equal endPtr, signaling that no conversion happened.
Lesson 873Converting strings to doubles with `strtod`
Enemy
A forward declaration is a way of saying to the compiler: "Hey, there is a struct named Enemy coming later.
Lesson 792Forward declarations in headers
enemy.c
If global_score is defined in game.h, and both player.c and enemy.c include it, they both end up trying to create their own version of that variable.
Lesson 925Common linkage errors and 'multiple definition'
enemy.h
However, in C, if you include a header file twice—perhaps because player.h and enemy.h both need physics.h—the compiler sees two identical sets of instructions.
Lesson 792Forward declarations in headersLesson 1203Header guard best practices
energy
When it subtracts 10, it is only changing that local "copy." The global energy at the top of the file remains 100.
Lesson 340Shadowing variables in functions
Engine
If you have a Car struct, and inside that Car is an Engine struct, and inside that Engine is a FuelPump struct, you can’t jump straight to the pump.
Lesson 624Accessing members of nested structs
engine_running
In the first example, because engine_running is 0 (false), !engine_running becomes 1 (true).
Lesson 167Logical NOT `!`
engine_temp
If you try to access engine_temp or call log_internal_state() from a different file like main.c, the compiler will act as if they don't exist.
Lesson 919The `static` keyword in global scope
engine.c
Imagine you have a file called engine.c that handles internal calculations.
Lesson 919The `static` keyword in global scope
ENOENT
For example, if you try to open a file and it fails, errno tells you if the file is missing (ENOENT) or if you simply don't have permission to see it (EACCES).
Lesson 740Introduction to errnoLesson 903When to use `errno` vs return codes
Enqueue
When a new piece of data arrives (called Enqueue), we move the rear forward and place the data there.
Lesson 1015Front and Rear pointersLesson 1016Enqueue operation logicLesson 1020Linked list queue implementation
entire structure
If you need to get the address of the data, you must point to the entire structure itself, rather than the tiny bit-fields hidden inside it.
Lesson 652Limitations of bit-field addresses
entry
We assume the hash table is an array of Entry structs.
Lesson 20The `main()` function entry pointLesson 1036Hash table lookup
Entry Point
The Header: This is the "shipping label." It tells the OS which processor the file was built for (like x86 or ARM) and the Entry Point, which is the memory address where the code actually starts.
Lesson 808The executable ELF format
enumeration
An enumeration (or enum) is a user-defined type that allows you to assign names to numbers.
Lesson 661Defining an enum type
envelopes
Instead, the box contains a row of envelopes.
Lesson 506Command line arguments `char **argv`
Environment Variables
Click Environment Variables, find the Path variable under "System variables," and click Edit.
Lesson 11Setting up MinGW on WindowsLesson 1071Environment variables in C
Epsilon
We do this by defining a very small threshold called epsilon.
Lesson 104Comparing floats for equalityLesson 166Comparing floating-point numbers
equality operator
The double equals sign (==), however, is the equality operator.
Lesson 161The equality operator `==`
erases everything
Behavior: If the file already exists, C erases everything inside it the moment you open it.
Lesson 710Understanding file modes: r, w, a
errno = 0
By setting errno = 0 right before strtol, you guarantee that if errno is non-zero afterward, it was definitely caused by that specific line of code.
Lesson 900Resetting `errno` before library calls
error cleanup block
While goto is often discouraged because it can create "spaghetti code," the C community widely accepts one specific pattern: the error cleanup block.
Lesson 310Legitimate use case: error cleanup blocks
error_code != 0
A conditional breakpoint allows you to stay hands-off until a specific logical condition is met, such as i == 500 or error_code != 0.
Lesson 835Setting conditional breakpoints
error: expected ';' before 'return'
The compiler will likely say: error: expected ';' before 'return'.
Lesson 66Reading compiler error messages
escape character
To solve this, we use a "magic wand" called the escape character: the backslash (\).
Lesson 46Escaping double quotes
every
Condition: Before every lap, the computer asks: "Is this true?" If yes, it runs the code inside the curly braces.
Lesson 172Building complex logical expressionsLesson 283Initialization, condition, and increment flow
every time
In the example above, if count were a normal variable, it would be reset to 0 every time incrementCounter was called, and the program would just print "1" three times.
Lesson 920The `static` keyword inside functions
exact same spot
Using the == operator checks if two pointers are pointing at the exact same spot in memory.
Lesson 467Pointer comparison with `==` and `<`
exactly once
You should call srand() exactly once at the very beginning of your main() function.
Lesson 885Seeding the generator with `srand`Lesson 886Why you should only seed once
eXamine
The x command (short for eXamine) allows you to look at a specific memory address and see exactly what bits are sitting there, regardless of what the variable type claims to be.
Lesson 839Examining raw memory with `x`
example.c:5
The first "by" line is the "smoking gun." It points to example.c:5.
Lesson 566Reading 'definitely lost' reports
exec
The FD_CLOEXEC flag ensures that a file descriptor is automatically closed when a process calls exec, preventing sensitive data leaks to child programs.
Lesson 1081Replacing process images with execl()Lesson 1096The close-on-exec flag
exec()
In systems programming, we rarely use exec() by itself because it is "destructive"—it completely replaces your current program with a new one, meaning any code after the exec() call never runs.
Lesson 1075Process duplication and copy-on-writeLesson 1083Combining fork() and exec()Lesson 1096The close-on-exec flag
execl
While there are several helper functions (like execl or execvp), they all eventually call the system call execve.
Lesson 1080The execve() family overview
execl()
When you call execl(), the operating system wipes the current program's memory—its code, variables, and stack—and loads a new executable in its place.
Lesson 1081Replacing process images with execl()
Executable
If everything matches up, the Linker spits out a single file: the Executable (like a.out on Linux or program.exe on Windows).
Lesson 6C as a compiled languageLesson 9Role of the CompilerLesson 28Phase 4: The Linker
executable binary
The compiler is the chef that takes your instructions and performs the "work" to produce a finished dish—in this case, an executable binary.
Lesson 30Creating an executable binary
executable file
After you have written your code and asked the compiler to translate it, you are left with a finished product: the executable file.
Lesson 31How the OS runs a programLesson 32Executing from the command line
execv()
Once execv() is called successfully, the current process is wiped clean, and the new program starts fresh using the strings you provided in your array as its own argv.
Lesson 1082Passing arguments to execv()
execve
Think of execve as a "body snatcher." When a process calls it, the process doesn't die, but its "soul" (the code it was running) is instantly wiped out and replaced by a new program.
Lesson 1080The execve() family overview
execve()
If the child then calls execve() to transform into a completely different program (like running ls or a custom script), those file descriptors stay open.
Lesson 1096The close-on-exec flag
execvp
While there are several helper functions (like execl or execvp), they all eventually call the system call execve.
Lesson 1080The execve() family overview
exit code
In C, this status report is an integer known as the exit code.
Lesson 1072Process termination and exit codes
exit(0)
Use exit(0) for a clean, immediate shutdown and exit(1) to stop the program when a fatal error occurs.
Lesson 313Exiting the program with exit()
exit(1)
Instead of manually writing your cleanup code before every single exit(1) or return statement, you can register your cleanup function once at the start of main.
Lesson 313Exiting the program with exit()Lesson 875Cleaning up at exit with `atexit`
exp()
Think of exp() as a "growth machine." If you tell it how much time has passed, it tells you how much something has grown.
Lesson 862Exponential and logarithmic functions: `exp`, `log`, `log10`
exp(x)
Use exp(x) for powers of $e$, log(x) for the natural logarithm, and log10(x) for base-10 calculations.
Lesson 862Exponential and logarithmic functions: `exp`, `log`, `log10`
expectation
const is about permission (Can my code change this?), while volatile is about expectation (Can the outside world change this?).
Lesson 917Combining `const` and `volatile`
expected
Instead of giving up or locking, we simply grab the new expected value and try the calculation again.
Lesson 1145Compare and swap (CAS) basics
explicit
When we declare a pointer like int myPtr or char myPtr, we are being explicit.
Lesson 461Implicit vs explicit pointer types
explicit casting
If your data is already stored in integer variables, you can use explicit casting to temporarily "pretend" a variable is a float for that specific calculation.
Lesson 130Forcing floating-point divisionLesson 131Casting between char and int
explicit declaration
An explicit declaration, commonly called a function prototype, is like giving the compiler a "heads-up." You provide the function's signature at the very top of your file, before main().
Lesson 329Implicit vs. explicit declarations
explicit type
The address might be exactly the same, but the explicit type changes how the dereference operator (*) behaves.
Lesson 461Implicit vs explicit pointer types
exponential time complexity
This is called exponential time complexity.
Lesson 361Fibonacci: The cost of redundancy
expression
In C, the ternary operator is an expression, which means the entire thing must evaluate to a single, specific type of value.
Lesson 197Assignment expression return valueLesson 207Ternary as an expressionLesson 209Type consistency in ternary branches
Extended Asm
To make C and assembly talk to each other without crashing your program, we use Extended Asm.
Lesson 959Input and Output operands in assembly
extern
Using extern is the primary way to manage "Global State." While you should use global variables sparingly to avoid messy code, extern is essential for sharing configuration settings, game states, or hardware flags across a large program with dozens of different files.
Lesson 146The `extern` keyword for multi-file codeLesson 797The `extern` keyword for variablesLesson 918Internal vs external linkage basicsLesson 921Sharing variables across files with `extern`Lesson 922Using `extern` with functionsLesson 923Storage class specifier precedenceLesson 925Common linkage errors and 'multiple definition'
External fragmentation
External fragmentation is like having ten small gaps under your bed.
Lesson 582Internal vs External fragmentationLesson 584Allocation strategies: Best-fit
External Linkage
By default, any variable or function you declare outside of a function (at the file level) has external linkage.
Lesson 918Internal vs external linkage basicsLesson 919The `static` keyword in global scope
extra memory
However, if you have to take all the clothes out, lay them neatly on the bed in order, and then put them back into the suitcase, you are using extra memory.
Lesson 1051In-place sorting vs extra memory

F

F_OK
To check for a file's existence, we use a special constant called F_OK (short for File OK).
Lesson 762Checking if a file exists
F_SETLKW
Use fcntl() with F_SETLKW to ensure only one process at a time can modify a shared file, preventing data corruption through cooperative locking.
Lesson 1099File locking with fcntl()
F_WRLCK
We tell the kernel we want an F_WRLCK (Write Lock), which is exclusive.
Lesson 1099File locking with fcntl()
fabs
By having a dedicated function for floating-point numbers (fabs) and another for integers (abs), the computer can process the math using the most efficient instructions for that specific data type.
Lesson 866Absolute values for floats with `fabs`
fabs()
To compare two floats safely, you subtract one from the other, take the absolute value (using fabs() from the math.h library), and check if the result is less than your tiny threshold.
Lesson 104Comparing floats for equalityLesson 166Comparing floating-point numbersLesson 866Absolute values for floats with `fabs`Lesson 979Mathematical macros using `_Generic`
fabsf()
This is why the math library (math.h) gives us three versions of the absolute value function: fabsf() for floats, fabs() for doubles, and fabsl() for long doubles.
Lesson 979Mathematical macros using `_Generic`
fabsl()
This is why the math library (math.h) gives us three versions of the absolute value function: fabsf() for floats, fabs() for doubles, and fabsl() for long doubles.
Lesson 979Mathematical macros using `_Generic`
factorial
In mathematics, the factorial of a number (written as $n!$) is a perfect example.
Lesson 357Factorial as a recursive example
factorial(1)
This continues until it reaches factorial(1).
Lesson 357Factorial as a recursive example
factorial(4)
It "pauses" the calculation of 5 and moves inside to calculate factorial(4).
Lesson 357Factorial as a recursive example
factorial(5)
When you call factorial(5), the computer doesn't get an answer immediately.
Lesson 357Factorial as a recursive example
Failure
Any non-zero value (1 to 255) means Failure.
Lesson 1072Process termination and exit codes
fall-through
We call this fall-through, and we use the break keyword to stop it.
Lesson 259The role of the break statement in switch
FALLING
This prevents bugs where a program enters a state (like FALLING) but has no instructions on what to do next.
Lesson 668Using enums for state machines
fast_square
Each file gets its own private, optimized version of fast_square.
Lesson 368Inline functions in header files
fatal error
A fatal error is like realize you have no oven and no flour.
Lesson 68Warnings vs Fatal errors
fclose
By checking the return value of fclose, you catch errors that happened at the very last second when the system tried to commit the remaining buffered data to the hardware.
Lesson 746Handling 'Disk Full' scenariosLesson 757Deleting files with remove
fcntl
You can set this flag when you first open the file using the O_CLOEXEC flag, or later using fcntl.
Lesson 1096The close-on-exec flag
fcntl()
Use fcntl() with F_SETLKW to ensure only one process at a time can modify a shared file, preventing data corruption through cooperative locking.
Lesson 1098Non-blocking I/O basicsLesson 1099File locking with fcntl()
FD_CLOEXEC
The FD_CLOEXEC flag ensures that a file descriptor is automatically closed when a process calls exec, preventing sensitive data leaks to child programs.
Lesson 1096The close-on-exec flag
fd[0]
The pipe() function creates a pair of file descriptors—fd[0] for reading and fd[1] for writing—enabling a one-way flow of data between related processes.
Lesson 1100Anatomy of a pipeLesson 1101Creating pipes with pipe()Lesson 1104Piping data between parent and childLesson 1105Redirecting stdout to a pipe
fd[1]
The pipe() function creates a pair of file descriptors—fd[0] for reading and fd[1] for writing—enabling a one-way flow of data between related processes.
Lesson 1100Anatomy of a pipeLesson 1101Creating pipes with pipe()Lesson 1104Piping data between parent and childLesson 1105Redirecting stdout to a pipe
fd1
If you close fd1, you can still use fd2 to write to the file.
Lesson 1094Duplicating descriptors with dup()
fd2
If you close fd1, you can still use fd2 to write to the file.
Lesson 1094Duplicating descriptors with dup()
fencepost problem
If you quickly answered "10," you’ve just encountered the fencepost problem.
Lesson 319The Fencepost problem (off-by-one errors)
feof()
If you are at the very last character of a file, feof() still returns false because you haven't tried to read past that character yet.
Lesson 721Detecting the end of a file with feofLesson 722Why feof inside a loop condition is badLesson 743Distinguishing EOF from errors with ferror
feof(file_pointer)
The function feof(file_pointer) returns a "true" value (non-zero) only after an input operation has tried to read past the end of the file.
Lesson 721Detecting the end of a file with feofLesson 743Distinguishing EOF from errors with ferror
ferror
If you detect an error using ferror, handle the problem (like asking the user to plug a USB drive back in), you must call clearerr before the stream will let you work with it again.
Lesson 744Clearing file error indicators with clearerr
ferror()
By checking ferror(), you make your code "robust." You are telling the computer: "Stop if the file is over, but alert me if something actually broke."
Lesson 743Distinguishing EOF from errors with ferror
ferror(file_pointer)
ferror(file_pointer) returns true only if an actual read/write error occurred (like a disk failure or a lost network connection).
Lesson 743Distinguishing EOF from errors with ferror
ff
Using %#x would turn ff into 0xff, which is the standard way programmers write hexadecimal in their code.
Lesson 689Printing hex and octal values
fflush
Notice how we use fflush here to ensure the user sees the prompt before the program pauses for input.
Lesson 751Forcing a write with fflushLesson 755When to use fflush(stdout)
fflush()
This is why if you print a prompt without a \n, it might not appear immediately until you call fflush() or the program ends.
Lesson 746Handling 'Disk Full' scenariosLesson 749Full buffering vs Line bufferingLesson 751Forcing a write with fflushLesson 1158Flushing `stdout` for accurate logs
fflush(stdout)
In this example, calling fflush(stdout) ensures the user sees "Loading..." the very moment the line is reached, rather than two seconds later when the program ends.
Lesson 748How C buffers I/O for speedLesson 751Forcing a write with fflushLesson 755When to use fflush(stdout)Lesson 1158Flushing `stdout` for accurate logs
fgetc()
In C, many input functions like fgetc() or fscanf() return a special constant called EOF when they can no longer read data.
Lesson 732The file position indicatorLesson 743Distinguishing EOF from errors with ferrorLesson 754Performance: Single char vs block I/O
fgetc(file_pointer)
To read a character, you call fgetc(file_pointer).
Lesson 716Character I/O with fgetc and fputc
fgetpos
Use fgetpos and fsetpos with the fpos_t type to reliably "bookmark" and return to locations in files of any size.
Lesson 738Using fgetpos and fsetpos for large files
fibonacci(38)
In the code above, fibonacci(40) calls fibonacci(39) and fibonacci(38).
Lesson 361Fibonacci: The cost of redundancy
fibonacci(39)
In the code above, fibonacci(40) calls fibonacci(39) and fibonacci(38).
Lesson 361Fibonacci: The cost of redundancy
fibonacci(40)
In the code above, fibonacci(40) calls fibonacci(39) and fibonacci(38).
Lesson 361Fibonacci: The cost of redundancy
field width
To fix this, you can specify a field width.
Lesson 685Specifying field width for alignment
FIFOs are blocking
The most important thing to remember is that FIFOs are blocking.
Lesson 1107Creating FIFOs with mkfifo()
FILE *
Think of FILE * as a "handle." When you open a file, the operating system gives you a pointer to a FILE structure.
Lesson 708The FILE pointer typeLesson 718Formatted file output with fprintfLesson 723Rewinding a file to the startLesson 726Reading raw bytes with fread
FILE *fp
A FILE pointer (e.g., FILE *fp) is a sophisticated wrapper around a file descriptor.
Lesson 1092File descriptors vs FILE pointers
file descriptor
In C, a socket is represented by a simple integer called a file descriptor.
Lesson 1110Creating a socket with socket()
file descriptor (fd)
Think of a file descriptor (fd) as a claim check at a coat room.
Lesson 1092File descriptors vs FILE pointers
file descriptors
Use file descriptors when you are doing systems programming, like redirecting input/output, working with network sockets, or when you need fine-grained control over exactly when data hits the disk.
Lesson 1092File descriptors vs FILE pointersLesson 1093Standard streams (0, 1, 2)Lesson 1100Anatomy of a pipeLesson 1101Creating pipes with pipe()
FILE pointer
A FILE pointer (e.g., FILE *fp) is a sophisticated wrapper around a file descriptor.
Lesson 1092File descriptors vs FILE pointers
file position indicator
In C, every open file has a similar mechanism called the file position indicator.
Lesson 732The file position indicator
file scope
In C, a variable has file scope (or global scope) when it is defined outside of any function, usually at the very top of your .c file.
Lesson 137Global variables and file scope
file_1.c
However, if you want to use function A (defined in file_1.c) inside file_2.c, file_2.c needs a declaration so it knows the function's name, return type, and parameters.
Lesson 922Using `extern` with functions
file_2.c
However, if you want to use function A (defined in file_1.c) inside file_2.c, file_2.c needs a declaration so it knows the function's name, return type, and parameters.
Lesson 922Using `extern` with functions
file_a.h
In C, a circular dependency happens when file_a.h contains #include "file_b.h", but file_b.h also contains #include "file_a.h".
Lesson 794Circular dependency issues
file_b.h
In C, a circular dependency happens when file_a.h contains #include "file_b.h", but file_b.h also contains #include "file_a.h".
Lesson 794Circular dependency issues
file_logic.c
When you compile these files together, the compiler sees the extern declaration in main.c and leaves a "placeholder." During the final stage (linking), the computer finds the actual code in file_logic.c and plugs it in.
Lesson 922Using `extern` with functions
FILE*
In C, file streams (FILE*) work the same way.
Lesson 744Clearing file error indicators with clearerr
file1.c
In C, if you define int score = 0; in file1.c and then define int score = 0; again in file2.c, the compiler will get confused and throw a "multiple definition" error.
Lesson 921Sharing variables across files with `extern`
file2.c
In C, if you define int score = 0; in file1.c and then define int score = 0; again in file2.c, the compiler will get confused and throw a "multiple definition" error.
Lesson 921Sharing variables across files with `extern`
fileno()
You can actually extract the raw descriptor from a FILE pointer using the fileno() function.
Lesson 1092File descriptors vs FILE pointers
first dependency
$<: Refers to the first dependency (the source file needed to build it).
Lesson 822Automatic variables like `$@` and `$<`
first member
In C, when you initialize a union using curly braces { }, the compiler defaults to the first member defined in the union template.
Lesson 660Initializing a union
first piece of data
The safest strategy is to initialize your variables using the first piece of data you encounter.
Lesson 318Finding Min and Max in a loop
First-fit
In a First-fit mindset, you don't care if there is a "perfect" spot further down that fits your compact car exactly.
Lesson 583Allocation strategies: First-fit
fit into
It calculates how many full units of y fit into x and hands you the leftover piece.
Lesson 865Truncation and remainder: `trunc` and `fmod`
five integers
If you increment this pointer (array_ptr++), it jumps forward by the size of five integers at once, rather than just one.
Lesson 927Arrays of pointers vs Pointers to arrays
flag (e.g
To make it clear, we often use the # flag (e.g., %#x).
Lesson 689Printing hex and octal values
Flag Variable pattern
In C, this is the Flag Variable pattern.
Lesson 315The Flag Variable pattern
flag3
In the following example, we use a zero-width bit-field to ensure flag3 doesn't share a storage unit with the first two flags.
Lesson 651Zero-width bit-fields for alignment
flags
By using flags—extra bits of text added to the command—you can tell the compiler exactly how you want it to behave.
Lesson 57Basic `gcc` command flags
flips that
The second ! flips that 0 back into a 1.
Lesson 174Operator `!` and boolean normalization
float
If you provide two different numeric types—like an int and a float—C won't crash, but it will perform "Type Promotion." It will force the smaller type to become the larger type so that the final answer is consistent.
Lesson 52Printing decimals with `%f`Lesson 74Naming rules and identifiersLesson 97Single precision `float`Lesson 98Double precision `double`Lesson 99The `long double` typeLesson 100Scientific notation in CLesson 101Precision loss and rounding errorsLesson 102Formatting decimals with `%.nf`Lesson 103The `<float.h>` header fileLesson 104Comparing floats for equalityLesson 116Macros vs. Const variablesLesson 120Floating-point suffixes (f, L)Lesson 121What is type promotion?Lesson 123Hierarchy of types in expressionsLesson 124Automatic conversion in assignmentsLesson 125Risks of narrowing conversionsLesson 126The 'Usual Arithmetic Conversions'Lesson 128Common conversion pitfallsLesson 129The cast operator `(type)`Lesson 130Forcing floating-point divisionLesson 133Truncation during float-to-int castsLesson 135Readability and intent in castingLesson 150The addition operator `+`Lesson 151The subtraction operator `-`Lesson 153Integer division `/` truncationLesson 154Floating-point divisionLesson 155The modulo operator `%` with integersLesson 158Mixing int and float in arithmeticLesson 166Comparing floating-point numbersLesson 194Compound multiplication and divisionLesson 209Type consistency in ternary branchesLesson 229Usual arithmetic conversionsLesson 230The `(type)` cast operatorLesson 234Safety with explicit castsLesson 235The `sizeof` operator with typesLesson 262Switch restrictions: integral types onlyLesson 320Floating point precision issues in loop conditionsLesson 335Parameters vs. ArgumentsLesson 336Defining multiple parametersLesson 383Declaring an array with `type name[size]`Lesson 456The difference between `int *p` and `*p`Lesson 509Casting `void *` to specific typesLesson 511Generic functions in CLesson 520Defining `typedef` for function pointersLesson 527Pointer type-punning dangersLesson 599Defining a struct with the struct keywordLesson 606Returning a struct from a functionLesson 608Using typedef with primitive typesLesson 610Combining struct definition and typedefLesson 611Anonymous structs with typedef
float myVar = 3.14
If you are using a float (which takes up 4 bytes), but you write float myVar = 3.14;, the compiler actually sees a double (8 bytes) being squeezed into a smaller float container.
Lesson 120Floating-point suffixes (f, L)
float.h
By using the limits defined in float.h, you acknowledge that floating-point math is "fuzzy" and give your program a specific threshold for what counts as "close enough."
Lesson 907Precision and epsilon in `float.h`
float*
This rule assumes that pointers of different types (like int and float) will never point to the same memory location.
Lesson 527Pointer type-punning dangersLesson 936Strict aliasing rule violations
Floating-point types
Floating-point types (like double and float) are at the top because they handle decimals.
Lesson 229Usual arithmetic conversions
floats
%f: Used for floats (numbers with decimal points like 3.14).
Lesson 49Introduction to Format Specifiers
floor
You need to know which floor the unit is on and then which door number it is on that specific floor.
Lesson 407Declaring 2D arrays: Rows and ColumnsLesson 864Rounding with `ceil`, `floor`, and `round`
floor()
Conversely, if you are calculating how many full $10 gift cards a user can buy with $25.99, you use floor() because they don't quite have enough for that third card.
Lesson 864Rounding with `ceil`, `floor`, and `round`
Flow
Instead, think of printf() as leaving a trail of breadcrumbs that tell you two specific things: State and Flow.
Lesson 1157Strategic `printf()` debugging
FLT_DIG
Think of these numbers as a "guarantee." If FLT_DIG is 6, the C standard promises that any 6-digit decimal number can be stored and brought back without losing its value.
Lesson 908Checking `FLT_DIG` and `DBL_DIG` for precision limits
FLT_EPSILON
Use FLT_EPSILON from <float.h> to compare floats by checking if their difference is smaller than the machine's limit of precision.
Lesson 103The `<float.h>` header fileLesson 907Precision and epsilon in `float.h`
FLT_MAX
Instead of hard-coding a limit like 3.4028e+38, you use FLT_MAX, and your program will automatically adapt if it is ever moved to a different type of processor.
Lesson 103The `<float.h>` header file
fmod
Use trunc to discard decimals without rounding, and use fmod as the floating-point version of the modulo (%) operator.
Lesson 865Truncation and remainder: `trunc` and `fmod`
folder path
Use -L to specify the folder path and -l to specify the library name (minus the 'lib' prefix).
Lesson 815Linking with shared libraries `-l` and `-L`
followed by a number between the
By adding a 0 followed by a number between the % and the d, you tell C: "I want this number to be at least this many characters wide, and if it isn't, fill the empty space with zeros."
Lesson 688Zero-padding numerical output
following
If you add parentheses greet(), you are following the recipe (calling the function).
Lesson 515Taking the address of a function
foo
Instead of manually guessing which register the variable foo is in, you let C handle the mapping:
Lesson 959Input and Output operands in assembly
FOPEN_MAX
You can actually see what your specific environment guarantees by printing the FOPEN_MAX constant found in the <stdio.h> library.
Lesson 715The maximum number of open files
for
Complexity isn't just about length; it’s about "indentation depth." Every time you add an if statement inside a for loop inside another if statement, you are forcing the next programmer to hold a massive mental map of conditions just to understand one line of code.
Lesson 4Standards: ANSI C vs C99 vs C11Lesson 120Floating-point suffixes (f, L)Lesson 224The comma operator `,`Lesson 240The comma operator in `for` loopsLesson 250Curly brace requirements for single vs multi-lineLesson 269Using while for indeterminate iterationsLesson 282The three parts of a for loop headerLesson 283Initialization, condition, and increment flowLesson 284Using the for loop as a counterLesson 285Scope of the loop variable in C99 vs C89Lesson 286Counting backwards with decrement operatorsLesson 287Using non-unit increments (e.g., i += 2)Lesson 288The comma operator in for loop headersLesson 289Optional components: the for(;;) infinite loopLesson 292Continue in while vs for loopsLesson 296Readability: when to avoid excessive breaksLesson 298Introduction to loops inside loopsLesson 300Using nested loops to print 2D gridsLesson 308Why goto is generally discouragedLesson 311The dangers of 'spaghetti code'Lesson 321Choosing the right loop for the taskLesson 353Concept of self-calling functionsLesson 358Iteration vs. Recursion comparisonLesson 364When to avoid recursionLesson 390Reading array values from user inputLesson 396Assigning values vs initializing arraysLesson 399Using `for` loops for array traversalLesson 400Printing array elements in a sequenceLesson 401Reverse traversal of an arrayLesson 402Finding the maximum value in an arrayLesson 403Calculating the sum and averageLesson 404Linear search for a specific valueLesson 405Avoiding off-by-one errors in loopsLesson 406Modifying all elements in a single passLesson 413Summing rows and columns individuallyLesson 430Setting memory blocks with `memset`Lesson 443Counting vowels and consonantsLesson 536Header file stdlib.h for allocationLesson 546Resizing blocks with reallocLesson 630Declaring an array of structsLesson 636Searching through struct arraysLesson 855Setting memory blocks with `memset`Lesson 964Compiler intrinsics as an alternativeLesson 966C99: Variable declarations and `bool`Lesson 972Specifying the standard with `-std=` flagsLesson 1061Linear Search on arraysLesson 1065Time complexity: O(n) vs O(log n)Lesson 1187The trap of premature optimizationLesson 1190Loop unrolling explainedLesson 1196Consistency: K&R vs. Allman style
for (initialization; condition; increment)
for (initialization; condition; increment)
Lesson 283Initialization, condition, and increment flow
for byte
u: The unit size (b for byte, h for halfword/2 bytes, w for word/4 bytes).
Lesson 839Examining raw memory with `x`
for decimal
f: The format (e.g., x for hex, d for decimal, c for char).
Lesson 839Examining raw memory with `x`
for halfword/2 bytes
u: The unit size (b for byte, h for halfword/2 bytes, w for word/4 bytes).
Lesson 839Examining raw memory with `x`
for hex
f: The format (e.g., x for hex, d for decimal, c for char).
Lesson 839Examining raw memory with `x`
for input or
The File Descriptor: The ID of the file or stream (like 0 for input or 1 for output).
Lesson 1097Reading and writing raw bytes
for Red
You could use the number 0 for Red, 1 for Yellow, and 2 for Green.
Lesson 661Defining an enum type
for rows and
By convention, we use i for rows and j for columns.
Lesson 411Nested `for` loops for 2D traversal
for true or
In C, a comparison isn't just an idea—it is an expression that evaluates to the integer 1 for true or 0 for false.
Lesson 159Greater than `>` and less than `<`Lesson 164Boolean result of comparisons
for you and
The program might work fine on your machine but crash on another, simply because the "garbage" value happened to be 0 for you and 4291 for someone else.
Lesson 569Detecting uninitialized value usage
for(;;)
A for(;;) loop creates an intentional infinite loop by omitting all three control expressions, serving as the heartbeat for programs that need to run indefinitely.
Lesson 289Optional components: the for(;;) infinite loop
Fork then Exec
To start a new program while keeping our original one alive, we use a two-step pattern: Fork then Exec.
Lesson 1083Combining fork() and exec()
formal shoes
To let someone in, they must meet two criteria: they must have an invitation AND they must be wearing formal shoes.
Lesson 253Short-circuit evaluation in logical AND
format
Those three dots (the ellipsis) tell the compiler: "Expect the format string first, but after that, anything goes."
Lesson 376How `printf` works internally
format specifier
If you type printf("My age is age");, the computer will literally print the word "age." To fix this, we use a special placeholder called a format specifier.
Lesson 50Printing integers with `%d`Lesson 87Printing integers with `%d` and `%ld`Lesson 99The `long double` type
format specifiers
We do this using format specifiers, which are special codes starting with a percent sign (%).
Lesson 49Introduction to Format SpecifiersLesson 684Format specifiers recap
format string
The format string (the text inside quotes containing %d).
Lesson 50Printing integers with `%d`
forward declaration
A forward declaration is a way of saying to the compiler: "Hey, there is a struct named Enemy coming later.
Lesson 628Forward declarations of structsLesson 792Forward declarations in headersLesson 794Circular dependency issues
four
Even though "Cat" looks like it only has three characters, it actually takes up four slots in memory:
Lesson 417The Null Terminator `\0` character
fpos_t
Future-proofing: Even if files get even larger in the future, your code using fpos_t will likely still work because the compiler manages the underlying data size.
Lesson 738Using fgetpos and fsetpos for large files
fprintf()
When you call printf() or fprintf(), C doesn't usually talk to the hardware right away.
Lesson 748How C buffers I/O for speedLesson 1092File descriptors vs FILE pointers
fputc
Use fgetc to read and fputc to write a single character, always checking for EOF with an integer variable to know when the file ends.
Lesson 716Character I/O with fgetc and fputc
fputc()
Functions like fgetc() or fputc() handle one character at a time.
Lesson 754Performance: Single char vs block I/O
fputs
What you'll learn: How to safely read and write whole lines of text in files using the fgets and fputs functions.
Lesson 717String I/is with fgets and fputsLesson 746Handling 'Disk Full' scenarios
fragmentation
Furthermore, arenas eliminate fragmentation and "manual memory management exhaustion." You don't have to track every single pointer or worry about a free() call for every malloc().
Lesson 578Motivation for custom allocatorsLesson 591Trade-offs of arena vs malloc
fread()
What you'll learn: How to use fread() to instantly reconstruct complex data structures from a binary file back into your program's variables.
Lesson 727The size and count parametersLesson 729Reading structs back into memoryLesson 754Performance: Single char vs block I/O
Free after use
The Free after use rule states that for every single time you ask for memory, you must explicitly give it back using the free() function once you are finished with it.
Lesson 553The 'Free after use' rule
free list
Think of a free list like a scavenger hunt.
Lesson 579Building a simple free list
FREE_SHIPPING_THRESHOLD
Readability: FREE_SHIPPING_THRESHOLD tells a story; 50.00 is just a digit.
Lesson 771Avoiding magic numbers with macros
free()
If you call free() on the same pointer a second time, you are "double-freeing." This isn't just a minor mistake; it’s a critical error that usually causes your program to crash instantly or, worse, creates a security hole that hackers can exploit.
Lesson 522Dangling pointers after `free`Lesson 523Memory leaks and lost pointersLesson 528Tools for pointer debugging (Valgrind)Lesson 534Scope of heap-allocated dataLesson 535Stack pointers vs Heap pointersLesson 542Why freeing NULL is safeLesson 550Definition of a memory leakLesson 551Losing the last pointer to a blockLesson 553The 'Free after use' ruleLesson 554Double-freeing a pointerLesson 555Invalid pointer increments before freeLesson 557What is a dangling pointerLesson 559Setting pointers to NULL after freeLesson 562Use-after-free vulnerabilitiesLesson 563Out-of-bounds array access on heapLesson 564Installing Valgrind MemcheckLesson 566Reading 'definitely lost' reportsLesson 581The concept of memory fragmentationLesson 585What is a Memory ArenaLesson 586Linear or Bump allocatorsLesson 587Resetting an arena in one stepLesson 588Arena allocation for frame-based tasksLesson 591Trade-offs of arena vs mallocLesson 593Using mmap for large allocationsLesson 596The munmap functionLesson 621Freeing dynamically allocated structsLesson 988Freeing the dynamic arrayLesson 996Deleting a node by valueLesson 997Memory cleanup for linked listsLesson 1008Linked list-based stack implementationLesson 1164What is a memory leak?Lesson 1166Identifying 'Invalid Read' errorsLesson 1167Tracking down 'Use After Free' bugsLesson 1170Cleaning up heap memory before exitLesson 1171Using AddressSanitizer (`-fsanitize=address`)
free(current)
If we called free(current) before assigning nextNode = current->next, we would be attempting to access current->next from memory that we no longer own.
Lesson 997Memory cleanup for linked lists
free(graph)
Because of this layering, you cannot simply call free(graph) and expect everything to disappear.
Lesson 1046Graph memory management
free(NULL)
Calling free(NULL) is completely safe and does nothing, so you don't need to wrap every free() call in an if statement.
Lesson 522Dangling pointers after `free`Lesson 542Why freeing NULL is safeLesson 554Double-freeing a pointer
free(numbers)
It will tell you exactly which line caused the invalid write and confirm that 40 bytes were "definitely lost" because you forgot to free(numbers).
Lesson 528Tools for pointer debugging (Valgrind)
free(ptr)
When you call free(ptr), you are telling the computer, "I am done with this memory; you can give it to someone else." The computer marks that memory as available, but it does not wipe the address stored inside your ptr variable.
Lesson 554Double-freeing a pointerLesson 559Setting pointers to NULL after freeLesson 562Use-after-free vulnerabilitiesLesson 868Allocating memory with `malloc` and `free`
free(scores)
When you call free(scores), the memory is released, but the pointer scores still holds the address of that memory.
Lesson 988Freeing the dynamic array
freopen
By calling freopen, you flip a lever that reroutes that same train onto a different track heading toward "File.txt." The train (your code) doesn't know the tracks have changed; it just keeps moving forward, but it ends up at a different destination.
Lesson 760Redirecting streams with freopen
from
In this code, we subtract b from INT_MAX to find the "ceiling." If a is already higher than that ceiling, adding b is guaranteed to overflow.
Lesson 1154Safe integer arithmetic and overflow checks
front
If the queue was completely empty before this operation, we also have to point the front at this first element, as they are now both the first and last person in line.
Lesson 1015Front and Rear pointersLesson 1016Enqueue operation logicLesson 1017Dequeue operation logicLesson 1018Circular array implementationLesson 1020Linked list queue implementation
front++
In a simple array-based queue, this means front++.
Lesson 1017Dequeue operation logic
fscanf()
In C, many input functions like fgetc() or fscanf() return a special constant called EOF when they can no longer read data.
Lesson 743Distinguishing EOF from errors with ferror
fseek
In text mode, fseek is more like a teleportation device that only works reliably if you have the exact coordinates provided by the file system itself.
Lesson 733Moving the pointer with fseekLesson 738Using fgetpos and fsetpos for large filesLesson 739Risks of seeking in text mode
fseek()
The fseek() function requires three pieces of information: the file pointer, how many bytes to move (the offset), and a starting reference point.
Lesson 733Moving the pointer with fseekLesson 734The SEEK_SET, SEEK_CUR, SEEK_END constantsLesson 736Finding file size using seek and tellLesson 758Creating temporary files with tmpfile
fsetpos
You don't necessarily need to know what’s written on the bookmark; you just hand it back to the computer later using fsetpos, and the computer instantly flips to that exact spot.
Lesson 738Using fgetpos and fsetpos for large files
ftell
Saving State: If you are processing a massive data file and need to pause, you can save the ftell value to a variable or a config file, then resume from that exact byte later.
Lesson 735Getting current position with ftellLesson 736Finding file size using seek and tellLesson 738Using fgetpos and fsetpos for large filesLesson 739Risks of seeking in text mode
ftell()
ftell(): We use this to report the current byte offset.
Lesson 736Finding file size using seek and tell
ftell(file)
When you call ftell(file), it returns a long integer representing how many bytes away from the start of the file you are.
Lesson 736Finding file size using seek and tell
FuelPump
If you have a Car struct, and inside that Car is an Engine struct, and inside that Engine is a FuelPump struct, you can’t jump straight to the pump.
Lesson 624Accessing members of nested structs
full buffering
When you write to a file, C switches to full buffering.
Lesson 749Full buffering vs Line buffering
func()
To avoid these bugs, never pass expressions that change values (like i++, --j, or func()) into a macro.
Lesson 775Side effects in macro arguments
function
In C, a function is exactly like that manual.
Lesson 322What is a function?
function A
However, if you want to use function A (defined in file_1.c) inside file_2.c, file_2.c needs a declaration so it knows the function's name, return type, and parameters.
Lesson 922Using `extern` with functions
function definition
The Source (math_utils.c): Contains the function definition.
Lesson 796Splitting code into `.c` and `.h`
function pointer
A function pointer is simply a variable that stores that starting address, allowing you to "hold" a function in your hand and pass it around your code.
Lesson 514Syntax of function pointersLesson 519The `qsort` callback mechanismLesson 675Implementing an interface with function pointers in structsLesson 928Declaring pointers to functions
Function Prototype
A function prototype acts like an "upcoming attractions" trailer; it tells the compiler, "Hey, a function with this specific name and shape exists, and you’ll find the full details later."
Lesson 326Placement of functions in a fileLesson 329Implicit vs. explicit declarationsLesson 330Function prototype syntaxLesson 331Benefits of forward declarationLesson 796Splitting code into `.c` and `.h`Lesson 965K&R C vs C89/C90 ANSI
function prototypes
A header file typically contains function prototypes.
Lesson 377Role of the `.h` file
function-like macro
A function-like macro takes this a step further.
Lesson 772Defining function-like macros
FunctionA
If FunctionA needs to call FunctionB, but FunctionB also needs to call FunctionA, it is impossible to define one "before" the other.
Lesson 331Benefits of forward declarationLesson 454Introduction to the stack frame
functionA()
When main() calls functionA(), a new frame for functionA is placed (or "pushed") on top of main.
Lesson 454Introduction to the stack frame
functional
Think of the ternary operator as a functional tool and if-else as a procedural tool.
Lesson 210Ternary vs If-Else for assignments
FunctionB
If FunctionA needs to call FunctionB, but FunctionB also needs to call FunctionA, it is impossible to define one "before" the other.
Lesson 331Benefits of forward declaration
functions have a home in memory
Just like variables, functions have a home in memory.
Lesson 515Taking the address of a function
functions.c
If you forgot to include functions.c, the linker would throw an error because it found a "call" but no "destination."
Lesson 59Compiling multiple source filesLesson 806Linking multiple object files
functions.o
The linker then looks at main.o, sees the request for greet(), finds the actual code for greet() inside functions.o, and patches the two together into the final my_program executable.
Lesson 806Linking multiple object files
fwrite
What you'll learn: How to use the two numerical arguments in fread and fwrite to precisely control how much data moves between your memory and a binary file.
Lesson 725Writing raw bytes with fwriteLesson 727The size and count parametersLesson 728Writing entire structs to diskLesson 752Setting custom buffers with setvbufLesson 754Performance: Single char vs block I/O
fwrite()
Use fwrite() with the "wb" mode to copy raw structs from memory to a file, providing a fast and efficient way to persist your data store.
Lesson 727The size and count parametersLesson 729Reading structs back into memoryLesson 754Performance: Single char vs block I/OLesson 1217Saving the data store to a binary file

G

game_logic.c
We define our global variable in main.c, and we want to modify it inside a function in game_logic.c.
Lesson 146The `extern` keyword for multi-file code
game.c
This means if you define int global_score = 100; in game.c, you can access that exact same memory location in ui.c by using the extern keyword.
Lesson 918Internal vs external linkage basics
game.h
If you accidentally include it twice—perhaps because main.c includes player.h and also includes game.h, which itself includes player.h—the compiler sees the same code twice in a row.
Lesson 789The 'duplicate definition' errorLesson 790Creating basic include guardsLesson 925Common linkage errors and 'multiple definition'
garbage value
This leftover data is known as a garbage value.
Lesson 77Garbage values and uninitialized variables
gcc -E
Use gcc -E to stop the build process early and view the final, expanded text that the preprocessor hands off to the compiler.
Lesson 767How `gcc -E` shows preprocessor output
gcc -E hello.c
If you run the command gcc -E hello.c, your terminal will fill with text.
Lesson 767How `gcc -E` shows preprocessor output
gcc -E hello.c -o preprocessed_output.txt
gcc -E hello.c -o preprocessed_output.txt
Lesson 767How `gcc -E` shows preprocessor output
gcc -fPIC -c math_utils.c -o math_utils.o
gcc -fPIC -c math_utils.c -o math_utils.o
Lesson 814Position Independent Code `-fPIC`
gcc -fsanitize=address -g my_program.c -o my_program
gcc -fsanitize=address -g my_program.c -o my_program
Lesson 1171Using AddressSanitizer (`-fsanitize=address`)
gcc -fsanitize=undefined main.c -o program
gcc -fsanitize=undefined main.c -o program
Lesson 941Tools to detect UB: UBSan
gcc -g
If you compile the code above with gcc -g, and then open it in GDB, you can type list to see your actual C code or print secret_number to see its value.
Lesson 825Compiling with debug symbols `-g`
gcc -g main.c -o my_program
gcc -g main.c -o my_program (Map included!)
Lesson 825Compiling with debug symbols `-g`
gcc -g my_program.c -o my_program
Compile: gcc -g my_program.c -o my_program
Lesson 1165Installing and running `valgrind`
gcc -o main.c my_app
Incorrect: gcc -o main.c my_app (This would actually try to overwrite your source code!)
Lesson 58Naming the output with `-o`
gcc -std=c89
If you try to compile the code above with gcc -std=c89, the compiler will complain because int i inside the loop wasn't allowed yet.
Lesson 972Specifying the standard with `-std=` flags
gcc -v
By typing gcc -v, you have confirmed that your toolchain is linked and ready to turn your future text files into powerful software.
Lesson 10Installing GCC on Linux/macOS
gcc -Wall hello.c -o hello
But if you run gcc -Wall hello.c -o hello, the compiler will give you a friendly heads-up.
Lesson 57Basic `gcc` command flags
gcc -Wall main.c
However, if you compile with gcc -Wall main.c, the compiler will shout:
Lesson 69Enabling all warnings with `-Wall`
gcc 11.4.0
If you see a wall of text that includes a version number (like gcc 11.4.0 or Apple clang version...), you are successful!
Lesson 10Installing GCC on Linux/macOS
gcc hello.c
By default, if you run gcc hello.c, the compiler creates a generic executable file named a.out (or a.exe on Windows).
Lesson 57Basic `gcc` command flagsLesson 58Naming the output with `-o`Lesson 60Understanding the `a.out` default
gcc hello.c -o hello
This is why, as you move toward professional development, you will eventually start using the -o (output) flag to give your programs unique names, like gcc hello.c -o hello.
Lesson 60Understanding the `a.out` default
gcc hello.c -o my_program
By typing gcc hello.c -o my_program, the compiler processes your text and creates a new file named my_program.
Lesson 30Creating an executable binary
gcc main.c
When you run a standard command like gcc main.c, the compiler is in its polite, quiet mode.
Lesson 69Enabling all warnings with `-Wall`Lesson 786Feature toggles via command line `-D`Lesson 804The `-c` flag for compilation
gcc main.c -L. -lmathutils -o my_program
gcc main.c -L. -lmathutils -o my_program
Lesson 812Linking with static libraries
gcc main.c -lm
On some systems (like Linux), when you use <math.h>, you need to tell the compiler to link the math library manually by adding -lm to the end of your compile command, like this: gcc main.c -lm.
Lesson 861Basic power and square root: `pow` and `sqrt`
gcc main.c -o app
When you run make, Make sees $@ and thinks, "The target is app, so I'll put that there." It sees $< and thinks, "The first dependency is main.c, so I'll put that there." The actual command executed remains gcc main.c -o app.
Lesson 822Automatic variables like `$@` and `$<`
gcc main.c -o main
As your C projects grow, typing gcc main.c -o main every time you make a change becomes tedious.
Lesson 818Structure of a Makefile Rule
gcc main.c -o program
Up until now, you have likely been compiling your code by typing a command like gcc main.c -o program every single time you make a change.
Lesson 61Introduction to `make` and Makefiles
gcc main.c functions.c -o my_app
As your C projects grow, typing gcc main.c functions.c -o my_app every time you make a change becomes tedious.
Lesson 819Targets, dependencies, and recipes
gcc main.c utils.c tools.c -o mytool
As your command-line utility grows to include multiple .c and .h files, typing gcc main.c utils.c tools.c -o mytool over and over becomes exhausting and error-prone.
Lesson 1211Writing the Makefile for the project
gcc src/main.c src/physics.c -Iinclude -o my_app
When you compile, you would run a command like gcc src/main.c src/physics.c -Iinclude -o my_app.
Lesson 800Organizing /src and /include folders
gcc test.c -o test
After compiling with gcc test.c -o test, you would run it using:
Lesson 564Installing Valgrind Memcheck
gdb
Compile with the -g flag, then use the run command inside gdb or lldb to execute your program under professional supervision.
Lesson 826Starting GDB with an executableLesson 1161Starting a program in `gdb` or `lldb`
gdb -p <PID>
Use gdb -p <PID> to hijack a running process, allowing you to debug live issues without restarting the software.
Lesson 838Attaching GDB to a running process
gdb ./filename
To debug a program, compile with the -g flag and launch it using gdb ./filename to enter the interactive debugging environment.
Lesson 826Starting GDB with an executable
gdb ./my_program
Once you have started GDB in your terminal (usually by typing gdb ./my_program), you will see a prompt that looks like (gdb).
Lesson 827The `run` and `quit` commands
gear
If gear is 2, it skips case 1 and goes straight to case 2.
Lesson 258Basic switch syntax and cases
generic function
A generic function is like a universal shipping container; it doesn't care what is inside as long as it follows basic handling instructions.
Lesson 511Generic functions in C
generic_swap
When we call generic_swap, we cast our specific data addresses into void *.
Lesson 511Generic functions in C
genericPtr
In this example, genericPtr is a chameleon.
Lesson 507The `void *` generic type
get_operation
If we want a function named get_operation that returns a pointer to a function taking two ints and returning an int, it looks like this:
Lesson 929Returning pointers to functions from functions
get_operation(char op)
get_operation(char op): This is our function name and its parameter.
Lesson 929Returning pointers to functions from functions
get_rectangle_stats
In this example, get_rectangle_stats has a void return type because it doesn't use the standard return slot.
Lesson 489Returning multiple values via pointers
get_wind_speed()
If get_wind_speed() connects to a real weather sensor that is currently broken or fluctuating, your test will fail—even if your landing logic is perfect.
Lesson 1176Mocking simple dependencies
getchar
But if your next line is a command that reads any character (like getchar or a string input), it will see that leftover Enter key, think you've already pressed Enter for the new prompt, and skip right past it.
Lesson 697How scanf leaves trailing newlines
getchar()
If a user types "Apple" and hits Enter, getchar() will grab the 'A' and leave 'p', 'p', 'l', 'e', and the 'newline' character sitting in a hidden waiting area called the input buffer.
Lesson 680Basic character input with getcharLesson 681EOF (End Of File) explainedLesson 682Using while loops with getcharLesson 683Relationship between char and int in I/OLesson 1093Standard streams (0, 1, 2)
getchar() != EOF
A while loop paired with getchar() != EOF allows you to process input of any length character-by-character until the user signals they are finished.
Lesson 682Using while loops with getchar
getenv
What you'll learn: How to access and retrieve configuration data from the operating system's environment using the getenv function.
Lesson 1071Environment variables in C
getenv()
Use getenv() to fetch external configuration data, but always check for NULL to ensure the variable actually exists.
Lesson 1071Environment variables in C
getpid()
To find out the "passport number" of your own program while it is running, you use a simple function from the unistd.h library called getpid().
Lesson 1067What is a process ID (PID)Lesson 1068Getting PID with getpid()
getppid()
Every process is born from another; use getppid() to retrieve the unique ID of the process that started the current one.
Lesson 1069Parent processes and getppid()Lesson 1079Handling orphaned processes
gets
If you gave it a small bucket (a character array) and the user typed a giant waterfall of text, gets would just keep pouring until it spilled over, overwriting other important data in your computer's memory.
Lesson 706The buffer size argument in fgets
gets()
If a user types "Administrator", which is 13 characters long, gets() will happily shove those extra letters into memory locations it doesn't own.
Lesson 432Why `gets` is dangerous and deprecatedLesson 699Why gets is dangerous and deprecatedLesson 1152Why `gets()` is strictly forbidden
gets(my_buffer)
When you use gets(my_buffer), you are telling C to take whatever the user types and shove it into my_buffer.
Lesson 432Why `gets` is dangerous and deprecated
gl
The OpenGL library prefixes everything with gl (e.g., glVertex3f), and the SDL library prefixes everything with SDL_.
Lesson 801Naming conventions for large projects
Global Offset Table (GOT)
This flag tells the compiler to generate a Global Offset Table (GOT).
Lesson 814Position Independent Code `-fPIC`
global variable
In C, a global variable is that communal fridge.
Lesson 142The dangers of global variables
Global variables
Global variables, however, are like a statue in the Town Square.
Lesson 137Global variables and file scopeLesson 913The `volatile` qualifier for hardware mapping
global_score
If global_score is defined in game.h, and both player.c and enemy.c include it, they both end up trying to create their own version of that variable.
Lesson 921Sharing variables across files with `extern`Lesson 925Common linkage errors and 'multiple definition'
global_volume
The linker sees these two separate object files, notices they both have a variable named global_volume, and throws the error because it doesn't know which one is the "real" one.
Lesson 925Common linkage errors and 'multiple definition'
glVertex3f
The OpenGL library prefixes everything with gl (e.g., glVertex3f), and the SDL library prefixes everything with SDL_.
Lesson 801Naming conventions for large projects
gmon.out
When it finishes, it will automatically generate a file named gmon.out.
Lesson 1182Introduction to the `gprof` profiler
gmtime
Specifically, gmtime converts the time to UTC (Greenwich Mean Time), which is the global scientific standard, ignoring local time zones or daylight savings.
Lesson 893Converting `time_t` to UTC with `gmtime`
goal
Imagine you have a box called goal containing the number 100.
Lesson 238The Indirection operator `*`
goes to
In the code above, the compiler looks at the first pair {0, 0} and knows that 0 goes to x and the second 0 goes to y because that is the order defined in the struct Point.
Lesson 631Initializing arrays of structs
goes up by one and
Increment (i++, j--): At the end of every lap, i goes up by one and j goes down by one.
Lesson 240The comma operator in `for` loops
gold
That key opens a safe containing the gold (the actual data).
Lesson 505Accessing data through double dereference
goldCoins = 100
You might wonder: "Why not just write goldCoins = 100?" In a simple program, you would.
Lesson 458Assigning values via pointers
goodbye.c
If you then write a completely different program called goodbye.c and compile that without specifying a name, the compiler will create a new a.out and overwrite the old one.
Lesson 60Understanding the `a.out` default
Google C Style Guide
They follow established guides like the Google C Style Guide or the Kernel Style.
Lesson 40C coding style guides
goto
A goto statement is like a step that says, "Now jump to the middle of a different recipe on page 42." If you use too many of these jumps, your program’s logic becomes tangled, intertwined, and impossible to follow—much like a bowl of tangled pasta.
Lesson 306Defining labels in C codeLesson 307The syntax of the goto statementLesson 308Why goto is generally discouragedLesson 309Legitimate use case: breaking out of nested loopsLesson 310Legitimate use case: error cleanup blocksLesson 311The dangers of 'spaghetti code'Lesson 312Function returns as a control flow mechanism
goto cleanup
Readability: It is immediately clear to other programmers that goto cleanup means "something went wrong; get us out of here safely."
Lesson 310Legitimate use case: error cleanup blocks
goto found
By using goto found;, we bypass the remainder of the inner loop, the remainder of the outer loop, and any code sitting between the loops and the label.
Lesson 309Legitimate use case: breaking out of nested loops
goto my_label
The Colon: The label destination ends with a colon (my_label:), but the goto command ends with a semicolon (goto my_label;).
Lesson 307The syntax of the goto statement
gpa
Each element in the array (classroom[0], classroom[1], etc.) is a complete structure with its own name, id, and gpa.
Lesson 630Declaring an array of structs
gprof
gprof is a classic tool for Linux and Unix systems that tracks how many times each function is called and how many milliseconds the CPU spends inside them.
Lesson 1182Introduction to the `gprof` profilerLesson 1183Identifying 'Hot Spots' in your code
grade
Because the ternary operator is an expression, it allows for "Inline Logic." In the example above, we didn't just use it to set the grade variable; we actually put a second ternary operator directly inside the printf function.
Lesson 51Printing characters with `%c`Lesson 207Ternary as an expressionLesson 239Member access `.` and `->`Lesson 262Switch restrictions: integral types onlyLesson 602Initializing structs with brace notationLesson 635Sorting an array of structsLesson 879Sorting structs by multiple fields
Graph
In the world of computer science, we call this map a Graph.
Lesson 1039Vertices and Edges definition
graph[V][V]
We assume we have an adjacency matrix graph[V][V] and a simple queue array.
Lesson 1044Breadth-First Search (BFS) logic
Graphical User Interface (GUI)
When you use an operating system like Windows or macOS, you are using a Graphical User Interface (GUI).
Lesson 12Introduction to the CLI
graphics.c
If physics.c includes constants.h, and graphics.c includes constants.h, and then main.c includes both physics.h and graphics.h, the contents of constants.h will be pasted into your main file multiple times.
Lesson 789The 'duplicate definition' errorLesson 799The role of the 'main' fileLesson 817Why we need build tools
graphics.h
If physics.c includes constants.h, and graphics.c includes constants.h, and then main.c includes both physics.h and graphics.h, the contents of constants.h will be pasted into your main file multiple times.
Lesson 789The 'duplicate definition' error
gravity_constant
If you are setting the player_age and the gravity_constant, keep them on separate lines!
Lesson 196Chained assignments `a = b = c`
greater
Left Scout: Starts at the beginning and moves right until it finds a value greater than the pivot.
Lesson 1056Quick Sort: Partitioning logic
Greater than
This is where the Greater than (>) and Less than (<) operators come in.
Lesson 159Greater than `>` and less than `<`
GREEN
GREEN is the "0-th" item, YELLOW is 1, and RED is 2.
Lesson 662Default integer values in enums
greet
When the computer reaches greet(); inside the main function, it "jumps" to the greet definition, executes all the code inside those curly braces, and then "jumps" back to exactly where it left off in main.
Lesson 322What is a function?Lesson 351Visualizing the stack during nested callsLesson 515Taking the address of a function
greet()
When the computer reaches greet(); inside the main function, it "jumps" to the greet definition, executes all the code inside those curly braces, and then "jumps" back to exactly where it left off in main.
Lesson 140Automatic duration variablesLesson 322What is a function?Lesson 351Visualizing the stack during nested callsLesson 515Taking the address of a functionLesson 806Linking multiple object files
greeting
Even though name is longer, the program remains stable because we didn't exceed the 15-byte limit of the greeting array.
Lesson 425Concatenating strings with `strcat`Lesson 436Using `strncat` for safer concatenationLesson 767How `gcc -E` shows preprocessor output
greetings.c
The compiler looks at main.c, sees that it needs a function called say_hello, finds that function inside greetings.c, and stitches them together.
Lesson 59Compiling multiple source files
greetUser
When it hits the line greetUser();, the computer pauses what it's doing in main and looks for the block of code labeled greetUser.
Lesson 325Writing your first custom function
greetUser()
When it hits the line greetUser();, the computer pauses what it's doing in main and looks for the block of code labeled greetUser.
Lesson 325Writing your first custom function
grid[0]
It takes the first inner set and places those values into the first row (grid[0]).
Lesson 409Initializing 2D arrays with nested braces
grid[1]
It then takes the second inner set and places them into the second row (grid[1]).
Lesson 409Initializing 2D arrays with nested braces
grid[i][j]
When the code runs grid[i][j], it is saying: "Go to row i, then find item j."
Lesson 411Nested `for` loops for 2D traversal
guaranteed to run at least once
This means the code inside a do-while loop is guaranteed to run at least once, no matter what.
Lesson 279Comparing while vs do-while use cases
guest list with addresses
An array of pointers, however, is like a guest list with addresses.
Lesson 483Array of strings vs 2D char array

H

had higher precedence than
If = had higher precedence than +, the code wouldn't make any sense!
Lesson 212Operator precedence table
handle_interruption
The operating system forcibly interrupts the while loop, jumps to the handle_interruption function, and then returns to the loop once the function finishes.
Lesson 1084What are Unix signals
handle_sigint
Instead, pause my execution and jump to the handle_sigint function."
Lesson 1087Basic signal handling with signal()
Handler
Self-documentation: The name Handler tells the next programmer what that function's purpose is, which the raw syntax struct Task (ptr)(int) fails to do.
Lesson 930Complex nested `typedef` structures
has higher precedence than
They assume that because * has higher precedence than +, the computer must calculate everything related to the multiplication before even looking at the addition.
Lesson 216Precedence of `*` over `+`Lesson 227Order of evaluation vs Precedence
has_
Booleans should be questions: If a variable is true or false, prefix it with is_, has_, or can_.
Lesson 1197Meaningful variable naming conventions
hash
By adding the original hash to it ((hash << 5) + hash), we effectively multiply by 33.
Lesson 1038String hashing with DJB2
Hash Function
Instead, you put the book's title into a special machine called a Hash Function.
Lesson 1036Hash table lookupLesson 1038String hashing with DJB2
Hash Table
A Hash Table works like a magic librarian.
Lesson 1035Hash table insertion
haystack
You have a large field (the haystack) and you are waving your detector to find a specific metal object (the needle).
Lesson 852Finding substrings with `strstr`
head pointer
This "handle" is what we call the head pointer.
Lesson 990Creating the head pointer
head_ref
Note that we pass a pointer to the head pointer (head_ref) so we can modify the original head back in the main function.
Lesson 993Prepending nodes to the head
head->prev
In a standard doubly linked list, the head->prev and tail->next pointers both point to NULL.
Lesson 1004Circular doubly linked lists
header
A function definition consists of a header and a body.
Lesson 323Anatomy of a function definition
header file
Instead, you put the recipe in its own folder (a source file) and put the name of the dish on a menu (a header file).
Lesson 382Sharing functions across modulesLesson 788The purpose of header files
Header Files
To stay organized, C programmers split code into two specific types of files: Source files (.c) and Header files (.h).
Lesson 378Separating interface from implementationLesson 796Splitting code into `.c` and `.h`
Header Guard
A Header Guard is a special wrapper made of preprocessor directives that acts like a velvet rope at a club.
Lesson 380Header Guards: `#ifndef` and `#define`
heading
Think of it this way: you use a semicolon at the end of an action, but not at the end of a heading or a container.
Lesson 22Semicolons as statement terminators
health
For example, if you wrote struct Player hero = {'B'};, the grade becomes 'B', but the score and health are automatically set to 0 and 0.0.
Lesson 193Compound subtraction `-=`Lesson 493Pointer to a constant (`const int *p`)Lesson 602Initializing structs with brace notationLesson 729Reading structs back into memory
health = 100
If you name your specific character variable hero, you can’t just say health = 100; because the computer won't know whose health you are talking about.
Lesson 601The dot operator for member access
health = health - 10
In standard math notation, you might write health = health - 10.
Lesson 193Compound subtraction `-=`
healthPoints
We declare healthPoints but forget to set its starting value before using it in a calculation.
Lesson 939Using uninitialized variables
heap buffer overflow
In C, the heap is that club, malloc() is your reservation, and sitting in seat 14 is a heap buffer overflow.
Lesson 561Heap buffer overflows
HEATING
At any given moment, the microwave is doing exactly one thing: it’s either IDLE, HEATING, or PAUSED.
Lesson 668Using enums for state machines
heatLevel gets the value 92
Always read the = sign as "gets" or "becomes." Instead of saying "heatLevel equals 92," say "heatLevel gets the value 92." This mental habit will help you avoid confusion when you start writing more complex code.
Lesson 78Assigning values with `=`
height
If you have three variables representing the dimensions of a box (length, width, height), putting them on one line makes it clear they belong together.
Lesson 76Multiple declarations in one line
hello_world.c
Good: hello_world.c or temp_converter.c.
Lesson 16Naming conventions for .c files
helper_math
However, if another file tries to call helper_math, the linker will fail because that symbol was marked private (static) and kept out of the global phonebook.
Lesson 809Symbol tables and visibility
helpers.h
Here is how you would define a shared math helper in a header file (e.g., helpers.h):
Lesson 368Inline functions in header files
hero
If you name your specific character variable hero, you can’t just say health = 100; because the computer won't know whose health you are talking about.
Lesson 601The dot operator for member accessLesson 729Reading structs back into memory
hero.health
For example, hero.health is just an integer.
Lesson 601The dot operator for member access
hero.health += 10
You can add to it (hero.health += 10), subtract from it, or pass it into a function.
Lesson 601The dot operator for member access
hero.health = 100
You must use the dot: hero.health = 100;.
Lesson 601The dot operator for member access
hero.level
Think of the dot as the word "of." When you write hero.level, you are telling C: "Look at the level of hero."
Lesson 601The dot operator for member access
heroStrength
The original heroStrength in main was never even touched; it was just the source for the initial photocopy.
Lesson 344Visualizing the stack frame copy
Hex
You will rarely use Octal in modern programming, but you will use Hex and Binary constantly when working with colors (like #FF5733), memory addresses, or hardware pins.
Lesson 119Integer literals (Hex, Octal, Binary)
Hexadecimal
In programming, we frequently use hexadecimal (base-16) to represent memory addresses or colors.
Lesson 451Hexadecimal notation for memoryLesson 841Checking for digits with `isdigit` and `isxdigit`
high
The Recursive Step: If the target is smaller than the middle, we call the function again, but we update the high boundary.
Lesson 1055Merge Sort: Recursive splittingLesson 1062Binary Search: Iterative approachLesson 1063Binary Search: Recursive approach
high-level
A high-level approach would be using an app on your phone.
Lesson 1What is a low-level language?
highScores[5]
If your array has a size of 3, asking for highScores[5] is like trying to open a locker that isn't there.
Lesson 386Accessing elements with the `[]` operator
HOME
Instead of hard-coding a folder path like C:\Users\Bob\Documents, you can ask the system for HOME.
Lesson 1071Environment variables in C
home address
They don't need to know what is inside the box yet; they need to know your home address so they can physically put the box inside your mailbox.
Lesson 490Swapping two numbers using pointersLesson 692How scanf uses memory addresses
Hot Spots
In programming, these high-traffic areas are called Hot Spots.
Lesson 1183Identifying 'Hot Spots' in your code
house
In C, when you create a string using an array like char myName[] = "Alice";, you are building a house.
Lesson 484Memory layout of string pointers
houses down (
He starts at the beginning of the street (a), walks exactly 3 houses down (+ 3), and opens the door (*) to get the letter.
Lesson 473The equivalence of `a[i]` and `*(a + i)`
how
The .h file defines what a tool does, while the .c file defines how it does it.
Lesson 377Role of the `.h` file
htons
Always use htons when sending data and ntohs when receiving it to ensure your numbers remain consistent across different types of hardware.
Lesson 952Network byte order and `htons`/`ntohs`
htons()
Because of this, we must use helper functions like htons() (Host TO Network Short) to ensure our port numbers are in the universal "Network Byte Order."
Lesson 952Network byte order and `htons`/`ntohs`Lesson 1109The sockaddr_in structure
htons(80)
On a standard PC, htons(80) will flip the bytes of the number.
Lesson 952Network byte order and `htons`/`ntohs`

I

i -= 10
By using expressions like i += 2 or i -= 10 in your loop header, you control the "stride" of the loop, allowing you to skip values efficiently.
Lesson 287Using non-unit increments (e.g., i += 2)
i *= 2
If you wanted to double a value every time, you could even use i *= 2.
Lesson 287Using non-unit increments (e.g., i += 2)
i += 2
By using expressions like i += 2 or i -= 10 in your loop header, you control the "stride" of the loop, allowing you to skip values efficiently.
Lesson 287Using non-unit increments (e.g., i += 2)
i += 5
If you are calculating a discount for every fifth item in a list, you could use i += 5.
Lesson 287Using non-unit increments (e.g., i += 2)
i < 10
When counting up, we usually use i < 10.
Lesson 286Counting backwards with decrement operators
i < 100
However, the computer spends extra energy on "loop overhead"—checking if the condition is still true (is i < 100?) and incrementing the counter (i++).
Lesson 1190Loop unrolling explained
i < 5
Crucially, the condition i < 5 prevents the program from trying to access scores[5], which doesn't exist.
Lesson 282The three parts of a for loop headerLesson 400Printing array elements in a sequence
i < size
Always use i < size instead of i <= size to ensure your loop stops exactly one step before the boundary.
Lesson 405Avoiding off-by-one errors in loopsLesson 478Bounds checking and pointer safety
i <= 10
The loop continues as long as the middle condition (i <= 10) remains true.
Lesson 288The comma operator in for loop headers
i <= 5
Incorrect: i <= 5 (Attempts to access index 5, which is out of bounds)
Lesson 405Avoiding off-by-one errors in loops
i <= num_elements
If we had used i <= num_elements, the loop would have tried to print scores[5].
Lesson 405Avoiding off-by-one errors in loops
i <= size
Always use i < size instead of i <= size to ensure your loop stops exactly one step before the boundary.
Lesson 405Avoiding off-by-one errors in loops
i = 2
Second Pass: The outer loop increments to i = 2.
Lesson 303Controlling the inner loop with outer loop variables
i = i++
You might look at the expression i = i++ and assume the logic is straightforward: take the current value of i, assign it to itself, and then increment it.
Lesson 225Undefined behavior: `i = i++`Lesson 937Sequence point violations
i = size
If you accidentally start at i = size, you are pointing at a memory location just outside the array, which contains "garbage" data or could cause your program to crash.
Lesson 401Reverse traversal of an array
i == 500
A conditional breakpoint allows you to stay hands-off until a specific logical condition is met, such as i == 500 or error_code != 0.
Lesson 835Setting conditional breakpoints
i > 0
Conversely, if you set a condition that is always true (like i > 0 while adding to i), your loop will run forever.
Lesson 286Counting backwards with decrement operatorsLesson 401Reverse traversal of an array
i >= 0
Similarly, ensure your condition is i >= 0.
Lesson 401Reverse traversal of an array
i--
Step Down: Use the decrement operator (i--) to subtract one from the variable every time the code block finishes.
Lesson 286Counting backwards with decrement operatorsLesson 401Reverse traversal of an array
I/O Redirection
The most common reason is I/O Redirection.
Lesson 1094Duplicating descriptors with dup()
id
You don't have to manage two different struct types for liquids and solids; you have one master InventoryItem that adapts to your needs while keeping common data (like id) in a single, predictable place.
Lesson 622The importance of NULL checks for struct pointersLesson 630Declaring an array of structsLesson 670Combining structs and unions
IDE
An IDE is like an automatic—it’s smoother, but it hides the mechanics from you.
Lesson 13Using a Text Editor vs IDE
identifier
You have many identical jars, so you put labels on them like "Salt," "Sugar," or "Flour." In C, a variable is like one of those jars, and the name you give it is called an identifier.
Lesson 74Naming rules and identifiersLesson 498The 'Clockwise/Spiral' rule for declarations
identity
Declaration is about identity (What is it called?
Lesson 79Declaration vs. Initialization
IDLE
At any given moment, the microwave is doing exactly one thing: it’s either IDLE, HEATING, or PAUSED.
Lesson 668Using enums for state machines
if
Complexity isn't just about length; it’s about "indentation depth." Every time you add an if statement inside a for loop inside another if statement, you are forcing the next programmer to hold a massive mental map of conditions just to understand one line of code.
Lesson 1What is a low-level language?Lesson 9Role of the CompilerLesson 23Case sensitivity in CLesson 132Safe downcasting techniquesLesson 163Truthiness: 0 vs non-zeroLesson 164Boolean result of comparisonsLesson 165Common pitfall: `=` vs `==`Lesson 167Logical NOT `!`Lesson 172Building complex logical expressionsLesson 173Logical vs Bitwise distinctionLesson 207Ternary as an expressionLesson 217Precedence of assignmentLesson 242Relational operators: <, <=, >, and >=Lesson 243Truthiness: 0 is false, non-zero is trueLesson 244The if statement syntaxLesson 245The else clause for alternative pathsLesson 246Else-if ladders for multiple conditionsLesson 248Logical OR (||) for combined conditionsLesson 250Curly brace requirements for single vs multi-lineLesson 251Variable scope inside if-else blocksLesson 253Short-circuit evaluation in logical ANDLesson 254Short-circuit evaluation in logical ORLesson 255Common mistake: assignment (=) vs equality (==)Lesson 256Nested if statements and dangling else logicLesson 257Using if statements for input validationLesson 258Basic switch syntax and casesLesson 264Comparing switch-case vs else-if laddersLesson 271Infinite loops: while(1) and while(true)Lesson 274The do-while syntax and the trailing semicolonLesson 281Pitfall: condition check occurs after executionLesson 287Using non-unit increments (e.g., i += 2)Lesson 290The break statement: exiting a loop earlyLesson 291The continue statement: skipping to the next iterationLesson 293Using break to exit infinite loops on conditionLesson 297Alternative patterns to avoid break and continueLesson 308Why goto is generally discouragedLesson 310Legitimate use case: error cleanup blocksLesson 312Function returns as a control flow mechanismLesson 318Finding Min and Max in a loopLesson 353Concept of self-calling functionsLesson 354Importance of the Base CaseLesson 402Finding the maximum value in an arrayLesson 404Linear search for a specific valueLesson 437Checking bounds before array accessLesson 443Counting vowels and consonantsLesson 462Checking for NULL before dereferencingLesson 525Dereferencing the NULL pointerLesson 536Header file stdlib.h for allocationLesson 539Checking for NULL return valuesLesson 542Why freeing NULL is safe
if (!is_finished)
Instead of writing if (is_finished == 0), which feels mathematical and clunky, you can write if (!is_finished).
Lesson 249Logical NOT (!) for inversion
if (!is_logged_in)
Instead of writing if (is_logged_in == 0), you can write if (!is_logged_in).
Lesson 167Logical NOT `!`
if (0.1 + 0.2 == 0.3)
If you try to ask if (0.1 + 0.2 == 0.3), the computer will likely tell you false because the tiny microscopic "dust" at the end of the numbers doesn't match perfectly.
Lesson 104Comparing floats for equality
if (a + b > INT_MAX)
You can't just do if (a + b > INT_MAX) because the addition a + b might overflow before the comparison even happens.
Lesson 905Using `INT_MAX` and `INT_MIN` for overflow checks
if (age >= 21)
If you write if (age >= 21), it might be obvious today that 21 is the legal drinking age.
Lesson 771Avoiding magic numbers with macros
if (balance > LONG_MAX)
If you are calculating a bank balance, you can check if (balance > LONG_MAX) to prevent an overflow before it happens, regardless of whether a long is 32-bits or 64-bits on that specific machine.
Lesson 942Limits of `limits.h` and `stdint.h`
if (condition) { ... }
Use if (condition) { ... } to create a gated block of code that only runs when your condition evaluates to true.
Lesson 244The if statement syntax
if (fptr != NULL)
By always checking if (fptr != NULL) before calling fclose(), you build a safety net.
Lesson 747Safe file closing patterns
if (is_finished == 0)
Instead of writing if (is_finished == 0), which feels mathematical and clunky, you can write if (!is_finished).
Lesson 249Logical NOT (!) for inversion
if (is_logged_in == 0)
Instead of writing if (is_logged_in == 0), you can write if (!is_logged_in).
Lesson 167Logical NOT `!`
if (is_sunny && is_weekend && have_gas)
if (is_sunny && is_weekend && have_gas).
Lesson 247Logical AND (&&) for combined conditions
if (is_sunny)
However, because the else is closest to if (is_sunny), it actually means: "If it is the weekend but NOT sunny, stay inside." If is_weekend were 0, nothing would print at all!
Lesson 256Nested if statements and dangling else logic
if (isEmpty())
Without the if (isEmpty()) check, the line stack[top] would try to access stack[-1].
Lesson 1013Handling Stack Underflow
if (light == 0)
If you see if (light == 0), you have to remember what 0 means.
Lesson 664Enums vs constant integers
if (message == NULL)
In the example above, if we didn't have the if (message == NULL) check, the printf function would try to read memory at address zero, causing a crash.
Lesson 1149Validating function arguments with `NULL` checks
if (my_total > INT_MAX)
Using these constants makes your code "portable." If you write a program that checks if (my_total > INT_MAX), that code will work correctly whether the maximum integer is 32,767 (on old 16-bit systems) or 2,147,483,647 (on modern systems).
Lesson 904Integer ranges in `limits.h`
if (my_variable < INT_MAX)
Using <limits.h> makes your code "portable." If you write a program that checks if (my_variable < INT_MAX), that code will work perfectly whether the maximum is 32 thousand or 2 billion.
Lesson 96The `<limits.h>` header file
if (n <= 0)
In this example, if (n <= 0) is the base case.
Lesson 354Importance of the Base Case
if (ptr != NULL)
If you have a complex program where different functions might try to use the same pointer, setting it to NULL ensures that a simple if (ptr != NULL) check can prevent a catastrophic crash.
Lesson 459Initializing pointers to NULLLesson 559Setting pointers to NULL after freeLesson 622The importance of NULL checks for struct pointers
if (s + 1 > s)
If you write if (s + 1 > s), and s is a signed integer, the compiler might simplify that entire expression to true and remove the check entirely.
Lesson 935Signed integer overflow vs Unsigned wrap
if (s->top == MAX - 1)
Because C doesn't automatically stop you from writing data past the end of an array, the if (s->top == MAX - 1) check is your safety net.
Lesson 1009The Push operation
if (signal == 2)
When you see if (signal == RED), it is much easier to understand than if (signal == 2).
Lesson 662Default integer values in enums
if (signal == RED)
When you see if (signal == RED), it is much easier to understand than if (signal == 2).
Lesson 662Default integer values in enums
if (status == 0)
While this works, your code will soon be filled with lines like if (status == 0).
Lesson 661Defining an enum type
if (status == 2)
If you see if (status == 2), you have to hunt through your notes to remember what "2" means.
Lesson 668Using enums for state machines
if (string1 == string2)
In C, you might be tempted to compare two strings using if (string1 == string2).
Lesson 426Comparing strings with `strcmp`
if (top >= MAX - 1)
By adding an if (top >= MAX - 1) check, you create a "boundary guard" that ensures your stack stays within its allocated "home" in memory.
Lesson 1012Handling Stack Overflow
if (x + 1 > x)
If you later write a check like if (x + 1 > x), the compiler might delete the entire if statement because, according to the "contract," x + 1 can never overflow.
Lesson 934What 'Undefined Behavior' actually means
if (x = 10)
The most common mistake for new C programmers is typing if (x = 10) when they mean if (x == 10).
Lesson 241Relational operators: == and !=
if (x = 5)
To the computer, if (x = 5) is a valid command to "set x to 5, then tell me if 5 is true." Since 5 is not 0, it is always true.
Lesson 161The equality operator `==`Lesson 255Common mistake: assignment (=) vs equality (==)
if (x == 10)
The most common mistake for new C programmers is typing if (x = 10) when they mean if (x == 10).
Lesson 241Relational operators: == and !=
if both sides are
Because & only results in a 1 if both sides are 1, using our mask will zero out everything except the bit we care about.
Lesson 188Common bitwise idioms
if statement
An if statement to check if the current item matches your criteria.
Lesson 317The Counter pattern (counting occurrences)
if-else if
When you have one variable that could be several different fixed values (like 1, 2, or 3), a switch is much cleaner and easier to read than a long chain of if-else if statements.
Lesson 258Basic switch syntax and cases
if-else if-else
Most developers follow a rule of thumb: if you need to check more than three conditions, it is usually better to use a standard if-else if-else block.
Lesson 208Nesting ternary operators
if/else
In modern C, almost everything a goto does can be done better with while loops, for loops, or if/else structures.
Lesson 311The dangers of 'spaghetti code'Lesson 770The danger of semicolon in `#define`
ignoreCase
If you were building a search tool, you would now use that ignoreCase variable to decide whether to convert strings to lowercase before comparing them.
Lesson 1208Adding command line flags (e.g., `-i` for case)
ignores the
While isgraph() catches everything from A to &, ispunct() ignores the A and only catches the &.
Lesson 843Distinguishing `ispunct` and `isgraph`
ile
To check for a file's existence, we use a special constant called F_OK (short for File OK).
Lesson 762Checking if a file exists
image_01.png
Build a complex filename (like image_01.png, image_02.png).
Lesson 703Formatting strings in memory with sprintf
image_02.png
Build a complex filename (like image_01.png, image_02.png).
Lesson 703Formatting strings in memory with sprintf
immediate initialization
The best way to avoid this "dark side" of C is to adopt a habit of immediate initialization.
Lesson 939Using uninitialized variables
Implementation-defined behavior
Instead, it categorizes "weird" code into two main buckets: Implementation-defined behavior and Undefined Behavior (UB).
Lesson 943Implementation-defined behavior vs UB
implicit
If you tell a friend to go to "123 Maple Street," that is an implicit piece of information.
Lesson 461Implicit vs explicit pointer types
implicit declaration
In older versions of C, if you called a function named calculate_tax() before defining it, the compiler would shrug and say, "I'll assume this function exists somewhere and that it returns an integer." This is an implicit declaration.
Lesson 329Implicit vs. explicit declarationsLesson 334Common errors with missing prototypes
impossible
To make this work, you choose a value that would be impossible or invalid in a real scenario.
Lesson 314The Sentinel Value pattern
in
The d in %d stands for "decimal," which is the standard way we represent whole numbers (integers) like 5, 42, or -10.
Lesson 50Printing integers with `%d`Lesson 490Swapping two numbers using pointers
in a
To display a literal % in a printf statement, use a double percent sign %%.
Lesson 690Escaping the percent sign %%
in the prototype and
Interestingly, the names of the variables in the prototype don't technically have to match the definition (e.g., you could use p and r in the prototype and price and rate in the definition).
Lesson 332Matching prototypes with definitions
in the third position and the
Because the input has a 1 in the third position and the mask also has a 1 there, the result becomes 4 (00000100).
Lesson 180Masking bits with `&`
In-order traversal
To take advantage of this and print the tree's contents in order (like a sorted list), we use a recursive strategy called In-order traversal.
Lesson 1025In-order traversal (Sorted output)
in-place
If you rearrange the clothes by shifting them around within the suitcase until they fit perfectly, you are sorting in-place.
Lesson 1051In-place sorting vs extra memory
inary dig
Each bulb represents a "bit" (a binary digit).
Lesson 179Understanding binary representation
include
You might have a src folder for your code and an include folder for your headers.
Lesson 63Header search paths
include guard
An include guard is a simple gatekeeper.
Lesson 790Creating basic include guards
includes
If A includes B, then B must be able to stand on its own or only depend on things "below" it.
Lesson 802Dependency graphing in your head
increment
Look at how the increment function fails to change the original score:
Lesson 346Preparing for pass by reference
increment happens last
The most important thing to remember is that the increment happens last.
Lesson 283Initialization, condition, and increment flow
increment(myScore)
When main calls increment(myScore), the value 10 is plucked out and dropped into the function's local score variable.
Lesson 346Preparing for pass by reference
incremental build
This is called an incremental build, and it is the secret to staying productive as a programmer.
Lesson 817Why we need build toolsLesson 824Incremental builds and file timestamps
indentation
The most important use of whitespace is indentation.
Lesson 36Whitespace and indentation
indented logging
To see what’s actually happening, we use a technique called indented logging.
Lesson 363Visualizing recursive depth
indeterminate
When you declare a local variable, its initial value is indeterminate.
Lesson 560Reading from uninitialized memory
indeterminate iterations
Unlike a for loop, which is usually built for counting a specific number of steps, the while loop is designed for indeterminate iterations.
Lesson 269Using while for indeterminate iterations
index
Without this sequence point, the compiler might try to evaluate arr[index] at the same time it is trying to increment index.
Lesson 223Sequence points in logic `&&` and `||`
index % size
Circular arrays use the modulo operator (index % size) to connect the end of the array back to the start, allowing for continuous reuse of empty memory slots.
Lesson 1018Circular array implementation
indirection operator
In programming, this act of "following the map" to see what’s inside the house is called dereferencing, and we use the indirection operator (*) to do it.
Lesson 238The Indirection operator `*`
Inefficient
In the Inefficient example, the char members act like "spacers" that force the compiler to add padding multiple times.
Lesson 641Reordering members to reduce padding
infinite loop
This is called an infinite loop, and it will cause your program to freeze or crash because it is stuck doing the same task over and over.
Lesson 268Updating the loop variable to avoid infinite loopsLesson 289Optional components: the for(;;) infinite loop
INFINITY
The <math.h> library provides two special constants for this: NAN and INFINITY.
Lesson 867Handling `NAN` and `INFINITY` constants
Ingredient
If the recipe says, "Take the Ingredient and chop it, then take the Ingredient and boil it," and you decide that your Ingredient is "an apple that I must wash first," the recipe becomes:
Lesson 775Side effects in macro arguments
init
The child becomes an "orphan." In modern systems, the system adopts these orphans, usually reassigning them to a special system process (like systemd or init) which has a PID of 1.
Lesson 1069Parent processes and getppid()Lesson 1079Handling orphaned processes
Initialization
Initialization is the act of clearing out that junk and putting your own specific value inside for the first time.
Lesson 79Declaration vs. Initialization
initialize
If you type up, GDB will inform you that you are now in initialize.
Lesson 834Moving between frames with `up` and `down`
initializer list
When you declare an array, you can use an initializer list—a comma-separated list of values inside curly braces.
Lesson 396Assigning values vs initializing arrays
initializing
To avoid bugs that are incredibly hard to track down, get into the habit of initializing your variables as soon as you create them: int score = 0;.
Lesson 77Garbage values and uninitialized variables
inline
In C, when you prefix a function with the inline keyword, you are telling the compiler: "Instead of jumping to this function's location in memory, please just copy and paste the function's code directly into the spot where it's called."
Lesson 366The `inline` keyword purposeLesson 367Compiler discretion with inliningLesson 368Inline functions in header filesLesson 369When to use inline functionsLesson 370Macros vs. Inline functionsLesson 778Macros vs inline functionsLesson 1194Using `inline` functions effectively
Inline Assembly
If you need to move data into a specific register (like the eax register on x86 processors) to talk to hardware, you use Inline Assembly.
Lesson 961Direct register access
inline functions are usually the winner
In modern C, inline functions are usually the winner. They provide "type safety," meaning the compiler will warn you if you try to pass a string into a math function.
Lesson 778Macros vs inline functions
inner loop
Nested loops work the same way: an inner loop completes its entire run for every single step of the outer loop.
Lesson 299Inner loop vs outer loop execution orderLesson 300Using nested loops to print 2D gridsLesson 301Nested loops for multiplication tables
Inorder Successor
To keep the tree in order, you look for the Inorder Successor—the smallest value in the right subtree.
Lesson 1029The three cases of node deletion
input
The compiler looks at your constraints, picks two registers, moves the value of input into the second register, runs your assembly, and then moves the result from the first register back into the output variable.
Lesson 180Masking bits with `&`Lesson 280Scope of variables declared inside do-whileLesson 822Automatic variables like `$@` and `$<`Lesson 873Converting strings to doubles with `strtod`Lesson 916Optimization benefits of `restrict`Lesson 959Input and Output operands in assembly
input buffer
If a user types "Apple" and hits Enter, getchar() will grab the 'A' and leave 'p', 'p', 'l', 'e', and the 'newline' character sitting in a hidden waiting area called the input buffer.
Lesson 680Basic character input with getchar
input_handlerskey_code
This technique is the backbone of "jump tables" in low-level programming and is frequently used to handle user input in games, where input_handlerskey_code triggers the correct action instantly.
Lesson 517Arrays of function pointers
input.c
If you change just one line of code in input.c, do you really need to wait for the computer to re-translate the other five files?
Lesson 817Why we need build tools
input[1]
If I write to output[0], I might be changing the value of input[1]!" To be safe, it would reload input[i] from the slow main memory every single time.
Lesson 916Optimization benefits of `restrict`
input[i]
If I write to output[0], I might be changing the value of input[1]!" To be safe, it would reload input[i] from the slow main memory every single time.
Lesson 916Optimization benefits of `restrict`
insert
Each time insert calls itself, the "problem" gets smaller.
Lesson 1024Recursive insertion logic
Insertion Sort
While algorithms like Bubble Sort and Insertion Sort are typically stable, Quick Sort is often unstable because it swaps elements over long distances.
Lesson 1051In-place sorting vs extra memoryLesson 1052Stability in sorting algorithms
inside
However, when you declare an array inside a function (a local array), C prioritizes speed over cleanliness.
Lesson 347What is a Stack Frame?Lesson 395Initialization of local vs global arraysLesson 412Printing a 2D matrix to the console
inside an
The most common mistake for beginners is using = when they mean == inside an if statement.
Lesson 255Common mistake: assignment (=) vs equality (==)
inside your
To display a single percent sign % in a string, you must write it as %% inside your printf function.
Lesson 48The percent sign `%%` literal
instead of
In C, any non-zero value is treated as "True." If you accidentally use = instead of ==, you aren't asking a question; you are performing an action that almost always results in "True."
Lesson 126The 'Usual Arithmetic Conversions'Lesson 165Common pitfall: `=` vs `==`
instead of the expected
Due to the order of operations (multiplication before addition), you would get 8 instead of the expected 16.
Lesson 772Defining function-like macros
int
If you absolutely need a variable that is exactly 32 bits regardless of the computer it runs on, advanced C programmers use specific headers like <stdint.h>, but understanding the fluid nature of the basic int is the first step toward writing software that works everywhere.
Lesson 9Role of the CompilerLesson 23Case sensitivity in CLesson 24The `return 0;` statementLesson 30Creating an executable binaryLesson 74Naming rules and identifiersLesson 75The syntax of a declarationLesson 76Multiple declarations in one lineLesson 81The `int` keywordLesson 82Short vs. Long integersLesson 83The `long long` typeLesson 84Using the `sizeof` operatorLesson 85Platform dependency of sizesLesson 86Fixed-width types from `<stdint.h>`Lesson 87Printing integers with `%d` and `%ld`Lesson 88Minimum and maximum valuesLesson 89The `signed` keywordLesson 95When to choose unsigned over signedLesson 96The `<limits.h>` header fileLesson 116Macros vs. Const variablesLesson 121What is type promotion?Lesson 122Integer promotion rulesLesson 123Hierarchy of types in expressionsLesson 124Automatic conversion in assignmentsLesson 125Risks of narrowing conversionsLesson 126The 'Usual Arithmetic Conversions'Lesson 127Mixing signed and unsigned in mathLesson 128Common conversion pitfallsLesson 130Forcing floating-point divisionLesson 131Casting between char and intLesson 132Safe downcasting techniquesLesson 133Truncation during float-to-int castsLesson 135Readability and intent in castingLesson 138Shadowing: Nested scope name clashesLesson 150The addition operator `+`Lesson 151The subtraction operator `-`Lesson 152Multiplication `*` mechanicsLesson 153Integer division `/` truncationLesson 154Floating-point divisionLesson 155The modulo operator `%` with integersLesson 157Basic arithmetic overflowLesson 158Mixing int and float in arithmeticLesson 183Left shift `<<` mechanicsLesson 189Using bitwise operators for flagsLesson 203Incrementing pointers (preview)Lesson 209Type consistency in ternary branchesLesson 228Implicit promotion to `int`Lesson 229Usual arithmetic conversionsLesson 230The `(type)` cast operatorLesson 231Truncation during castingLesson 233Promotion of `char` and `short`
int (*a)[n]
int a[n] is a collection of many addresses, while int (a)[n] is a single address pointing to a specific array structure.
Lesson 927Arrays of pointers vs Pointers to arrays
int * const
Use int * const when you have a specific memory buffer (like a hardware register) that must never be swapped for another address during your program's execution.
Lesson 494Constant pointer to a value (`int * const p`)Lesson 911Difference between `const int *` and `int * const`
int * p
You might see programmers write int p;, int p;, or even int * p;.
Lesson 455Declaring pointer variables with `*`
int *a[n]
int a[n] is a collection of many addresses, while int (a)[n] is a single address pointing to a specific array structure.
Lesson 927Arrays of pointers vs Pointers to arrays
int *ap[10]
If you try to read int *ap[10] as "integer pointer array," you’ll actually get it wrong.
Lesson 926Reading declarations with the 'Right-Left' rule
int *arr
Up to this point, you have likely used malloc() to create an array, storing the address in a simple pointer like int *arr.
Lesson 981Structure for dynamic arrays
int *items
While you could use a pointer like int *items, this forces the actual data to live somewhere else in memory, requiring two separate allocations and causing the CPU to jump around to find your data.
Lesson 672Flexible array members in C99
int *myPtr
When we declare a pointer like int myPtr or char myPtr, we are being explicit.
Lesson 461Implicit vs explicit pointer types
int *p
When we declare int p, we are telling C: "This variable holds the location of an integer." When we declare int *pp, we are saying: "This variable holds the location of a variable that holds the location of an integer."
Lesson 134Casting pointers (Introductory look)Lesson 455Declaring pointer variables with `*`Lesson 456The difference between `int *p` and `*p`Lesson 501Declaring `int **pp`
int *ptr
If you declare int *ptr; without initializing it, that pointer is "wild." It is pointing to a completely random, unpredictable location in your computer’s RAM.
Lesson 457The Dereference operator `*`Lesson 459Initializing pointers to NULLLesson 521Uninitialized 'wild' pointersLesson 652Limitations of bit-field addressesLesson 1155Initializing pointers to `NULL` immediately
int *restrict ptr
When you declare a pointer as int *restrict ptr, you are saying: "For the lifetime of this pointer, only this specific pointer (or values derived directly from it) will be used to access the object it points to."
Lesson 915The `restrict` pointer qualifier
int *temp = ptr
If you must use pointer arithmetic, create a copy like int *temp = ptr; and move temp instead.
Lesson 555Invalid pointer increments before free
int age = 0
In our example, simply changing the declaration to int age = 0; clears the error.
Lesson 1168Detecting uninitialized memory reads
int age = 25
Each room has a unique room number, like "Room 101" or "Room 5002." When you create a variable in C, like int age = 25;, the computer picks an empty room and puts the value 25 inside it.
Lesson 237The Address-of operator `&`
int argc
This is handled through two special parameters: int argc (the count) and char argv (the arguments).
Lesson 506Command line arguments `char **argv`
int arr[] = {[10] = 1}
If you define an array without a fixed size—like int arr[] = {[10] = 1};—the compiler is smart enough to see the highest index mentioned (10) and automatically make the array large enough to hold it (size 11).
Lesson 394Designated initializers (C99)
int b
Then, it adds padding at the end so that if you created an array of these structures, the next int b in the second element would also be correctly aligned.
Lesson 571CPU word size and alignmentLesson 638Understanding memory alignmentLesson 643Alignment requirements for different types
int balance = -50
For example, int balance = -50; works perfectly fine.
Lesson 89The `signed` keyword
int batteryLevel
In this code, int batteryLevel is an abstraction.
Lesson 8Hardware abstraction in C
int board[3][4]
Unlike a static array (like int board[3][4]), this structure lives on the heap.
Lesson 504Dynamic 2D array structures
int const *ptr
Does int const *ptr mean the pointer is constant, or the integer is constant?
Lesson 498The 'Clockwise/Spiral' rule for declarations
int count = 0
If we had used int count = 0; without the static keyword, the output would simply be "Visitor number: 1" three times in a row, because the variable would reset to zero every time the function started.
Lesson 145Persisting data between function calls
int counter
If you have a global int counter, and ten threads increment it at once, they will trip over each other, leading to data races and wrong totals.
Lesson 1122Thread-local storage basicsLesson 1142Atomic types like atomic_int
int found = 1
You would need to use a "flag" (a variable like int found = 1;) and check that flag in the outer loop, or use a return statement if you are ready to exit the entire function.
Lesson 305Breaking out of nested loops: the limitation of break
int global_score
If you simply write int global_score; at the top of both files, the compiler gets confused.
Lesson 797The `extern` keyword for variables
int global_score = 0
In C, variables have a specific "home." When you declare int global_score = 0; at the top of a file, you are telling the compiler to reserve a spot in memory for that integer.
Lesson 797The `extern` keyword for variables
int global_score = 100
This means if you define int global_score = 100; in game.c, you can access that exact same memory location in ui.c by using the extern keyword.
Lesson 918Internal vs external linkage basics
int grid[2][3]
When we declare a 2D array like int grid[2][3], our brains immediately visualize a table with two rows and three columns.
Lesson 408Memory layout: Row-major order
int health
When you call malloc(sizeof(struct Character)), the computer looks at your struct definition, adds up the bytes for the int health and int level, and finds a contiguous block of memory that fits.
Lesson 620Allocating structs on the heap with malloc
int i
If you try to compile the code above with gcc -std=c89, the compiler will complain because int i inside the loop wasn't allowed yet.
Lesson 966C99: Variable declarations and `bool`Lesson 972Specifying the standard with `-std=` flags
int i = 0
A starting point: Usually int i = 0; (because arrays in C always start at index 0).
Lesson 399Using `for` loops for array traversal
int i = 1
In this example, int i = 1 sets the stage.
Lesson 283Initialization, condition, and increment flow
int i = 10
Start High: Set your variable to the starting maximum value (e.g., int i = 10).
Lesson 286Counting backwards with decrement operators
int ignoreMe
By using unnamed bit-fields, you ensure that mode starts at exactly the 6th bit without cluttering your code with "dummy" variables like int reserved1; or int ignoreMe;.
Lesson 650Unnamed bit-fields for padding
int items[10]
In standard C, if you define int items[10], your crate is always exactly big enough for 10 items.
Lesson 672Flexible array members in C99
int level
When you call malloc(sizeof(struct Character)), the computer looks at your struct definition, adds up the bytes for the int health and int level, and finds a contiguous block of memory that fits.
Lesson 620Allocating structs on the heap with malloc
int locker
In C, this is like declaring a standard variable: int locker;.
Lesson 383Declaring an array with `type name[size]`
int main()
Up until now, you have likely seen int main() or int main(void).
Lesson 506Command line arguments `char **argv`
int main() { ... }
int main() { ... }: This is the heart of your program.
Lesson 17The 'Hello World' code
int main(void)
Up until now, you have likely seen int main() or int main(void).
Lesson 506Command line arguments `char **argv`
int matrix[5][10]
If you have int matrix[5][10], sizeof(matrix) will not return 50 (the total number of integers).
Lesson 931The `sizeof` operator with complex types
int myNumber
Instead of writing int myNumber;, you write struct Player player1;.
Lesson 600Declaring struct variables
int numbers[3]
In C, if you write int numbers[] = {10, 20, 30};, the compiler looks at your list, counts three items, and automatically treats it as int numbers[3].
Lesson 393Omitting size during initializationLesson 472Accessing arrays with pointer notation
int numbers[5]
When you declare an array like int numbers[5];, you aren't just creating a row of five integers; you are also creating a convenient way to find the very first element.
Lesson 471Array names as constant pointersLesson 474Passing arrays to functions as pointersLesson 476Pointer to the start of an array
int pi = 3
While the C compiler won't force you to use uppercase (it will let you name a constant int pi = 3;), doing so makes your code much harder for humans to read.
Lesson 117Naming conventions for constants
int playerHealth
In the example above, int playerHealth; tells the computer: "Reserve a spot in memory for a whole number and call it playerHealth." You can then put a value in it using the equals sign.
Lesson 81The `int` keyword
int power
If you declare a variable (int power;) but forget to initialize it, and then try to use it in a calculation, your program will use whatever "garbage" number happened to be sitting in that memory slot from a previous task.
Lesson 79Declaration vs. Initialization
int pp
When we declare int p, we are telling C: "This variable holds the location of an integer." When we declare int *pp, we are saying: "This variable holds the location of a variable that holds the location of an integer."
Lesson 501Declaring `int **pp`
int ptr
A pointer to a pointer (declared as int ptr) is like a locked box that contains a second treasure map.
Lesson 502Visualizing pointer chains
int ptr = (int)malloc(sizeof(int))
If you have ever looked at C++ code, you might see something like int ptr = (int)malloc(sizeof(int));.
Lesson 540Casting malloc return in C vs C++
int ptrToPtr
In C, we denote this with two asterisks: int ptrToPtr;.
Lesson 500Concept of double indirection
int reserved1
By using unnamed bit-fields, you ensure that mode starts at exactly the 6th bit without cluttering your code with "dummy" variables like int reserved1; or int ignoreMe;.
Lesson 650Unnamed bit-fields for padding
int roomNumber
When you declare a single variable, like int roomNumber;, you are booking one room.
Lesson 389The relationship between array size and memory
int score = 0
In C, if you define int score = 0; in file1.c and then define int score = 0; again in file2.c, the compiler will get confused and throw a "multiple definition" error.
Lesson 77Garbage values and uninitialized variablesLesson 143The `auto` keywordLesson 146The `extern` keyword for multi-file codeLesson 793What should NOT go in a headerLesson 921Sharing variables across files with `extern`
int score = 10
When you write a line of code like int score = 10;, two specific things happen in your computer's memory:
Lesson 448How variables are stored in RAM
int score = 100
When you write int score = 100;, two things happen:
Lesson 80Variables in memory addresses
int score = 50
In C, when you create a variable like int score = 50;, you are putting the "mail" (the number 50) into one of those mailboxes.
Lesson 449The Address-of operator `&`
int scores[10]
Up until now, you have likely created arrays like this: int scores[10];.
Lesson 982Initial memory allocation with malloc
int scores[5]
If you declare an array without any curly braces at all (e.g., int scores[5];), the memory is left "dirty." It contains whatever random data was left there by the last program that used it.
Lesson 383Declaring an array with `type name[size]`Lesson 391Initialization with curly braces `{}`Lesson 392Partial initialization and default zerosLesson 393Omitting size during initializationLesson 397The danger of uninitialized arrays
int screen[5][10]
When you write int screen[5][10], you are telling the computer to set aside enough space for 50 integers.
Lesson 407Declaring 2D arrays: Rows and Columns
int temperature = -10
One handy trick to remember: you can also use the minus sign to represent a negative number directly, such as int temperature = -10;.
Lesson 151The subtraction operator `-`
int total = 10 + 5
For example, if you write int total = 10 + 5;, C first calculates 15 and then stores that final number into the variable named total.
Lesson 150The addition operator `+`
int total = 50
It is as if your code became int total = 50;.
Lesson 328Returning values from functions
int x = (int)2.0f
If we simply cast a float to an int (int x = (int)2.0f), C translates the value to the integer 2.
Lesson 657Using unions for type punning
int x = 10
If you give a global variable a value (like int x = 10;), it goes in the Initialized Data segment.
Lesson 119Integer literals (Hex, Octal, Binary)Lesson 515Taking the address of a functionLesson 1070The process memory layout
int x = printWelcomeMessage()
If you tried to write int x = printWelcomeMessage();, the compiler would get confused and throw an error.
Lesson 324The `void` return type
int_fast32_t
However, portability experts often prefer "minimum-width" types like int_least16_t (at least 16 bits) or "fastest" types like int_fast32_t (the speediest type that holds at least 32 bits).
Lesson 942Limits of `limits.h` and `stdint.h`
int_least16_t
However, portability experts often prefer "minimum-width" types like int_least16_t (at least 16 bits) or "fastest" types like int_fast32_t (the speediest type that holds at least 32 bits).
Lesson 942Limits of `limits.h` and `stdint.h`
INT_MIN
To help us handle these limits, C provides the <limits.h> header, which contains two vital constants: INT_MAX (the largest possible integer) and INT_MIN (the smallest possible negative integer).
Lesson 88Minimum and maximum valuesLesson 96The `<limits.h>` header fileLesson 905Using `INT_MAX` and `INT_MIN` for overflow checksLesson 1154Safe integer arithmetic and overflow checks
int*
It’s the language's way of saying, "I’m pointing to something in memory, but I don't know if it's an integer or a character." Inside the function, you tell C what the data is by "casting" it back to an int*, allowing qsort to remain flexible enough to sort anything.
Lesson 512The `memcpy` function signatureLesson 513Implementing a generic swap functionLesson 527Pointer type-punning dangersLesson 540Casting malloc return in C vs C++Lesson 881Handling the `void*` return of `bsearch`Lesson 936Strict aliasing rule violationsLesson 1059Using C library 'qsort' function
int* p
You might see programmers write int p;, int p;, or even int * p;.
Lesson 455Declaring pointer variables with `*`
int32_t
Use <stdint.h> types like int32_t or uint8_t when you need guaranteed, consistent variable sizes across different computers.
Lesson 86Fixed-width types from `<stdint.h>`Lesson 942Limits of `limits.h` and `stdint.h`
int8_t
Use signed char or unsigned char (or even better, int8_t or uint8_t from <stdint.h>).
Lesson 86Fixed-width types from `<stdint.h>`Lesson 945The significance of `char` signness
integer
Even better, use an integer as your loop counter and calculate the decimal value based on that integer.
Lesson 320Floating point precision issues in loop conditionsLesson 493Pointer to a constant (`const int *p`)
integer division
If you divide two integers (whole numbers), C performs integer division.
Lesson 130Forcing floating-point divisionLesson 154Floating-point division
integer overflow
This is called integer overflow, and it can lead to massive bugs or security holes.
Lesson 882Common pitfalls in comparison function logicLesson 905Using `INT_MAX` and `INT_MIN` for overflow checks
Integer Promotion
The most common form of this is Integer Promotion.
Lesson 121What is type promotion?
integerPi
In the code above, integerPi becomes exactly 3.
Lesson 125Risks of narrowing conversions
integers
%d: Used for integers (whole numbers like 5, -42, or 0).
Lesson 49Introduction to Format Specifiers
integral types
To achieve this, it only accepts integral types.
Lesson 262Switch restrictions: integral types only
Integration testing
Integration testing is like putting three gears together and turning the first one to see if the third one moves.
Lesson 1177Integration testing vs. Unit testing
intent
Using const isn't just about preventing errors; it's about intent.
Lesson 35Documenting intent vs mechanicsLesson 496When to use `const` with pointers
INTEREST_RATE
You define a variable for the INTEREST_RATE.
Lesson 114Why use constants?
Interface
By defining a standard set of function pointers, you create an Interface: a contract that says "any struct of this type will have a function named operate, but how it operates is up to the specific instance."
Lesson 675Implementing an interface with function pointers in structs
internal
When you apply the static keyword to a global variable or function, you change its linkage from external to internal.
Lesson 919The `static` keyword in global scope
Internal fragmentation
Internal fragmentation is like buying a large suitcase to carry a single pair of socks.
Lesson 582Internal vs External fragmentation
Internal Linkage
Internal Linkage is like a conversation happening inside a specific apartment; the neighbors next door have no idea it’s happening.
Lesson 918Internal vs external linkage basics
internal_key
By using opaque types, you create a "contract." You can change internal_key to a double or rename it entirely inside the .c file, and the user’s code won't break.
Lesson 673Opaque types with header files
interrupt service routines
Use volatile whenever you are dealing with memory-mapped I/O, interrupt service routines, or shared global variables in multi-threaded applications where a value might change without the compiler seeing it happen in the current code block.
Lesson 914How `volatile` prevents compiler optimization
into
If you try to pass 0 or -1.0 into log(), your program will return a special value called NaN (Not a Number) or -HUGE_VAL, representing negative infinity.
Lesson 234Safety with explicit castsLesson 688Zero-padding numerical outputLesson 862Exponential and logarithmic functions: `exp`, `log`, `log10`
into the variable
It performs this action, putting the value 5 into the variable b.
Lesson 196Chained assignments `a = b = c`
intPtr
Because you told C that intPtr points to an int, the compiler handles the math behind the scenes.
Lesson 461Implicit vs explicit pointer typesLesson 465How data types affect step size
intrinsics
Most modern compilers (like GCC and Clang) provide intrinsics: built-in functions that talk directly to the CPU.
Lesson 956The `__builtin_bswap` compiler intrinsics
intt
This could be a missing semicolon, a misspelled keyword like intt instead of int, or an unclosed curly brace.
Lesson 1156The difference between syntax and logic errors
invalid
To make this work, you choose a value that would be impossible or invalid in a real scenario.
Lesson 314The Sentinel Value pattern
Invalid Read
An Invalid Read means you tried to look at data you don't own, while an Invalid Write means you tried to change it.
Lesson 568Finding invalid reads and writes
Invalid read of size 4
Invalid read of size 4 (The "size 4" usually means an int).
Lesson 1166Identifying 'Invalid Read' errors
invalid read or write
When you use malloc, the clerk gives you a room key (a pointer) and says, "You have access to room 402." An invalid read or write happens when you try to barge into room 403 or try to sleep in the hallway.
Lesson 568Finding invalid reads and writes
Invalid Write
An Invalid Read means you tried to look at data you don't own, while an Invalid Write means you tried to change it.
Lesson 568Finding invalid reads and writes
inventory.h
As your projects grow, you might include player.h in your main.c, but also in inventory.h.
Lesson 791How `#pragma once` works
InventoryItem
You don't have to manage two different struct types for liquids and solids; you have one master InventoryItem that adapts to your needs while keeping common data (like id) in a single, predictable place.
Lesson 670Combining structs and unions
invitation
To let someone in, they must meet two criteria: they must have an invitation AND they must be wearing formal shoes.
Lesson 253Short-circuit evaluation in logical AND
IPv4
While the operating system uses a generic structure called sockaddr for all types of communication (like Bluetooth or internal files), we use the specialized sockaddr_in specifically for IPv4 internet networking.
Lesson 1109The sockaddr_in structure
is "stronger" than
By internalizing that * is "stronger" than +, you can write complex formulas without worrying that the computer will misinterpret your intent.
Lesson 216Precedence of `*` over `+`
is 1 and
You can look at the "Variables" window and see that i is 1 and sum is 0.
Lesson 1162Setting breakpoints and stepping through code
is a
If x is a float, it returns the function pointer for sqrtf.
Lesson 979Mathematical macros using `_Generic`
is a very large negative number
If a is a very large positive number and b is a very large negative number, a - b might exceed the limits of a signed integer, wrap around to a negative value, and tell qsort that the larger number is actually smaller.
Lesson 882Common pitfalls in comparison function logic
is also a valid number
Because 0 is also a valid number, atoi cannot tell you if the conversion failed or if the user actually typed "0".
Lesson 871Converting strings to integers with `atoi` and `atol`
is greater than
To check if adding b to a will cause an overflow, you check if a is greater than INT_MAX - b.
Lesson 905Using `INT_MAX` and `INT_MIN` for overflow checks
is just
To the compiler, Cinnamon is just 0 and Salt is just 1.
Lesson 667Type safety concerns with enums
is not
If 00000001 is 1, then 10000001 is not -1.
Lesson 91How bits represent negative numbers
is not equal
If your name is not equal to a name on that list, you are allowed to pass.
Lesson 162The inequality operator `!=`
is one digit and
Because 9 is one digit and 10 is two, the text on the screen will shift or "jump" to the right as the numbers grow.
Lesson 688Zero-padding numerical output
is represented as
In memory, the number 1 is represented as 00 00 00 01 (in hex).
Lesson 951Checking system endianness at runtime
is the absolute definition of
In the eyes of a C compiler, the number 0 is the absolute definition of false.
Lesson 163Truthiness: 0 vs non-zero
is the business card
If p is the business card, *p is the act of actually driving to the office and looking inside.
Lesson 456The difference between `int *p` and `*p`
is_
Booleans should be questions: If a variable is true or false, prefix it with is_, has_, or can_.
Lesson 1197Meaningful variable naming conventions
is_authenticated
For example, is_authenticated is much clearer than status.
Lesson 1197Meaningful variable naming conventions
is_door_locked()
The real function is_door_locked() checks physical hardware.
Lesson 1176Mocking simple dependencies
is_on
Endianness and Ordering: If you define is_on first, one compiler might put it in the "Lowest" (Rightmost) bit of the byte, while another might put it in the "Highest" (Leftmost) bit.
Lesson 954Bit-fields in structures and portability
is_raining
If you have a variable representing is_raining, then !is_raining means "it is sunny." It takes whatever boolean value you give it and turns it into its exact opposite.
Lesson 167Logical NOT `!`Lesson 249Logical NOT (!) for inversion
is_running
If you have a simple flag (like is_running) that many threads need to check, an atomic load is the most efficient way to handle it without risking data corruption.
Lesson 1143Atomic load and store
is_weekend
However, because the else is closest to if (is_sunny), it actually means: "If it is the weekend but NOT sunny, stay inside." If is_weekend were 0, nothing would print at all!
Lesson 256Nested if statements and dangling else logic
isalpha
Think of isalpha as a digital gatekeeper that only lets the letters A through Z (either uppercase or lowercase) pass through.
Lesson 840Testing for alphabetic characters with `isalpha`
isalpha()
When you use the functions in <ctype.h>, such as isalpha(), isdigit(), or tolower(), you might assume they accept a standard char.
Lesson 846The importance of casting to `unsigned char` in `ctype` functions
isalpha(c)
Use isalpha(c) from <ctype.h> to quickly check if a character is a letter (A-Z or a-z).
Lesson 840Testing for alphabetic characters with `isalpha`
isdigit
What you'll learn: Why you must cast arguments to (unsigned char) when using functions like isdigit or toupper to prevent program crashes.
Lesson 841Checking for digits with `isdigit` and `isxdigit`Lesson 846The importance of casting to `unsigned char` in `ctype` functions
isdigit()
When you use the functions in <ctype.h>, such as isalpha(), isdigit(), or tolower(), you might assume they accept a standard char.
Lesson 846The importance of casting to `unsigned char` in `ctype` functions
isEmpty()
This is usually a simple function called isEmpty() that checks if your stack pointer or top index is at its starting position (typically -1 or NULL).
Lesson 1013Handling Stack Underflow
isgraph
The relationship is simple: isgraph is the parent category, and ispunct is a specific sub-category that excludes alphanumeric characters.
Lesson 843Distinguishing `ispunct` and `isgraph`
isgraph()
When working with the <ctype.h> library, you will encounter two functions that seem to overlap: ispunct() and isgraph().
Lesson 843Distinguishing `ispunct` and `isgraph`
isinf()
Use isnan() and isinf() to detect mathematical errors or overflows, as standard equality checks (==) will not work on these special constants.
Lesson 867Handling `NAN` and `INFINITY` constants
islower()
The functions isupper() and islower() act like digital detectives.
Lesson 844Case testing with `isupper` and `islower`
islower(c)
Use isupper(c) to check for capital letters and islower(c) to check for small letters, ensuring you #include <ctype.h> first.
Lesson 844Case testing with `isupper` and `islower`
isnan()
Use isnan() and isinf() to detect mathematical errors or overflows, as standard equality checks (==) will not work on these special constants.
Lesson 867Handling `NAN` and `INFINITY` constants
isOn
The colon tells the compiler: "Don't give isOn a full integer's worth of space; just give it one solitary bit."
Lesson 648The colon operator and bit width
ispunct
The relationship is simple: isgraph is the parent category, and ispunct is a specific sub-category that excludes alphanumeric characters.
Lesson 843Distinguishing `ispunct` and `isgraph`
ispunct()
When working with the <ctype.h> library, you will encounter two functions that seem to overlap: ispunct() and isgraph().
Lesson 843Distinguishing `ispunct` and `isgraph`
isspace
The isspace function provides a clean, portable way to detect spaces, tabs, and newlines by returning a non-zero value for any whitespace character.
Lesson 842Identifying whitespace with `isspace`
isupper()
The functions isupper() and islower() act like digital detectives.
Lesson 844Case testing with `isupper` and `islower`
isupper(c)
Use isupper(c) to check for capital letters and islower(c) to check for small letters, ensuring you #include <ctype.h> first.
Lesson 844Case testing with `isupper` and `islower`
isxdigit
Use isdigit to find numbers 0-9, and isxdigit when you need to include the letters A-F for hexadecimal values.
Lesson 841Checking for digits with `isdigit` and `isxdigit`
it does not return
The most critical thing to understand about execl() is that it does not return.
Lesson 1081Replacing process images with execl()
Item 0: 0
If you ran this code, you might see Item 0: 0, Item 1: 32764, and Item 2: -1073741824.
Lesson 397The danger of uninitialized arrays
Item 1: 32764
If you ran this code, you might see Item 0: 0, Item 1: 32764, and Item 2: -1073741824.
Lesson 397The danger of uninitialized arrays
Item 2: -1073741824
If you ran this code, you might see Item 0: 0, Item 1: 32764, and Item 2: -1073741824.
Lesson 397The danger of uninitialized arrays
itemPrice * SALES_TAX_RATE
Readability: itemPrice * SALES_TAX_RATE reads like a human sentence.
Lesson 118Literal vs. Symbolic constants
items[i] = 0
In the code example above, GDB would stop on the line items[i] = 0; the moment i reached 3, revealing a buffer overflow that accidentally overwrote the target variable.
Lesson 836Using `watch` for memory changes
itemsRead
If itemsRead is less than 3, we know the file ended early or an error occurred.
Lesson 696Handling the return value of scanfLesson 726Reading raw bytes with fread
Iteration
Iteration is like standing at the bottom and saying, "While I haven't reached the top, move my left foot, then my right foot." You stay in one "state" and keep track of your progress with a counter.
Lesson 358Iteration vs. Recursion comparison
Iteration Number
To trace a loop, draw three columns: Iteration Number, Variable Values, and Condition Check (True/False).
Lesson 273Tracing while loop execution on paper

J

j < 5
Instead of saying j < 5 (which creates a fixed width), we say j <= i.
Lesson 302The 'triangle' pattern logic
j <= 1
Row 1 (i = 1): The inner loop runs while j <= 1.
Lesson 302The 'triangle' pattern logic
j <= 5
Row 5 (i = 5): The inner loop runs while j <= 5.
Lesson 302The 'triangle' pattern logic
j <= i
Instead of saying j < 5 (which creates a fixed width), we say j <= i.
Lesson 302The 'triangle' pattern logic
Job
Imagine you have a Person struct that contains a pointer to a Job struct.
Lesson 629Deep vs shallow copies of nested structs
John
If your file has a full name like John Doe, fscanf with %s will only grab John.
Lesson 719Formatted file input with fscanf
John Doe
If your file has a full name like John Doe, fscanf with %s will only grab John.
Lesson 719Formatted file input with fscanf
joinable
By default, when you create a thread in C using pthread_create, it is joinable.
Lesson 1121Detaching threads

K

K&R
In C, two "dialects" rule the landscape: K&R and Allman.
Lesson 1196Consistency: K&R vs. Allman style
Kernel Style
They follow established guides like the Google C Style Guide or the Kernel Style.
Lesson 40C coding style guides
key
A robust lookup function will check if the key stored at that index actually matches the search_key you provided.
Lesson 505Accessing data through double dereferenceLesson 1031Key-Value pair conceptLesson 1036Hash table lookup
Key-Value pair
This is the heart of the Key-Value pair concept.
Lesson 1031Key-Value pair concept
Key-Value Store
In programming, this is a Key-Value Store.
Lesson 1212Project scope: A simple Key-Value store
Keys must be unique
The most important rule is that Keys must be unique. Just as two people can't have different lockers with the same number, you cannot have two identical keys in a data structure.
Lesson 1031Key-Value pair concept
kill
If you want to force a stuck program to close (using the kill command) or check how much memory a specific program is using, you need to know its PID.
Lesson 1067What is a process ID (PID)Lesson 1085Common signals: SIGINT, SIGTERM, SIGKILL
kill -9
When the OS sends SIGKILL (via kill -9), it doesn't even talk to your program.
Lesson 1085Common signals: SIGINT, SIGTERM, SIGKILL
kill()
If you try to kill() a process belonging to another user or the System, the function will return -1 and set errno to indicate that permission was denied.
Lesson 1086Sending signals with kill()

L

L-value
To understand how this works, you need to understand the two "sides" of the operation: the L-value and the R-value.
Lesson 191L-values vs R-values
label
In the example above, the compiler inserts 3 bytes of empty padding after label so that quantity starts on a memory address that is a multiple of 4.
Lesson 73What is a variable?Lesson 309Legitimate use case: breaking out of nested loopsLesson 637The sizeof operator on structs
label maker
sprintf, on the other hand, is like a label maker.
Lesson 703Formatting strings in memory with sprintf
label_name
The goto statement requires a named label followed by a colon (label_name:) to act as a destination, allowing the program to jump directly to that line.
Lesson 307The syntax of the goto statement
Large integers
Large integers (like long long) are in the middle.
Lesson 229Usual arithmetic conversions
largest
While a struct allocates enough memory to hold all its members simultaneously, a union only allocates enough memory to hold its largest member.
Lesson 653Defining a union with the union keyword
last fixed argument
The va_start macro requires two pieces of information: your va_list variable and the name of the last fixed argument before the dots.
Lesson 373Using `va_list` and `va_start`
Last Name
For example, if you want to sort a list of employees by Department and then by Last Name, you would first sort by name, then perform a stable sort by department.
Lesson 1052Stability in sorting algorithms
LD_LIBRARY_PATH
LD_LIBRARY_PATH is an environment variable that tells the dynamic linker extra directories to search for shared libraries at runtime.
Lesson 816Runtime library loading and `LD_LIBRARY_PATH`
leaks
Most developers in the Apple ecosystem use a built-in tool called leaks or the Address Sanitizer (ASan) instead, but for this course, we recommend using a Linux virtual machine or Docker if you want to follow along with Valgrind specifically.
Lesson 564Installing Valgrind Memcheck
Least Privilege
It follows the principle of Least Privilege: a variable should only exist where it is absolutely needed.
Lesson 285Scope of the loop variable in C99 vs C89
left
When you are standing at a node, the left pointer leads you to a whole new subtree where all the values are smaller, and the right pointer leads to a subtree where all the values are larger.
Lesson 78Assigning values with `=`Lesson 190Simple assignment `=`Lesson 1022Recursive tree node structureLesson 1026Pre-order and Post-order traversal
left subtree
In a BST, every single value in the left subtree must be smaller than the Root, and every single value in the right subtree must be larger than the Root.
Lesson 1023Properties of a Binary Search Tree
left-alignment
If you want left-alignment, simply put a minus sign (-) before the number.
Lesson 685Specifying field width for alignment
Left-to-Right Associativity
In C, this "first-come, first-served" logic is called Left-to-Right Associativity.
Lesson 213Left-to-right associativity
len
Rather than writing a manual loop every time, the C Standard Library provides a handy tool called strlen (short for string length).
Lesson 847Finding string length with `strlen`
length
Both functions require three main ingredients: the socket file descriptor (the ID of your connection), a buffer (the actual data), and the length of that data.
Lesson 76Multiple declarations in one lineLesson 420Length vs Size of a string arrayLesson 423Getting length with `strlen`Lesson 1115Sending and receiving over socketsLesson 1193Avoiding redundant calculations in loops
length - 1
Loop Bounds: Ensure your loop stops exactly at length - 1.
Lesson 403Calculating the sum and average
Length (strlen)
Length (strlen): This tells you how much data is actually in the array.
Lesson 423Getting length with `strlen`
length + 1
When you define the size of your array, you must ensure the size is at least length + 1.
Lesson 419Initializing strings with sizesLesson 420Length vs Size of a string array
letter
To use unions successfully, your code must keep track of which member is currently "active." If you write to the letter, only read from the letter until you assign something else.
Lesson 655Accessing union members
letter = getchar()
In this example, letter = getchar() happens first.
Lesson 197Assignment expression return value
level
C looks at the "first slot" of the function call, sees 10, and assigns that value to the first parameter, level.
Lesson 337Positional matching of argumentsLesson 601The dot operator for member accessLesson 729Reading structs back into memory
Level 0
In GDB, the "top" of the stack (the current function) is Level 0.
Lesson 834Moving between frames with `up` and `down`
Level 1
Then it returns to Level 2 to print "(Done with 2)", and finally back to Level 1 to print "(Done with 3)".
Lesson 356Tracing a simple recursive callLesson 834Moving between frames with `up` and `down`
Level 2
Then it returns to Level 2 to print "(Done with 2)", and finally back to Level 1 to print "(Done with 3)".
Lesson 356Tracing a simple recursive call
Level 3
The computer goes back to Level 3 to finish the last line of code, printing "(Done with 1)".
Lesson 356Tracing a simple recursive call
levelUp
When levelUp(heroStrength) is called, the value 50 is plucked out of main's memory and pasted into a new memory location labeled strength inside levelUp.
Lesson 344Visualizing the stack frame copy
levelUp(heroStrength)
When levelUp(heroStrength) is called, the value 50 is plucked out of main's memory and pasted into a new memory location labeled strength inside levelUp.
Lesson 344Visualizing the stack frame copy
lexicographical comparison
To see if "apple" matches "apple," we use a process called lexicographical comparison via the strcmp (string compare) function.
Lesson 850Lexicographical comparison with `strcmp`
lib
There is a small trick here: by convention, shared library files start with lib and end with .so (on Linux) or .dylib (on macOS).
Lesson 812Linking with static librariesLesson 815Linking with shared libraries `-l` and `-L`
libc
Since a shared library (like libc) is used by many different programs simultaneously, it cannot guarantee it will always be loaded at the exact same memory address.
Lesson 814Position Independent Code `-fPIC`
libmathhelper.so
For example, to link a library file named libmathhelper.so, you simply write -lmathhelper.
Lesson 815Linking with shared libraries `-l` and `-L`
libmathutils.a
If you have a library called libmathutils.a, you tell your compiler where to find it and to include it during the final build phase:
Lesson 812Linking with static libraries
libphysics.so
Imagine you have a project where your library is stored in a folder named /libs and the file is named libphysics.so.
Lesson 815Linking with shared libraries `-l` and `-L`
library
To perform tasks like displaying words or reading what a user types, you need to "borrow" tools from a specialized toolbox called a library.
Lesson 41The `stdio.h` library
library name
Use -L to specify the folder path and -l to specify the library name (minus the 'lib' prefix).
Lesson 815Linking with shared libraries `-l` and `-L`
library shelf location
Passing by pointer is like handing your colleague a small sticky note with the library shelf location of the manual.
Lesson 491Efficiency of passing large structs by pointer
libs
By adding your custom directory to LD_LIBRARY_PATH, the linker checks your libs folder first.
Lesson 816Runtime library loading and `LD_LIBRARY_PATH`
libutils.a
If you want a teammate to use your math functions, you can just give them the libutils.a file and the corresponding .h header files.
Lesson 811Creating archives with the `ar` tool
Lifetime
Lifetime is the duration the actor is actually in the building.
Lesson 139Lifetime vs. Scope
LIFO
The defining rule of a stack is LIFO, which stands for Last-In, First-Out.
Lesson 1006Stack abstract data type conceptLesson 1007Array-based stack implementation
light
Because the compiler treats light as a simple int, it doesn't verify if the new value makes sense for that specific category.
Lesson 667Type safety concerns with enums
limit
If you run this, C converts -5 to a massive unsigned integer to match the type of limit.
Lesson 127Mixing signed and unsigned in math
limit = 100
Is it okay to change limit = 100 halfway through the code?
Lesson 117Naming conventions for constants
limited scope
A static variable has a limited scope (it can only be seen inside its function) but a permanent lifetime (it stays in memory for the entire duration of the program).
Lesson 139Lifetime vs. Scope
limits.h
Use limits.h to check the boundaries of standard types and stdint.h to define variables with specific size requirements, ensuring your code runs safely on everything from a toaster to a supercomputer.
Lesson 88Minimum and maximum valuesLesson 904Integer ranges in `limits.h`Lesson 942Limits of `limits.h` and `stdint.h`
line
It prints every line that contains that string.
Lesson 1204Project scope: A custom `grep` clone
Line 5
If you try to compile this, the compiler will point to Line 5.
Lesson 67Line number tracking
Line B
Typing next (or just n) will execute the greet() function entirely and move the pointer directly to Line B.
Lesson 830Stepping through code with `next` and `step`
line buffering
By default, when you print to the screen (stdout), C uses line buffering.
Lesson 749Full buffering vs Line buffering
line-buffered
Standard output (stdout) is typically line-buffered.
Lesson 748How C buffers I/O for speed
Linear Allocator
A Linear Allocator (also called a Bump Allocator) is like a tall stack of clean trays.
Lesson 586Linear or Bump allocators
Linear Search
You wouldn't start at page one and flip through every single sheet (that is Linear Search).
Lesson 1061Linear Search on arraysLesson 1062Binary Search: Iterative approachLesson 1064Importance of sorted data
linked list
This is the fundamental building block of a linked list, a data structure where each element (a "node") points to the next one in line.
Lesson 626Self-referential structs for linked listsLesson 989Defining the self-referential node struct
Linker
When you compile this code, the compiler sees the declaration and says, "Okay, I'll leave a placeholder here for calculate." However, once the compiler finishes, the Linker steps in to connect the placeholder to the actual logic.
Lesson 28Phase 4: The LinkerLesson 29Understanding `.o` and `.obj` filesLesson 64Library linking basicsLesson 70The 'undefined reference' linker errorLesson 381Compiling multiple `.c` filesLesson 807Understanding 'undefined reference' errorsLesson 808The executable ELF format
Linker Error
If you try to use a function that doesn't exist, you get a Linker Error (like the famous undefined reference).
Lesson 28Phase 4: The Linker
Linker Substitution
To do this simply in C, we often use Linker Substitution.
Lesson 1176Mocking simple dependencies
linking
Building a C program is usually a two-step dance: compiling (translating code into machine language) and linking (stitching those translations into a final app).
Lesson 804The `-c` flag for compilationLesson 805What is inside a `.o` file
Linux
If you are on Linux, you likely already have a package manager—think of it like an app store for your command line.
Lesson 10Installing GCC on Linux/macOS
list
If you compiled this with the -g flag (which is required for GDB to "see" your text), typing list inside GDB would show you exactly these lines, complete with their line numbers.
Lesson 825Compiling with debug symbols `-g`Lesson 828Listing source code with `list`Lesson 853Tokenizing strings with `strtok`
list 20
Shows the code surrounding line 20.
Lesson 828Listing source code with `list`
list main
Shows the code at the start of the main function.
Lesson 828Listing source code with `list`
list of locations
A pointer to a pointer is like a finger pointing to a list of locations where those folders are kept.
Lesson 504Dynamic 2D array structures
listen()
The listen() function marks a socket as passive, allowing it to queue incoming connection requests in a backlog until the program is ready to process them.
Lesson 1112Listening for connectionsLesson 1113Accepting client connections
Literal Constant
In C, the number 0.07 is a Literal Constant.
Lesson 118Literal vs. Symbolic constants
literal string
When you want the messenger to deliver a message exactly as you wrote it—letter for letter—we call that a literal string.
Lesson 43Printing literal strings
Little Endian
Little Endian flips it: you put "EE" in the first box, "FF" in the second, and "CO" in the third.
Lesson 950Big Endian vs Little Endian explained
Little-Endian
Some CPUs (like Intel) store the "small" part of a number first (Little-Endian), while others store the "big" part first (Big-Endian).
Lesson 951Checking system endianness at runtimeLesson 952Network byte order and `htons`/`ntohs`Lesson 953Manual byte swapping techniques
lives
The computer matches the first %d with the first variable after the comma (score), and the second %d with the second variable (lives).
Lesson 50Printing integers with `%d`
LL
To tell the compiler a constant number is a long long, we often add the suffix LL to the end of the number.
Lesson 83The `long long` type
lldb
Compile with the -g flag, then use the run command inside gdb or lldb to execute your program under professional supervision.
Lesson 1161Starting a program in `gdb` or `lldb`
LLONG_MAX
The largest value a long long can hold. This is guaranteed to be at least 64 bits (roughly 9 quintillion) on any modern system.
Lesson 88Minimum and maximum valuesLesson 909Managing large constants: `LONG_MAX` vs `LLONG_MAX`
Load Factor
In a hash table, this "crowdedness" is called the Load Factor.
Lesson 1037Load factor and rehashing
Local Paths
#include "my_settings.h": The quotes tell the compiler to look in Local Paths (your desk) first, and then check any paths you provided with the -I flag.
Lesson 63Header search paths
local variable
One common mistake is returning a pointer to a local variable created inside the thread function.
Lesson 136Local variables and block scopeLesson 1120Returning values from threads
localScore
When addScore(myScore) is called, C looks at the value inside myScore (which is 50) and copies that number into a brand new memory bucket named localScore.
Lesson 486Pass-by-value limitations
localtime
When you look inside the struct tm returned by localtime, there are two "gotchas" that trip up every C programmer:
Lesson 892Converting `time_t` to local time with `localtime`
localtime()
The localtime() function is your "sorter." It takes that raw number and breaks it down into a struct tm, which contains separate variables for years, months, days, hours, and minutes.
Lesson 891Breaking down time with `struct tm`Lesson 892Converting `time_t` to local time with `localtime`
lock
To fully unlock the mutex, the thread must call unlock the exact same number of times it called lock.
Lesson 1126Locking and unlocking mutexesLesson 1130Recursive mutexes
Lock Ordering
The simplest way to prevent this architectural disaster is Lock Ordering.
Lesson 1139Common synchronization pitfalls
lock-free
An algorithm is lock-free if, at any point in time, at least one thread in the system is making progress.
Lesson 1146Lock-free programming concepts
locked box
The paper tells you where to find a locked box (the first pointer).
Lesson 505Accessing data through double dereference
log_debug_info()
If you tried to call log_debug_info() from a different file like main.c, the compiler would behave as if the function doesn't exist, even if you typed the name correctly.
Lesson 798Static functions for file scoping
log_internal_state()
If you try to access engine_temp or call log_internal_state() from a different file like main.c, the compiler will act as if they don't exist.
Lesson 919The `static` keyword in global scope
log_message
You could create a Logger struct with a function pointer log_message.
Lesson 675Implementing an interface with function pointers in structs
log()
The math.h library provides three essential functions for this: exp(), log(), and log10().
Lesson 862Exponential and logarithmic functions: `exp`, `log`, `log10`
log(x)
This is the natural logarithm ($\ln$). It is the inverse of exp(). If exp(1) is $e$, then log(e) is 1.
Lesson 862Exponential and logarithmic functions: `exp`, `log`, `log10`
log10()
The math.h library provides three essential functions for this: exp(), log(), and log10().
Lesson 862Exponential and logarithmic functions: `exp`, `log`, `log10`
log10(x)
This is the common logarithm (base 10). This is very intuitive for humans because it essentially counts the number of zeros in a large number. For example, log10(1000) is 3.
Lesson 862Exponential and logarithmic functions: `exp`, `log`, `log10`
Logger
You could create a Logger struct with a function pointer log_message.
Lesson 675Implementing an interface with function pointers in structs
logger.h
Level 1 (The Base): Low-level utilities like logger.h or constants.h.
Lesson 802Dependency graphing in your head
logger.o
Because of $@ and $<, this single rule works for logger.o, math_utils.o, or player.o without you ever having to type those names manually.
Lesson 822Automatic variables like `$@` and `$<`
logic
Variables separate the configuration of your build from the logic of your build.
Lesson 257Using if statements for input validationLesson 821Using variables in Makefiles
logic.c
When you compile your test, you simply link logic.c and test_main.c with your mock_sensor.c instead of the real hardware driver.
Lesson 1176Mocking simple dependenciesLesson 1210Structuring the project into multiple `.c` files
Logical NOT
This is where the Logical NOT operator comes in.
Lesson 249Logical NOT (!) for inversion
logical operators
Use logical operators for decision-making, such as if statements (e.g., "If the user is logged in AND has a premium account").
Lesson 173Logical vs Bitwise distinction
long double
While a double usually occupies 8 bytes of memory, a long double often uses 12 or 16 bytes (depending on your computer’s architecture), allowing it to store many more digits after the decimal point with significantly less rounding error.
Lesson 99The `long double` typeLesson 103The `<float.h>` header fileLesson 120Floating-point suffixes (f, L)Lesson 647Restrictions on bit-field types
long long
A reliable rule of thumb for C programming is to order your members by size, descending. Start with your double or long long types, move to int, then short, and put char at the very bottom.
Lesson 83The `long long` typeLesson 88Minimum and maximum valuesLesson 187Shift operator constraintsLesson 229Usual arithmetic conversionsLesson 641Reordering members to reduce padding
LONG_MAX
Always use LONG_MAX and LLONG_MAX from <limits.h> to check for overflows and define boundaries for large integer variables.
Lesson 909Managing large constants: `LONG_MAX` vs `LLONG_MAX`
Look in this directory
The uppercase -L flag stands for Look in this directory.
Lesson 815Linking with shared libraries `-l` and `-L`
Look to the left
Look to the left. If you see *, say "pointer to." If you see a type (like int), say that type.
Lesson 926Reading declarations with the 'Right-Left' rule
Look to the right
Look to the right. If you see [], say "array of." If you see (), say "function returning."
Lesson 926Reading declarations with the 'Right-Left' rule
looking at the address
If you use the name alone greet, you are just looking at the address written on the paper.
Lesson 515Taking the address of a function
looks
It is important to remember that %.nf only changes how the number looks on the screen.
Lesson 102Formatting decimals with `%.nf`
loop
While losing a few bytes once might not be noticed, losing those bytes inside a loop or a recursive function creates a compound interest effect that can quickly consume every byte of RAM your computer has available.
Lesson 317The Counter pattern (counting occurrences)Lesson 552Leaking in loops and recursion
looping
By limiting the scope to an array of structs, you focus on the most important C concepts: string manipulation (copying names into the keys), looping (searching for a specific key), and memory layout.
Lesson 1212Project scope: A simple Key-Value store
lost your original key
If the mover comes back and says, "Sorry, there are no larger units available," but then realizes they lost your original key during the search, you are in trouble.
Lesson 547Handling realloc failure safely
low
We then tell the function to handle the left half (from low to mid) and the right half (from mid + 1 to high).
Lesson 1055Merge Sort: Recursive splittingLesson 1062Binary Search: Iterative approachLesson 1063Binary Search: Recursive approach
low-level
A low-level approach is like standing in the kitchen.
Lesson 1What is a low-level language?
lowercase
Always name your files in lowercase, use underscores instead of spaces, and ensure they end in a lowercase .c.
Lesson 16Naming conventions for .c files
lowercase_snake_case
While modern computers can handle spaces and capital letters, C tradition (and many professional tools) prefers lowercase_snake_case.
Lesson 16Naming conventions for .c files
ls -l
If you run ls -l, you'll notice the file type starts with a p (for pipe).
Lesson 1107Creating FIFOs with mkfifo()
ls | grep
When you run ls | grep, the shell forks a process for ls and uses dup2 to point its stdout to a pipe.
Lesson 1105Redirecting stdout to a pipe
luckyNumbers[3]
If you try to access luckyNumbers[3] in the example above, you are asking C for the fourth item in a three-item list.
Lesson 385Array indexing starting from zero

M

Machine Code
Its job is to take those assembly words and translate them into a specific sequence of binary numbers known as Machine Code.
Lesson 27Phase 3: Assembly to Object CodeLesson 30Creating an executable binary
macOS
If you are on macOS, Apple provides these tools through a package called "Command Line Tools." Open your terminal and type:
Lesson 10Installing GCC on Linux/macOSLesson 564Installing Valgrind Memcheck
macros
Use macros only when you need "generic" behavior (logic that works on int, float, and double simultaneously) or when you need to perform special preprocessor tricks that a regular function simply cannot do.
Lesson 768Defining constants with `#define`Lesson 778Macros vs inline functionsLesson 932Using `typeof` in C23Lesson 1077Capturing child exit status
magnitude
You are interested in the magnitude of the movement, not the direction.
Lesson 866Absolute values for floats with `fabs`
main
Use pthread_exit() when you have complex logic where a thread needs to stop deep inside a nested function, or when you want the main thread to finish its own work but allow background threads to keep processing in the background.
Lesson 6C as a compiled languageLesson 15The concept of a Source FileLesson 17The 'Hello World' codeLesson 20The `main()` function entry pointLesson 21Curly braces `{}` and blocksLesson 23Case sensitivity in CLesson 24The `return 0;` statementLesson 30Creating an executable binaryLesson 31How the OS runs a programLesson 32Executing from the command lineLesson 71Common beginner typosLesson 141Function parameters as local scopeLesson 251Variable scope inside if-else blocksLesson 322What is a function?Lesson 325Writing your first custom functionLesson 327The `return` statement flowLesson 328Returning values from functionsLesson 334Common errors with missing prototypesLesson 341Understanding 'Pass by Value'Lesson 344Visualizing the stack frame copyLesson 345Limitations of pass by valueLesson 346Preparing for pass by referenceLesson 347What is a Stack Frame?Lesson 350Return addresses in memoryLesson 351Visualizing the stack during nested callsLesson 454Introduction to the stack frameLesson 486Pass-by-value limitationsLesson 489Returning multiple values via pointersLesson 490Swapping two numbers using pointersLesson 530Stack frame lifecycle and local variablesLesson 600Declaring struct variablesLesson 760Redirecting streams with freopenLesson 767How `gcc -E` shows preprocessor outputLesson 799The role of the 'main' fileLesson 823Phony targets like `clean` and `all`Lesson 828Listing source code with `list`Lesson 829Setting breakpoints with `break`Lesson 833Inspecting the call stack with `backtrace`Lesson 836Using `watch` for memory changesLesson 875Cleaning up at exit with `atexit`Lesson 993Prepending nodes to the headLesson 1118Passing arguments to threadsLesson 1122Thread-local storage basicsLesson 1123The pthread_exit functionLesson 1174Building a minimal custom test harnessLesson 1202Indentation and whitespace rulesLesson 1208Adding command line flags (e.g., `-i` for case)
main.c
When you run make, Make sees $@ and thinks, "The target is app, so I'll put that there." It sees $< and thinks, "The first dependency is main.c, so I'll put that there." The actual command executed remains gcc main.c -o app.
Lesson 29Understanding `.o` and `.obj` filesLesson 57Basic `gcc` command flagsLesson 59Compiling multiple source filesLesson 146The `extern` keyword for multi-file codeLesson 377Role of the `.h` fileLesson 378Separating interface from implementationLesson 379Using `#include` with quotesLesson 381Compiling multiple `.c` filesLesson 382Sharing functions across modulesLesson 788The purpose of header filesLesson 789The 'duplicate definition' errorLesson 790Creating basic include guardsLesson 791How `#pragma once` worksLesson 796Splitting code into `.c` and `.h`Lesson 797The `extern` keyword for variablesLesson 798Static functions for file scopingLesson 799The role of the 'main' fileLesson 802Dependency graphing in your headLesson 806Linking multiple object filesLesson 809Symbol tables and visibilityLesson 817Why we need build toolsLesson 819Targets, dependencies, and recipesLesson 822Automatic variables like `$@` and `$<`Lesson 824Incremental builds and file timestampsLesson 825Compiling with debug symbols `-g`Lesson 919The `static` keyword in global scopeLesson 921Sharing variables across files with `extern`Lesson 922Using `extern` with functionsLesson 1159Using `__FILE__` and `__LINE__` macrosLesson 1201Using `clang-format` for automationLesson 1210Structuring the project into multiple `.c` filesLesson 1211Writing the Makefile for the project
main.c -> auth.h -> database.h
In this graph, the flow is main.c -> auth.h -> database.h.
Lesson 802Dependency graphing in your head
main.c:5:10: error: expected ';' after expression
main.c:5:10: error: expected ';' after expression
Lesson 66Reading compiler error messages
main.c:5:10: error: expected ';' before 'return'
When you compile a program with an error, your terminal will spit out a message that usually looks like this: main.c:5:10: error: expected ';' before 'return'.
Lesson 67Line number tracking
main.exe
Between your text file (main.c) and your final app (main.exe), there is a crucial middle step: the Object File.
Lesson 29Understanding `.o` and `.obj` files
main.h
If you ever find yourself wanting to include main.h inside database.h, stop!
Lesson 802Dependency graphing in your head
main.o
However, if main.o tries to call a function located in math.o, the object file just leaves a blank space with a sticky note saying, "I'll need someone to plug in the math logic here later."
Lesson 29Understanding `.o` and `.obj` filesLesson 806Linking multiple object filesLesson 819Targets, dependencies, and recipesLesson 824Incremental builds and file timestamps
main()
If you accidentally include a main() function in two different files within the same project, the compiler will get confused—like an airport with two different control towers giving conflicting orders—and will throw a "multiple definition" error.
Lesson 17The 'Hello World' codeLesson 20The `main()` function entry pointLesson 21Curly braces `{}` and blocksLesson 31How the OS runs a programLesson 149Memory segments: Stack vs. DataLesson 306Defining labels in C codeLesson 307The syntax of the goto statementLesson 313Exiting the program with exit()Lesson 322What is a function?Lesson 326Placement of functions in a fileLesson 329Implicit vs. explicit declarationsLesson 330Function prototype syntaxLesson 331Benefits of forward declarationLesson 339Local scope of parametersLesson 342Memory allocation for parametersLesson 348Pushing and popping framesLesson 349Storage of local variablesLesson 351Visualizing the stack during nested callsLesson 353Concept of self-calling functionsLesson 381Compiling multiple `.c` filesLesson 395Initialization of local vs global arraysLesson 454Introduction to the stack frameLesson 503Modifying a pointer inside a functionLesson 600Declaring struct variablesLesson 799The role of the 'main' fileLesson 885Seeding the generator with `srand`Lesson 886Why you should only seed onceLesson 888Getting a unique seed with `time(NULL)`Lesson 1072Process termination and exit codesLesson 1080The execve() family overviewLesson 1093Standard streams (0, 1, 2)Lesson 1119Waiting for threads with pthread_joinLesson 1123The pthread_exit functionLesson 1170Cleaning up heap memory before exitLesson 1175Separating logic from `main()` for testability
MainCode.c
If half are named MainCode.c and the other half are math_functions.c, your project becomes a messy "junk drawer." Sticking to lowercase names with underscores is a "handshake" between you and other programmers, signaling that you follow standard professional conventions.
Lesson 16Naming conventions for .c files
make all
Instead of typing make main, you just type make (which defaults to the first target) or make all.
Lesson 823Phony targets like `clean` and `all`
make clean
When you type make clean to sweep away your old .o files, make will look at that file and say: "clean is up to date." It won't run your cleanup commands because it thinks the "job" of creating a file named clean is already finished.
Lesson 823Phony targets like `clean` and `all`Lesson 1211Writing the Makefile for the project
make main
Instead of typing make main, you just type make (which defaults to the first target) or make all.
Lesson 823Phony targets like `clean` and `all`
Makefile
Once you have saved a file named Makefile (no extension) in your project folder, you no longer need to remember complex GCC arguments.
Lesson 61Introduction to `make` and MakefilesLesson 62Automating the build processLesson 818Structure of a Makefile RuleLesson 1211Writing the Makefile for the project
malloc
While calloc is slightly slower than malloc (because the computer has to spend time writing zeros to every memory address), it prevents a very common class of bugs: accidentally using uninitialized variables.
Lesson 532Introduction to the Heap segmentLesson 534Scope of heap-allocated dataLesson 536Header file stdlib.h for allocationLesson 537The malloc function signatureLesson 538Calculating size with sizeofLesson 539Checking for NULL return valuesLesson 540Casting malloc return in C vs C++Lesson 541The free function signatureLesson 543Contiguous allocation with callocLesson 544Difference between malloc and callocLesson 545Zero-initialization overheadLesson 546Resizing blocks with reallocLesson 549Using realloc as malloc or freeLesson 552Leaking in loops and recursionLesson 555Invalid pointer increments before freeLesson 558Returning addresses of local variablesLesson 561Heap buffer overflowsLesson 563Out-of-bounds array access on heapLesson 565Running a program under ValgrindLesson 566Reading 'definitely lost' reportsLesson 568Finding invalid reads and writesLesson 576The aligned_alloc functionLesson 578Motivation for custom allocatorsLesson 580Managing a static memory poolLesson 591Trade-offs of arena vs mallocLesson 592Introduction to brk and sbrkLesson 593Using mmap for large allocationsLesson 594Anonymous memory mappingsLesson 598Page faults and resident set sizeLesson 620Allocating structs on the heap with mallocLesson 621Freeing dynamically allocated structsLesson 672Flexible array members in C99Lesson 736Finding file size using seek and tellLesson 868Allocating memory with `malloc` and `free`Lesson 869Contiguous allocation with `calloc`Lesson 870Resizing blocks with `realloc`Lesson 982Initial memory allocation with mallocLesson 985Amortized time complexityLesson 986Accessing elements by indexLesson 988Freeing the dynamic arrayLesson 991Allocating a new node in memoryLesson 993Prepending nodes to the headLesson 1046Graph memory managementLesson 1118Passing arguments to threadsLesson 1120Returning values from threadsLesson 1131Cleaning up mutex resourcesLesson 1149Validating function arguments with `NULL` checksLesson 1165Installing and running `valgrind`Lesson 1169Reading the Valgrind leak summaryLesson 1185Profiling memory allocation frequency
malloc.c
If you notice your program is spending 20% of its execution time inside malloc.c, you don't have a logic error—you have an allocation frequency problem.
Lesson 1185Profiling memory allocation frequency
malloc(count * sizeof(type))
Use malloc(count * sizeof(type)) to reserve a block of heap memory, and always check if the resulting pointer is NULL before using it.
Lesson 982Initial memory allocation with malloc
malloc(sizeof(struct Character))
When you call malloc(sizeof(struct Character)), the computer looks at your struct definition, adds up the bytes for the int health and int level, and finds a contiguous block of memory that fits.
Lesson 620Allocating structs on the heap with malloc
mandatory
However, when you are measuring a data type directly (like int or float), the parentheses are mandatory.
Lesson 235The `sizeof` operator with types
Manual memory
Manual memory (The Heap) is like a giant warehouse across town.
Lesson 533Manual vs automatic memory management
manual override
Think of an explicit cast as a manual override.
Lesson 234Safety with explicit casts
Map
Inside that Drawer is a Map that tells you exactly where the Treasure (the actual data) is hidden.
Lesson 500Concept of double indirectionLesson 616The arrow operator `->` syntax
MAP_ANONYMOUS
However, when we use the MAP_ANONYMOUS flag, we tell the OS: "Give me a block of RAM that isn't connected to any file."
Lesson 594Anonymous memory mappings
mask
Think of a mask as a piece of paper with a single hole punched in it.
Lesson 188Common bitwise idioms
Massif
To catch these "chatty" allocation patterns, you can use profiling tools like Valgrind with the Massif tool.
Lesson 1185Profiling memory allocation frequency
master light switch
Think of conditional compilation as a master light switch for your code: you can leave the "debug lamps" plugged in, but turn them all off with one click before shipping.
Lesson 787Managing debug prints with macros
math or logic
If you are using a 8-bit variable for math or logic, be explicit.
Lesson 945The significance of `char` signness
math_functions.c
If half are named MainCode.c and the other half are math_functions.c, your project becomes a messy "junk drawer." Sticking to lowercase names with underscores is a "handshake" between you and other programmers, signaling that you follow standard professional conventions.
Lesson 16Naming conventions for .c files
math_tools.c
If main.c tries to use a function sitting in math_tools.c, it will complain that it doesn't recognize the function name.
Lesson 377Role of the `.h` fileLesson 798Static functions for file scopingLesson 805What is inside a `.o` file
math_tools.o
When you compile this into math_tools.o, the file contains the binary logic for add and calculate.
Lesson 805What is inside a `.o` file
MATH_UTILS_H
The first time the compiler hits this file, MATH_UTILS_H isn't defined, so it enters the file and defines it.
Lesson 380Header Guards: `#ifndef` and `#define`
math_utils.o
Because of $@ and $<, this single rule works for logger.o, math_utils.o, or player.o without you ever having to type those names manually.
Lesson 803From source code to object filesLesson 809Symbol tables and visibilityLesson 822Automatic variables like `$@` and `$<`
math.h
To compare two floats safely, you subtract one from the other, take the absolute value (using fabs() from the math.h library), and check if the result is less than your tiny threshold.
Lesson 166Comparing floating-point numbersLesson 862Exponential and logarithmic functions: `exp`, `log`, `log10`Lesson 864Rounding with `ceil`, `floor`, and `round`Lesson 866Absolute values for floats with `fabs`Lesson 979Mathematical macros using `_Generic`
math.o
However, if main.o tries to call a function located in math.o, the object file just leaves a blank space with a sticky note saying, "I'll need someone to plug in the math logic here later."
Lesson 29Understanding `.o` and `.obj` files
MathFunc
Anyone reading your code immediately understands that op belongs to the MathFunc family, making your programs much easier to maintain.
Lesson 520Defining `typedef` for function pointers
matrix[A][B]
To add an edge in an undirected adjacency matrix, you must set two values—matrix[a][b] and matrix[b][a]—to 1.
Lesson 1040Adjacency Matrix implementationLesson 1043Adding edges in undirected graphs
matrix[b][a]
To add an edge in an undirected adjacency matrix, you must set two values—matrix[a][b] and matrix[b][a]—to 1.
Lesson 1043Adding edges in undirected graphs
matrix[i][j]
This is because every matrix[i][j] must equal matrix[j][i].
Lesson 1043Adding edges in undirected graphs
matrix[j][i]
This is because every matrix[i][j] must equal matrix[j][i].
Lesson 1043Adding edges in undirected graphs
max
By the time the loop finishes, the variables min and max have "survived" every comparison, representing the extremes of the entire group.
Lesson 318Finding Min and Max in a loopLesson 402Finding the maximum value in an array
MAX_PLAYERS
Before the compiler even looks at your logic, the preprocessor literally replaces the text MAX_PLAYERS with the digit 4.
Lesson 115Defining constants with `#define`Lesson 768Defining constants with `#define`
MAX_RECORDS
By always checking your record_count against your MAX_RECORDS, you ensure that your data store remains stable.
Lesson 1216Adding and Deleting records safely
MAX_RECORDS 100
Because C doesn't automatically shrink or grow arrays, we usually define a maximum capacity, like MAX_RECORDS 100.
Lesson 1216Adding and Deleting records safely
MAX_SPEED
By using #undef, you ensure that MAX_SPEED doesn't leak into other parts of a large project where that name might mean something completely different.
Lesson 769Removing definitions with `#undef`
MAX_USER_LIMIT
If you see MAX_USER_LIMIT in all caps, your brain instantly registers: "This is a constant.
Lesson 117Naming conventions for constants
Max-Heap
In a Max-Heap, the highest value is always at the very top—the CEO.
Lesson 1058Heap Sort: Binary heap concept
Maximum Search Time = Tree Height
Therefore, the Maximum Search Time = Tree Height.
Lesson 1030Tree height and balancing concept
maxLimit
In this example, even though currentTemp isn't higher than maxLimit, the >= operator returns true because they are equal.
Lesson 160Greater or equal `>=` and less or equal `<=`
mechanics
In programming, this is called documenting mechanics.
Lesson 35Documenting intent vs mechanics
megaphone
Think of printf like a megaphone: you speak into it, and the output is broadcast immediately for the world to see.
Lesson 703Formatting strings in memory with sprintf
mem
Because mem functions know the exact size of the buffer, the compiler and the CPU can perform "vectorization"—processing 16, 32, or even 64 bytes in a single clock cycle.
Lesson 860Performance differences between `str` and `mem` functions
member
You must tell C which structure you are talking about before you can ask for a member inside it.
Lesson 633Combining array indexing and member access
membership_fee
In the example above, the two membership_fee variables are actually two different people with the same name living in different houses.
Lesson 251Variable scope inside if-else blocks
memcheck
Valgrind is most famous for its memcheck tool.
Lesson 528Tools for pointer debugging (Valgrind)
memchr
Because memchr requires you to provide a specific size, it is safe to use on binary data where a zero byte might just be part of the data rather than the end of the "sentence." It is a robust tool for navigating raw buffers and binary files.
Lesson 859Searching memory bytes with `memchr`
memcmp
Since memcmp checks every single bit, including the junk in the gaps, it may find differences where you didn't expect them.
Lesson 858Comparing memory blocks with `memcmp`Lesson 860Performance differences between `str` and `mem` functions
memmove
Because memmove has to check for overlaps and potentially copy data in reverse, it can be slightly slower than the "dumb" memcpy.
Lesson 857Handling overlapping regions with `memmove`
memory alignment
This requirement is called memory alignment.
Lesson 576The aligned_alloc functionLesson 638Understanding memory alignment
Memory Arena
A Memory Arena (also called a region or zone) is like taking a large cafeteria tray to the buffet.
Lesson 585What is a Memory Arena
memory layout
By limiting the scope to an array of structs, you focus on the most important C concepts: string manipulation (copying names into the keys), looping (searching for a specific key), and memory layout.
Lesson 1212Project scope: A simple Key-Value store
memory mapping
Unnamed bit-fields are essential for memory mapping.
Lesson 650Unnamed bit-fields for padding
memory-mapped I/O
Use volatile whenever you are dealing with memory-mapped I/O, interrupt service routines, or shared global variables in multi-threaded applications where a value might change without the compiler seeing it happen in the current code block.
Lesson 914How `volatile` prevents compiler optimization
Memory-mapped peripheral registers
Memory-mapped peripheral registers (like the example above).
Lesson 913The `volatile` qualifier for hardware mapping
memset
The memset function (found in the <string.h> header) is your "giant squeegee." It is designed to fill a block of memory with a specific byte value as fast as possible.
Lesson 430Setting memory blocks with `memset`Lesson 855Setting memory blocks with `memset`Lesson 859Searching memory bytes with `memchr`
merge
The merge function takes a range within an array, treats it as two halves, and uses a temporary array to build the result.
Lesson 1054Merge Sort: The Merge function
Merge Sort
Some algorithms, like Merge Sort, follow a "divide and conquer" approach that requires creating temporary arrays to hold data while comparing it.
Lesson 1051In-place sorting vs extra memory
Message
By subtracting the start of the string (message) from the pointer returned by the function, we can calculate the exact index where the character lives.
Lesson 671Anonymous unions inside structsLesson 851Searching for characters with `strchr` and `strrchr`
message[0] = 'B'
If you try to do something like message[0] = 'B'; to turn "Hello" into "Bello," your program will likely crash (a "Segmentation Fault").
Lesson 479String literals as `char` pointers
message[0] = 'J'
In C, if you try to modify a string literal—for example, by writing message[0] = 'J';—your program will likely crash with a "Segmentation Fault." The compiler won't always warn you about this danger because char * looks like any other pointer that is allowed to change data.
Lesson 485Using `const char *` for safety
mid
We then tell the function to handle the left half (from low to mid) and the right half (from mid + 1 to high).
Lesson 1055Merge Sort: Recursive splittingLesson 1062Binary Search: Iterative approach
mid + 1
We then tell the function to handle the left half (from low to mid) and the right half (from mid + 1 to high).
Lesson 1055Merge Sort: Recursive splitting
might result in
If C didn't do this, adding 0.5 to 1 might result in 1 instead of 1.5 because the computer might try to force everything into a whole-number format.
Lesson 126The 'Usual Arithmetic Conversions'
min
However, if you are looking for the minimum value in a list of positive test scores and you start your min at 0, no score will ever be smaller than 0.
Lesson 318Finding Min and Max in a loop
MinGW
On Windows, the most popular free "translator" for this job is MinGW (Minimalist GNU for Windows).
Lesson 11Setting up MinGW on Windows
Minimum Viable Product (MVP)
Instead, we are going to focus on a Minimum Viable Product (MVP).
Lesson 1204Project scope: A custom `grep` clone
misaligned access
In the world of C, this "stitching together" is called a misaligned access.
Lesson 526Misaligned pointer access
missing_document.txt
If missing_document.txt is not there, your console will display:
Lesson 741Using perror for descriptive errors
mkdir
When you type ls or mkdir, the shell doesn't become that command; it forks a child.
Lesson 1083Combining fork() and exec()
mkfifo()
mkfifo() creates a special file on the system that acts as a permanent gateway for unrelated processes to exchange data streams.
Lesson 1106Introduction to named pipes (FIFOs)Lesson 1107Creating FIFOs with mkfifo()
mkstemp
In modern, high-security professional software, developers often use more advanced functions like mkstemp.
Lesson 759Generating temp filenames with tmpnam
mkstemp()
By using mkstemp(), you ensure that if a file with that random name already exists, the system will try a different random name until it finds a truly unique one, leaving no room for a hacker to intercept your data.
Lesson 763Temporary file security risks
mktemp()
Older C functions like tmpnam() or mktemp() are dangerous because they only suggest a filename.
Lesson 763Temporary file security risks
mktime
Notice that mktime modifies the original my_date variable to fill in the day of the week, saving you from doing the complex calendar math yourself.
Lesson 895Converting `struct tm` back to `time_t` with `mktime`
mmap
If you pass an incorrect length or a pointer that wasn't returned by mmap, the operating system will likely throw an error, as you are trying to return "books" that don't belong to you.
Lesson 590Growing an arena with virtual memoryLesson 593Using mmap for large allocationsLesson 594Anonymous memory mappingsLesson 596The munmap function
mmap()
When you ask the operating system for a specific chunk of memory using system calls like mmap(), you don't just get a blank slate; you get to decide what the CPU is allowed to do with that space.
Lesson 594Anonymous memory mappingsLesson 595Memory protection constants (PROT_READ)
mock
A mock is a "stunt double." It has the exact same name and signature as the real function, but instead of doing hard work, it returns a hard-coded value that you control.
Lesson 1176Mocking simple dependencies
mock_sensor.c
When you compile your test, you simply link logic.c and test_main.c with your mock_sensor.c instead of the real hardware driver.
Lesson 1176Mocking simple dependencies
mode
By using unnamed bit-fields, you ensure that mode starts at exactly the 6th bit without cluttering your code with "dummy" variables like int reserved1; or int ignoreMe;.
Lesson 650Unnamed bit-fields for padding
modular building
The -c flag allows for modular building: you prepare the pieces individually so that the final assembly is fast and organized.
Lesson 804The `-c` flag for compilation
Modular Compilation
In C, object files allow for Modular Compilation:
Lesson 29Understanding `.o` and `.obj` files
modulo operator
To capture that "leftover" value, we use the modulo operator, represented by the percent sign %.
Lesson 155The modulo operator `%` with integers
modulo operator (%)
However, because rand() gives you such a wide range of numbers, you will almost always use the modulo operator (%) to "squish" that number into a range that is useful for your program.
Lesson 883Generating pseudo-random numbers with `rand`Lesson 887Scaling `rand` results to a specific rangeLesson 1018Circular array implementationLesson 1032A simple modular hash function
MONDAY
You can add Monday to Red, or assign the number 402 to a variable that is supposed to only hold True or False.
Lesson 662Default integer values in enumsLesson 667Type safety concerns with enums
money
Portability: If you define Money as a float and later realize you need more precision, you only have to change one line of code (the typedef) to turn every Money variable into a double.
Lesson 343Why changing a parameter doesn't affect the callerLesson 608Using typedef with primitive types
month is zero-indexed
First, the month is zero-indexed, so December is 11.
Lesson 891Breaking down time with `struct tm`
MOT_
If you are writing a module for managing a motor, every public function and global variable should start with MOT_ or motor_.
Lesson 801Naming conventions for large projects
motor_
If you are writing a module for managing a motor, every public function and global variable should start with MOT_ or motor_.
Lesson 801Naming conventions for large projects
mov
In C, a command like mov might exist on both platforms, but the order of the inputs and how the CPU handles them is completely different.
Lesson 26Phase 2: Compilation to AssemblyLesson 27Phase 3: Assembly to Object CodeLesson 957The `asm` keyword syntaxLesson 963Platform-specific assembly (x86 vs ARM)
msg.data.text
Normally, nesting a union requires giving it a name, leading to clunky access like msg.data.text.
Lesson 671Anonymous unions inside structs
MSYS2
The simplest way to get MinGW is through a project called MSYS2, which manages the installation for you.
Lesson 11Setting up MinGW on Windows
mtx_init
C11 provides thrd_create to start a task and mtx_init for "mutexes" (locks), which ensure two chefs don't try to use the same knife at the exact same time.
Lesson 967C11: Multi-threading and Anonymous structures
multiple definition
If two different source files (.c files) both claim to "own" the same global variable, the linker gets confused and quits with a multiple definition error.
Lesson 925Common linkage errors and 'multiple definition'
multiply_and_print
Instead of writing separate functions for add_and_print and multiply_and_print, we write one compute function that accepts a "math rule" as an argument.
Lesson 518Passing functions as arguments
munmap
The munmap function releases a specific range of memory pages back to the operating system, requiring both the starting address and the exact size of the region.
Lesson 596The munmap function
must be sorted
However, there is a catch: to use the O(log n) approach, your data must be sorted beforehand.
Lesson 1065Time complexity: O(n) vs O(log n)
mutex_a
If Thread 1 has already grabbed mutex_a, Thread 2 will simply wait at the very first line of its code until Thread 1 is completely finished with both locks.
Lesson 1139Common synchronization pitfalls
mutex_b
If both Thread 1 and Thread 2 are programmed to always lock mutex_a before mutex_b, the deadlock vanishes.
Lesson 1139Common synchronization pitfalls
Mutexes
To solve this in C, we use two tools: Mutexes (to ensure only one thread touches the rack at a time) and Semaphores (to track how many spots are full or empty).
Lesson 1124Understanding race conditionsLesson 1136The producer-consumer problem
My First Program.c
Bad: My First Program.c (Spaces can break command-line tools).
Lesson 16Naming conventions for .c files
my_age
The sequence point ensures that by the time display_age starts, my_age has officially become 26 in the computer's memory.
Lesson 226Function call sequence points
my_age++
In this example, the expression my_age++ has a value (25) and a side effect (changing the variable to 26).
Lesson 226Function call sequence points
my_area
Instead, it directly modifies my_area and my_perimeter back in the main function.
Lesson 489Returning multiple values via pointers
my_array[3]
When you write my_array[3], you are using "array subscript notation." It’s comfortable and easy to read.
Lesson 473The equivalence of `a[i]` and `*(a + i)`
my_buffer
When you use gets(my_buffer), you are telling C to take whatever the user types and shove it into my_buffer.
Lesson 432Why `gets` is dangerous and deprecated
my_date
Notice that mktime modifies the original my_date variable to fill in the day of the week, saving you from doing the complex calendar math yourself.
Lesson 895Converting `struct tm` back to `time_t` with `mktime`
my_float
This often fails because the compiler doesn't expect ptr_as_int to affect my_float.
Lesson 527Pointer type-punning dangers
my_function()
Creation: When my_function() starts, a block of memory is reserved on the stack.
Lesson 530Stack frame lifecycle and local variables
my_label
The Colon: The label destination ends with a colon (my_label:), but the goto command ends with a semicolon (goto my_label;).
Lesson 307The syntax of the goto statement
my_logger.log_message()
Your main program doesn't have to change; it simply calls my_logger.log_message(), trusting that the correct behavior has been "plugged in."
Lesson 675Implementing an interface with function pointers in structs
my_perimeter
Instead, it directly modifies my_area and my_perimeter back in the main function.
Lesson 489Returning multiple values via pointers
my_program
If you were to peak inside the resulting my_program file with a tool like readelf, you would see the OS-level headers that turn your simple C logic into a formal, runnable application.
Lesson 30Creating an executable binaryLesson 57Basic `gcc` command flagsLesson 59Compiling multiple source filesLesson 806Linking multiple object filesLesson 808The executable ELF formatLesson 812Linking with static libraries
my_project.supp
You can copy that block into a file named my_project.supp.
Lesson 570Suppressing known tool warnings
my_settings.h
When it sees #include "my_settings.h", it pauses, opens your file named my_settings.h, copies everything inside it, and pastes it directly into your main file at that exact spot before the compiler even starts reading the code.
Lesson 766The `#include` directive for local files
my_variable
Because standard C variables usually start with lowercase letters (my_variable), a capitalized name signals that you are looking at a custom-defined type.
Lesson 75The syntax of a declarationLesson 612Naming conventions for typedef types
myAccount
In this example, Account_t is the blueprint, and myAccount is the actual data.
Lesson 612Naming conventions for typedef types
myApp
.size: Look inside myApp for a member named size.
Lesson 624Accessing members of nested structs
myApp.size.width
When you see myApp.size.width, read it from left to right as a map:
Lesson 624Accessing members of nested structs
myBook.pages = 300
Up until now, you might have filled these compartments one by one using the "dot" operator: myBook.pages = 300;.
Lesson 602Initializing structs with brace notation
myCar
For example, if you have myCar.speed, you are looking directly at the "speed" property of the myCar object.
Lesson 616The arrow operator `->` syntax
myCar.speed
For example, if you have myCar.speed, you are looking directly at the "speed" property of the myCar object.
Lesson 616The arrow operator `->` syntax
myData.decimal
When you write to myData.decimal, the computer writes 32 bits to memory in a format called IEEE 754 (the standard for decimals).
Lesson 659The danger of reading the wrong union member
myData.integer
When you ask for myData.integer, the computer looks at those exact same 32 bits but interprets them using two's complement (the standard for integers).
Lesson 659The danger of reading the wrong union member
myGrid
In the example above, myGrid acts like a table.
Lesson 407Declaring 2D arrays: Rows and Columns
myHealth
In the code above, the variable names myLevel and myHealth don't actually matter to the displayStats function.
Lesson 337Positional matching of arguments
myInt
Just show me the bits." The myInt member gives us the integer 1073741824, which is the decimal version of those raw bits.
Lesson 657Using unions for type punning
MyLabel
They are case-sensitive (MyLabel is different from mylabel).
Lesson 306Defining labels in C code
myLevel
In the code above, the variable names myLevel and myHealth don't actually matter to the displayStats function.
Lesson 337Positional matching of arguments
myLuckyNumber
In the example above, myLuckyNumber is our variable.
Lesson 73What is a variable?
myNumber
It looks at myNumber, sees the value 10, and scribbles 10 into that new memory spot.
Lesson 341Understanding 'Pass by Value'
myPtr
Your pointer myPtr simply holds the address of that distant neighborhood.
Lesson 484Memory layout of string pointers
mySavings
Once the function finishes, the money variable is destroyed, and mySavings remains exactly as it was.
Lesson 343Why changing a parameter doesn't affect the caller
myScore
When addScore(myScore) is called, C looks at the value inside myScore (which is 50) and copies that number into a brand new memory bucket named localScore.
Lesson 346Preparing for pass by referenceLesson 486Pass-by-value limitations
mytool
The make utility looks for the file, checks if main.c or utils.c are newer than the existing mytool executable, and runs the compiler only if needed.
Lesson 1211Writing the Makefile for the project
myValues
In the example below, notice how we pass myValues to the function.
Lesson 474Passing arrays to functions as pointers
myVariable
No spaces: Use "camelCase" (like myVariable) or "snake_case" (like my_variable).
Lesson 75The syntax of a declaration

N

n - i - 1
You might notice the inner loop runs until n - i - 1.
Lesson 445Sorting an array using Bubble Sort
n <= 0
Once the base case (n <= 0) is reached, no new frames are added.
Lesson 359The call stack in recursion
n=3
When count(3) is called, a frame for n=3 is pushed onto the stack.
Lesson 359The call stack in recursion
name
To declare an array in C, you need three specific pieces of information: the type of data you are storing, a name for the array, and the size (how many slots you need) inside square brackets.
Lesson 239Member access `.` and `->`Lesson 383Declaring an array with `type name[size]`Lesson 436Using `strncat` for safer concatenationLesson 630Declaring an array of structsLesson 879Sorting structs by multiple fieldsLesson 938Accessing out-of-bounds memory
NAME = value
To create one, use the equals sign: NAME = value.
Lesson 821Using variables in Makefiles
name[5] = '\0'
By setting name[5] = '\0', you chop off the newline, leaving you with a clean "Alice".
Lesson 702Removing the newline from fgets
Named Pipe
A FIFO (First-In, First-Out) is often called a Named Pipe.
Lesson 1107Creating FIFOs with mkfifo()
NAN
A NAN is like a contagious virus: any math operation involving a NAN will result in another NAN.
Lesson 867Handling `NAN` and `INFINITY` constants
NASA_ROVER_
Be Unique: Always prefix guards with your project name (e.g., NASA_ROVER_).
Lesson 1203Header guard best practices
NDEBUG
Instead of manually deleting every assert() line from your source code—which would be a nightmare to put back later if you found a bug—the C Standard Library provides a "kill switch" called NDEBUG (which stands for "No Debug").
Lesson 901Using `assert` for internal debuggingLesson 902Disabling assertions with `NDEBUG`
needle
You have a large field (the haystack) and you are waving your detector to find a specific metal object (the needle).
Lesson 852Finding substrings with `strstr`
negative
A negative number if the first item comes before the second.
Lesson 1059Using C library 'qsort' function
nested loop
We use a nested loop: an outer loop that handles the rows and an inner loop that handles the individual columns within those rows.
Lesson 412Printing a 2D matrix to the console
NET_
Use unique, module-specific prefixes (like UI_ or NET_) for all public functions and global variables to prevent naming conflicts in large projects.
Lesson 801Naming conventions for large projects
Network
But as your project grows into dozens of .c files, you might have one file handling Network connections and another handling User profiles.
Lesson 801Naming conventions for large projects
Network Byte Order
To fix this, the internet uses a standard "postal code" rule: all data traveling over a network must be in Big-Endian format, also known as Network Byte Order.
Lesson 952Network byte order and `htons`/`ntohs`
Never trust the user
The golden rule of command-line utilities is: Never trust the user. You must check argc before touching anything in argv.
Lesson 1205Handling `argc` and `argv` robustly
new
To avoid these traps, always set the pointers on your new node first.
Lesson 1005Common pointer update pitfalls
new_node
When you insert a new node (new_node) between two existing nodes (prev_node and next_node), you must update four pointers to maintain the integrity of the chain.
Lesson 1002Inserting in a doubly linked list
new_node->next
If you update prev_node->next before you've saved its value into new_node->next, you lose your connection to the rest of the list—it’s like dropping the hand of the person behind you before the new person has grabbed it.
Lesson 1002Inserting in a doubly linked list
new_node->prev
New Node's Prev: Point new_node->prev to the prev_node.
Lesson 1002Inserting in a doubly linked list
newfd
In plain English, this says: "Make newfd a copy of oldfd." If newfd was already open, the system closes it first to make room for the new connection.
Lesson 1095Redirecting output with dup2()
newline character
In C, we use a special symbol called the newline character, written as \n.
Lesson 44Newline character `\n`
newNode->next = NULL
By setting newNode->next = NULL, you are essentially saying, "This is a valid node, but it isn't connected to a chain yet." Without this step, the next pointer would contain "garbage" data—a random memory address that could cause your program to crash if you tried to follow it.
Lesson 991Allocating a new node in memory
next available slot
Instead, you are telling the pointer to move to the next available slot of that specific data type.
Lesson 465How data types affect step size
next_free_byte
It is best used for "scratchpad" memory where you fill the pool during a task and reset the next_free_byte to zero when the task is finished.
Lesson 580Managing a static memory pool
next_node
When you insert a new node (new_node) between two existing nodes (prev_node and next_node), you must update four pointers to maintain the integrity of the chain.
Lesson 1002Inserting in a doubly linked list
next_node->prev
Backward Link: Point next_node->prev to the new_node.
Lesson 1002Inserting in a doubly linked list
nextNode = current->next
If we called free(current) before assigning nextNode = current->next, we would be attempting to access current->next from memory that we no longer own.
Lesson 997Memory cleanup for linked lists
no equals sign
Notice that there is no equals sign and no semicolon at the end.
Lesson 768Defining constants with `#define`
no semicolon
Notice that there is no equals sign and no semicolon at the end.
Lesson 768Defining constants with `#define`
no type
If there is no type (just *p in an equation), you are traveling through the pointer to the value it points to.
Lesson 456The difference between `int *p` and `*p`
Node B
In programming terms, if you are at Node C and need to access Node B, a singly linked list leaves you stranded.
Lesson 998The 'prev' pointer concept
Node buckets
By using Node buckets, we are creating an array of "Head Pointers." Each index in our array acts as the start of a linked list.
Lesson 1034Implementing the bucket array
Node C
In programming terms, if you are at Node C and need to access Node B, a singly linked list leaves you stranded.
Lesson 998The 'prev' pointer concept
node->data
Indirectly lost: The memory for the int array (node->data).
Lesson 567Identifying 'indirectly lost' memory
non-portable
While bit-fields are efficient, they come with a major warning: they are highly non-portable.
Lesson 954Bit-fields in structures and portability
nondeterministic
Race conditions are the most hated bugs in systems programming because they are nondeterministic.
Lesson 1124Understanding race conditions
nop
During the optimization phase (like -O2 or -O3), it might decide the nop is dead code and remove it entirely.
Lesson 958The basic `volatile` asm block
normalCount
In the code above, normalCount is born and dies with every function call.
Lesson 144Static local variables
notebook
A static variable is like a notebook kept in that same room; even if everyone leaves and comes back tomorrow, the notes from the last meeting are still there.
Lesson 920The `static` keyword inside functions
nput/
The name stands for Standard Input/Output, and the .h indicates it is a "header" file.
Lesson 18The `#include` directiveLesson 678The header file <stdio.h>
ntohl()
While many systems provide built-in functions like ntohl() for networking, writing your own swap logic ensures your code is portable across different compilers and platforms.
Lesson 953Manual byte swapping techniques
ntohs
Always use htons when sending data and ntohs when receiving it to ensure your numbers remain consistent across different types of hardware.
Lesson 952Network byte order and `htons`/`ntohs`
ntohs()
network to host short (converts it back so your programs can read it).
Lesson 952Network byte order and `htons`/`ntohs`
NULL
A NULL pointer is a special "grounding" value that tells the computer, "This pointer intentionally points to nothing." If you accidentally try to use a NULL pointer, the program will crash instantly and predictably, which is much easier to fix than a wild pointer causing silent damage.
Lesson 427Searching for characters with `strchr`Lesson 428Searching for substrings with `strstr`Lesson 429Tokenizing strings with `strtok`Lesson 459Initializing pointers to NULLLesson 462Checking for NULL before dereferencingLesson 521Uninitialized 'wild' pointersLesson 522Dangling pointers after `free`Lesson 525Dereferencing the NULL pointerLesson 539Checking for NULL return valuesLesson 542Why freeing NULL is safeLesson 546Resizing blocks with reallocLesson 547Handling realloc failure safelyLesson 549Using realloc as malloc or freeLesson 554Double-freeing a pointerLesson 557What is a dangling pointerLesson 559Setting pointers to NULL after freeLesson 562Use-after-free vulnerabilitiesLesson 602Initializing structs with brace notationLesson 603Designated initializers in C99Lesson 622The importance of NULL checks for struct pointersLesson 626Self-referential structs for linked listsLesson 707Checking for NULL return in fgetsLesson 709Opening files with fopenLesson 710Understanding file modes: r, w, aLesson 712Checking for NULL file pointersLesson 714Handling file not found errorsLesson 715The maximum number of open filesLesson 740Introduction to errnoLesson 741Using perror for descriptive errorsLesson 745Handling 'Permission Denied' errorsLesson 747Safe file closing patternsLesson 753The setbuf shorthandLesson 762Checking if a file existsLesson 851Searching for characters with `strchr` and `strrchr`Lesson 852Finding substrings with `strstr`Lesson 853Tokenizing strings with `strtok`Lesson 859Searching memory bytes with `memchr`Lesson 870Resizing blocks with `realloc`Lesson 880Using `bsearch` on sorted arraysLesson 881Handling the `void*` return of `bsearch`Lesson 889Getting system time with `time_t`Lesson 897The global `errno` variableLesson 898Interpreting errors with `perror`Lesson 901Using `assert` for internal debuggingLesson 903When to use `errno` vs return codesLesson 970C23: The `nullptr` constantLesson 982Initial memory allocation with mallocLesson 988Freeing the dynamic arrayLesson 990Creating the head pointerLesson 992Traversing the list with a while loop
null statement
However, when you accidentally place one immediately after a while condition, you create what programmers call a null statement.
Lesson 272Common error: semicolon after while header
null terminator
However, there is a catch: C strings must always end with a special hidden character called the null terminator, written as \0.
Lesson 416Defining strings with double quotesLesson 417The Null Terminator `\0` characterLesson 418Difference between `'a'` and `"a"`Lesson 419Initializing strings with sizes
nullptr
By using nullptr, you tell the compiler, "I am definitely dealing with a memory address here." This helps modern static analysis tools catch errors faster and ensures your code is ready for the future of the language.
Lesson 970C23: The `nullptr` constant
nullptr_t
Unlike NULL, which is a macro that expands to an integer, nullptr is a reserved keyword with its own special type: nullptr_t.
Lesson 970C23: The `nullptr` constant
num
If it reaches the limit (num) without finding it, it returns NULL.
Lesson 339Local scope of parametersLesson 859Searching memory bytes with `memchr`
number_of_tests
Because one side of the division is now a double, C automatically promotes number_of_tests to a double as well, resulting in a precise calculation.
Lesson 230The `(type)` cast operator
numbers = &some_other_int
If you try to do numbers = &some_other_int;, the compiler will throw an error.
Lesson 471Array names as constant pointers
numbers[0]
In C, if you use the name numbers without any square brackets, the compiler treats it as the memory address of numbers[0].
Lesson 472Accessing arrays with pointer notationLesson 476Pointer to the start of an array
numbers[1]
numbers[1] is the same as *(numbers + 1)
Lesson 472Accessing arrays with pointer notation
numbers[2]
When you write numbers[2], the compiler is actually performing pointer arithmetic under the hood: it takes the starting address and adds two "steps" of the integer size to find the value.
Lesson 470Navigating memory blocks manuallyLesson 472Accessing arrays with pointer notation
numbers[i]
While using numbers[i] is often more readable, incrementing a pointer is a fundamental pattern in low-level programming.
Lesson 477Iterating arrays using pointer increments

O

O_CLOEXEC
You can set this flag when you first open the file using the O_CLOEXEC flag, or later using fcntl.
Lesson 1096The close-on-exec flag
O_NONBLOCK
To do this, we use the fcntl() (file control) function to add the O_NONBLOCK flag to a file descriptor.
Lesson 1098Non-blocking I/O basics
O(log n)
When searching for a specific value in a collection, the two most common speeds you will encounter are O(n) and O(log n).
Lesson 1027Searching for a value in BSTLesson 1065Time complexity: O(n) vs O(log n)
O(n)
When searching for a specific value in a collection, the two most common speeds you will encounter are O(n) and O(log n).
Lesson 1065Time complexity: O(n) vs O(log n)
O(n²)
In computer science, we call this O(n²) complexity (pronounced "Big O of n-squared").
Lesson 1050Time complexity of O(n^2) sorts
Object Code
During this step, the compiler takes your source code and translates it into Object Code.
Lesson 803From source code to object files
of
Think of the dot as the word "of." When you write hero.level, you are telling C: "Look at the level of hero."
Lesson 350Return addresses in memoryLesson 601The dot operator for member access
OFF
Computers, however, are made of tiny electronic switches that can only be in two states: ON (1) or OFF (0).
Lesson 167Logical NOT `!`Lesson 179Understanding binary representation
Off-By-One
The most frequent cause of an invalid read is the Off-By-One error.
Lesson 1166Identifying 'Invalid Read' errors
offset
Virtual memory is divided into fixed-size pages (usually 4KB), and every address identifies a specific page number and a byte offset within that page.
Lesson 587Resetting an arena in one stepLesson 588Arena allocation for frame-based tasksLesson 597Virtual memory pages and offsets
OK
Because C lacks true namespacing for enums, the names RED, YELLOW, and OK become global constants.
Lesson 666Scoped enum limitations in C
oldfd
In plain English, this says: "Make newfd a copy of oldfd." If newfd was already open, the system closes it first to make room for the new connection.
Lesson 1095Redirecting output with dup2()
ON
Computers, however, are made of tiny electronic switches that can only be in two states: ON (1) or OFF (0).
Lesson 167Logical NOT `!`Lesson 179Understanding binary representation
on a highlighted function
By pressing a on a highlighted function, perf will show you your C code side-by-side with the assembly instructions.
Lesson 1186Using `perf` for hardware-level insights
one
They group multiple statements together into a single "block." However, C allows a shorthand that often trips up beginners: if a control structure (like an if statement or a for loop) only needs to execute exactly one statement, the braces are optional.
Lesson 250Curly brace requirements for single vs multi-lineLesson 653Defining a union with the union keywordLesson 660Initializing a unionLesson 799The role of the 'main' fileLesson 1134Signaling with pthread_cond_signalLesson 1135Broadcasting to all threads
one apple
We take the weight of the whole bag and divide it by the weight of one apple.
Lesson 388Calculating array size with `sizeof`
one byte
Each cubby is exactly the same size: one byte (8 bits).
Lesson 447Memory as a linear sequence of bytes
one data type
Instead, you are telling the computer to move forward by one data type.
Lesson 468Scaling factor in pointer math
One free() for every malloc()
The golden rule is: One free() for every malloc(). Think of them as a pair of brackets that must always be closed.
Lesson 553The 'Free after use' rule
one whole data type
Instead, you are telling the pointer to move forward by one whole data type.
Lesson 464Adding integers to pointers
one-quarter
A common industry standard is the Quarter-Rule: only shrink the capacity by half when the size drops to one-quarter of the capacity.
Lesson 987Popping and shrinking logic
only if both compared bits are
The bitwise & operator returns a 1 only if both compared bits are 1, making it perfect for extracting specific bits from a value.
Lesson 175Bitwise AND `&`
only results in a
Because & only results in a 1 if both sides are 1, using our mask will zero out everything except the bit we care about.
Lesson 188Common bitwise idioms
ontiguous
calloc (short for contiguous allocation) is the more polite version of this request.
Lesson 543Contiguous allocation with calloc
OOM (Out of Memory) Killer
When a process "hog" consumes all memory, the operating system may trigger an OOM (Out of Memory) Killer.
Lesson 556Impact of leaks on long-running processes
op
Anyone reading your code immediately understands that op belongs to the MathFunc family, making your programs much easier to maintain.
Lesson 520Defining `typedef` for function pointers
Opaque Types
In C, we achieve this "sealed case" using Opaque Types.
Lesson 673Opaque types with header files
open()
If you open a FIFO to write, the open() function will pause your program until another program opens that same file to read.
Lesson 1092File descriptors vs FILE pointersLesson 1106Introduction to named pipes (FIFOs)
operands
In programming terminology, we call the numbers being added operands.
Lesson 150The addition operator `+`
operate
By defining a standard set of function pointers, you create an Interface: a contract that says "any struct of this type will have a function named operate, but how it operates is up to the specific instance."
Lesson 675Implementing an interface with function pointers in structs
operation compares the
In the example above, the & operation compares the input and mask bit-by-bit.
Lesson 180Masking bits with `&`
operator (like
While it is tempting to use the - operator (like end - start), the C standard doesn't actually mandate that time_t must be a simple count of seconds.
Lesson 890Measuring intervals with `difftime`
operator flips a true value to
The ! operator flips a true value to 0 and any false value (0) to 1.
Lesson 167Logical NOT `!`
operator flips truth to
The ! operator flips truth to 0 and falsehood to 1, while !! scales any non-zero value down to a clean 1.
Lesson 174Operator `!` and boolean normalization
operator on a
If you try to use the operator on a void , the compiler will throw an error because it doesn't know where the data ends or how to translate the binary 1s and 0s.
Lesson 508Why you can't dereference `void *`
Operator Precedence
Instead, C follows the rules of Operator Precedence.
Lesson 216Precedence of `*` over `+`
Operator Precedence Table
C follows a similar, but much larger, set of rules called the Operator Precedence Table.
Lesson 212Operator precedence table
operator returns a
The bitwise & operator returns a 1 only if both compared bits are 1, making it perfect for extracting specific bits from a value.
Lesson 175Bitwise AND `&`
operator with
Use the & operator with scanf to pass a variable's memory address, allowing the function to reach into your computer's storage and update the value directly.
Lesson 692How scanf uses memory addresses
Optimization Levels
In C, we give this permission using Optimization Levels.
Lesson 1188Compiler optimization levels (`-O1`, `-O2`, `-O3`)
or for working with
For this reason, memset is almost exclusively used for setting memory to 0 or for working with char arrays (strings).
Lesson 855Setting memory blocks with `memset`
ORANGE
If you add ORANGE in the middle later, C re-numbers the rest automatically.
Lesson 664Enums vs constant integers
oranges
One bucket represents the variable apples and the other represents oranges.
Lesson 150The addition operator `+`
order
Think of it like a shipping kit: you have a large Order box, and inside it, there is a smaller Address box.
Lesson 55Argument-specifier matchingLesson 625Initializing nested structures
order matters
The key rule to remember is order matters.
Lesson 602Initializing structs with brace notation
Order of Operations
It doesn't necessarily do the heavy lifting itself, but it dictates the order of operations.
Lesson 54Multiple specifiers in one lineLesson 773Why parenthesize macro argumentsLesson 799The role of the 'main' file
original
When you use x-- in a line of code, C uses the original value of x for whatever calculation is happening right now, and only after that calculation is finished does it subtract one from the variable.
Lesson 201Postfix decrement `x--`Lesson 607The syntax of typedef
Otherwise
To make a complete system, you need a backup plan: "Otherwise, turn on the fan."
Lesson 245The else clause for alternative paths
outer loop
Nested loops work the same way: an inner loop completes its entire run for every single step of the outer loop.
Lesson 299Inner loop vs outer loop execution orderLesson 300Using nested loops to print 2D gridsLesson 301Nested loops for multiplication tables
output
The compiler looks at your constraints, picks two registers, moves the value of input into the second register, runs your assembly, and then moves the result from the first register back into the output variable.
Lesson 58Naming the output with `-o`Lesson 916Optimization benefits of `restrict`Lesson 959Input and Output operands in assembly
output[0]
If I write to output[0], I might be changing the value of input[1]!" To be safe, it would reload input[i] from the slow main memory every single time.
Lesson 916Optimization benefits of `restrict`
outside
When you declare an array outside of any function (a global array), C acts like a diligent hotel maid.
Lesson 395Initialization of local vs global arraysLesson 412Printing a 2D matrix to the console
overflow
In programming, this "mess" is called data loss or overflow.
Lesson 132Safe downcasting techniques
overhead
In programming, this commute is called overhead.
Lesson 365The overhead of function calls
overwrite
If you then write a completely different program called goodbye.c and compile that without specifying a name, the compiler will create a new a.out and overwrite the old one.
Lesson 60Understanding the `a.out` default

P

p1
When you write struct Player p1; inside a function, that memory is carved out of the "stack." The moment that function finishes, p1 is destroyed.
Lesson 467Pointer comparison with `==` and `<`Lesson 498The 'Clockwise/Spiral' rule for declarationsLesson 609Creating a shorthand for struct namesLesson 620Allocating structs on the heap with malloc
p1 < p2
If pointer p1 points to the third house and p2 points to the fifth, p1 < p2 will be true because p1 points to an earlier memory location.
Lesson 467Pointer comparison with `==` and `<`
p2
If pointer p1 points to the third house and p2 points to the fifth, p1 < p2 will be true because p1 points to an earlier memory location.
Lesson 467Pointer comparison with `==` and `<`Lesson 498The 'Clockwise/Spiral' rule for declarations
pacman -S mingw-w64-ucrt-x86_64-gcc
Type this command: pacman -S mingw-w64-ucrt-x86_64-gcc and hit Enter.
Lesson 11Setting up MinGW on Windows
padding
However, if the members have different alignment requirements, the compiler will insert invisible, empty bytes called padding to ensure every piece of data sits at a "natural" boundary.
Lesson 575Manual padding in structuresLesson 638Understanding memory alignmentLesson 947Structure padding and alignment issues
page fault
A page fault occurs when your program tries to access an address that is in your virtual map but isn't currently sitting in physical RAM.
Lesson 598Page faults and resident set size
Page Number
Virtual memory is divided into fixed-size pages (usually 4KB), and every address identifies a specific page number and a byte offset within that page.
Lesson 597Virtual memory pages and offsets
Pages
Virtual memory is divided into fixed-size pages (usually 4KB), and every address identifies a specific page number and a byte offset within that page.
Lesson 597Virtual memory pages and offsetsLesson 598Page faults and resident set size
Pantry
If you need "Salt" (a standard ingredient), you don't look in your own pockets; you go straight to the Pantry (the System Path).
Lesson 795Standard header search paths
paper
When you write paper, the computer performs two distinct moves:
Lesson 505Accessing data through double dereferenceLesson 1128Avoiding deadlocks
Parameter
Think of the Parameter as a parking spot and the Argument as the car.
Lesson 335Parameters vs. Arguments
Parameters
In the world of C, these labels are your Parameters.
Lesson 335Parameters vs. Arguments
parent
This new process (the child) gets a copy of the original process's (the parent) variables, file descriptors, and code.
Lesson 1075Process duplication and copy-on-write
Parent Process
This "creator" is known as the Parent Process, and its identification number is the PPID (Parent Process ID).
Lesson 1069Parent processes and getppid()
parent_id
If you run this program in a standard terminal, parent_id will typically be the PID of your shell (like bash or zsh).
Lesson 1069Parent processes and getppid()
parent.child.member
Nest structures to group related data hierarchically, using multiple dots (e.g., parent.child.member) to access the inner values.
Lesson 623Defining a struct inside another struct
Pareto Principle
Performance profiling usually reveals the Pareto Principle: roughly 80% of your program's execution time is spent in only 20% of your code.
Lesson 1183Identifying 'Hot Spots' in your code
partitioning
Quick Sort is a divide-and-conquer algorithm, but all the heavy lifting happens in a step called partitioning.
Lesson 1056Quick Sort: Partitioning logic
PascalCase
Use a consistent naming style like PascalCase or a _t suffix for typedef names to clearly separate type definitions from variable names.
Lesson 612Naming conventions for typedef types
Pass by Value
In C, this behavior is called Pass by Value.
Lesson 343Why changing a parameter doesn't affect the caller
Pass-by-value
This is exactly how Pass-by-value works in C.
Lesson 486Pass-by-value limitations
password_protected
It might overwrite the password_protected variable, changing 'N' to something else and accidentally "unlocking" the program.
Lesson 1152Why `gets()` is strictly forbidden
Path
Click Environment Variables, find the Path variable under "System variables," and click Edit.
Lesson 11Setting up MinGW on WindowsLesson 632Indexing into a struct array
path.x[2]
Remember: path.x[2] would imply that the variable x is an array.
Lesson 632Indexing into a struct array
path[0].x = 0; path[0].y = 0
If you have a large array, this method is significantly cleaner than writing path[0].x = 0; path[0].y = 0; over and over.
Lesson 631Initializing arrays of structs
path[i]
When you write path[i].x, C evaluates path[i] first, which results in a single struct Point.
Lesson 632Indexing into a struct array
path[i].x
When you write path[i].x, C evaluates path[i] first, which results in a single struct Point.
Lesson 632Indexing into a struct array
pattern rules
The power of these variables is most obvious when you use pattern rules.
Lesson 822Automatic variables like `$@` and `$<`
Pause
The moment GDB attaches, it sends a signal to the program to pause.
Lesson 831Continuing execution with `continue`Lesson 838Attaching GDB to a running process
PAUSED
At any given moment, the microwave is doing exactly one thing: it’s either IDLE, HEATING, or PAUSED.
Lesson 668Using enums for state machines
Peek
The peek function returns the value of the top element without modifying the stack's pointer or removing any data.
Lesson 1006Stack abstract data type conceptLesson 1011Implementing the Peek function
PEMDAS
In elementary school, you likely learned PEMDAS—the rule that says you must do multiplication before addition.
Lesson 212Operator precedence table
Pen
To do this, they need two things: a Pen and Paper.
Lesson 1128Avoiding deadlocks
people
In the code above, if C evaluated (cookies / people) when people was 0, the program would crash.
Lesson 170Short-circuit evaluation of `&&`
people == 0
Because of short-circuiting, the program sees people == 0 is true, decides the whole if is true, and skips the dangerous math on the right side entirely.
Lesson 254Short-circuit evaluation in logical OR
people > 0
Because of short-circuiting, the computer sees people > 0 is false, skips the division entirely, and moves straight to the else block.
Lesson 170Short-circuit evaluation of `&&`
perf
Unlike simple timers that tell you how long a program took to run, perf looks at "hardware events." It watches the CPU as it executes your instructions and takes snapshots (samples) of what the program is doing thousands of times per second.
Lesson 1186Using `perf` for hardware-level insights
perf record
perf record and perf report allow you to pinpoint the exact lines of code where your CPU is spending the most effort.
Lesson 1186Using `perf` for hardware-level insights
perf record ./my_program
perf record ./my_program: This runs your code and records performance data into a file named perf.data.
Lesson 1186Using `perf` for hardware-level insights
perf report
When you run perf report, you’ll see a list of functions sorted by "Overhead." If a function shows 80.00%, it means the CPU was busy inside that function 80% of the time.
Lesson 1186Using `perf` for hardware-level insights
perf.data
perf record ./my_program: This runs your code and records performance data into a file named perf.data.
Lesson 1186Using `perf` for hardware-level insights
performance
The primary advantage of an arena is performance.
Lesson 591Trade-offs of arena vs malloc
permanent lifetime
A static variable has a limited scope (it can only be seen inside its function) but a permanent lifetime (it stays in memory for the entire duration of the program).
Lesson 139Lifetime vs. Scope
permanentCount
In the example above, permanentCount remembers its value because it sits safely in the Data Segment.
Lesson 149Memory segments: Stack vs. Data
permission
const is about permission (Can my code change this?), while volatile is about expectation (Can the outside world change this?).
Lesson 917Combining `const` and `volatile`
Permission Denied
In C, this is exactly what happens when you encounter a Permission Denied error.
Lesson 745Handling 'Permission Denied' errors
perror
Just remember to call perror immediately after the failure occurs, as other successful functions might reset the error code before you get a chance to read it.
Lesson 741Using perror for descriptive errorsLesson 898Interpreting errors with `perror`
perror("custom message")
Use perror("custom message") to instantly translate system error codes into human-readable descriptions.
Lesson 898Interpreting errors with `perror`
perror()
You can then use perror(), a handy function that prints a human-readable description of the last error that occurred.
Lesson 745Handling 'Permission Denied' errors
Person
Imagine you have a Person struct that contains a pointer to a Job struct.
Lesson 629Deep vs shallow copies of nested structsLesson 1035Hash table insertion
Person2 = Person1
If you perform a shallow copy (Person2 = Person1), C simply copies the memory address stored in the pointer.
Lesson 629Deep vs shallow copies of nested structs
photo[0][2]
If you were to look at the memory addresses, you would see that photo[0][2] (the end of the first row) is sitting right next to photo[1][0] (the start of the second row).
Lesson 408Memory layout: Row-major order
photo[1][0]
When you ask for photo[1][0], the computer calculates the location by saying: "I need to skip exactly one full row of three elements to get to the start of the second row."
Lesson 408Memory layout: Row-major order
photocopy
You want your friend to perform a calculation with that number, so you hand them a photocopy of your paper.
Lesson 339Local scope of parametersLesson 343Why changing a parameter doesn't affect the callerLesson 345Limitations of pass by value
photos/vacation/beach.jpg
This is incredibly useful when you need the last occurrence of something, such as finding the file extension in a full file path like photos/vacation/beach.jpg.
Lesson 851Searching for characters with `strchr` and `strrchr`
physical space
sizeof tells you how much physical space the array occupies in memory.
Lesson 847Finding string length with `strlen`
physics_lib.so
Instead, it leaves a "placeholder" or a note that says, "When this program starts, go find the code for calculate_physics() in physics_lib.so."
Lesson 813What is a shared library `.so` / `.dll`
physics.c
If physics.c includes constants.h, and graphics.c includes constants.h, and then main.c includes both physics.h and graphics.h, the contents of constants.h will be pasted into your main file multiple times.
Lesson 788The purpose of header filesLesson 789The 'duplicate definition' errorLesson 802Dependency graphing in your headLesson 806Linking multiple object filesLesson 817Why we need build tools
physics.h
If physics.c includes constants.h, and graphics.c includes constants.h, and then main.c includes both physics.h and graphics.h, the contents of constants.h will be pasted into your main file multiple times.
Lesson 379Using `#include` with quotesLesson 789The 'duplicate definition' errorLesson 1203Header guard best practices
pick one and stick to it
While C traditionally favors snake_case, the most important rule is to pick one and stick to it throughout your entire project.
Lesson 1197Meaningful variable naming conventions
pid_t
When you call it, it reaches out to the system kernel and asks, "What is my ID?" It then returns a value of type pid_t, which is essentially a specialized integer used specifically for process IDs.
Lesson 1068Getting PID with getpid()Lesson 1074Handling fork() return values
Pilot
If you define the Pilot struct first, the compiler will complain because it hasn't seen the Starship struct yet.
Lesson 628Forward declarations of structs
pipe
In systems programming, a pipe is a unidirectional communication channel managed by the operating system kernel.
Lesson 1100Anatomy of a pipeLesson 1101Creating pipes with pipe()Lesson 1102Unidirectional flow in pipes
pipe()
The pipe() function creates a pair of file descriptors—fd[0] for reading and fd[1] for writing—enabling a one-way flow of data between related processes.
Lesson 1100Anatomy of a pipeLesson 1101Creating pipes with pipe()Lesson 1103Closing unused pipe endsLesson 1104Piping data between parent and childLesson 1106Introduction to named pipes (FIFOs)
pipe(fd)
When we create a pipe using pipe(fd), we get two file descriptors: fd[0] for reading and fd[1] for writing.
Lesson 1105Redirecting stdout to a pipe
pivot
You pick one card—let’s say a 7—and call it the pivot.
Lesson 1056Quick Sort: Partitioning logic
plain text
It is vital to understand that a source file must be plain text.
Lesson 15The concept of a Source File
platform
Instead, the size is often determined by your platform—the combination of your computer’s hardware (CPU) and your operating system.
Lesson 85Platform dependency of sizes
platform-dependent
In C, the size of a struct is platform-dependent.
Lesson 644Platform dependency of struct size
Play
You want to hit Play and let the movie run normally until the next scene you care about.
Lesson 831Continuing execution with `continue`
Player
In your personal life, your name might be "Robert," but in the context of a game, your "type" might be "Player." By using typedef, you are telling the compiler, "Every time I say Player, I really just mean int."
Lesson 608Using typedef with primitive types
player_age
If you are setting the player_age and the gravity_constant, keep them on separate lines!
Lesson 196Chained assignments `a = b = c`
player_attribute_flags
Using status |= mask instead of status = status | mask makes your code cleaner and reduces the chance of typos, especially when your variable names are long (like player_attribute_flags).
Lesson 195Compound bitwise assignments
player_experience_points
When you use -=, you are explicitly saying "I am modifying this specific variable." In complex programs where variable names might be long (like player_experience_points), using -= prevents typos because you only have to write the name once.
Lesson 193Compound subtraction `-=`
PLAYER_H
Since PLAYER_H was defined during the first pass, the check fails, and the preprocessor jumps straight to the #endif, effectively ignoring the rest of the file.
Lesson 790Creating basic include guards
player_health_points
It eliminates the clutter of repeating the variable name, which reduces the chance of typos—especially when your variable names are long, like player_health_points.
Lesson 192Compound addition `+=`
player->health
Because the user’s code only sees the header, they can’t access the struct members directly (e.g., player->health would cause a compiler error).
Lesson 673Opaque types with header files
player.c
If global_score is defined in game.h, and both player.c and enemy.c include it, they both end up trying to create their own version of that variable.
Lesson 146The `extern` keyword for multi-file codeLesson 797The `extern` keyword for variablesLesson 925Common linkage errors and 'multiple definition'
player.h
If you accidentally include it twice—perhaps because main.c includes player.h and also includes game.h, which itself includes player.h—the compiler sees the same code twice in a row.
Lesson 789The 'duplicate definition' errorLesson 790Creating basic include guardsLesson 791How `#pragma once` worksLesson 792Forward declarations in headersLesson 1203Header guard best practices
player.o
Because of $@ and $<, this single rule works for logger.o, math_utils.o, or player.o without you ever having to type those names manually.
Lesson 822Automatic variables like `$@` and `$<`
player.pos.x
player.x is easier to read than player.pos.x.
Lesson 671Anonymous unions inside structs
player.position.coords.x
Previously, if you nested a struct inside another, you had to give it a name, leading to long, clunky access chains like player.position.coords.x.
Lesson 967C11: Multi-threading and Anonymous structures
player.x
player.x is easier to read than player.pos.x.
Lesson 671Anonymous unions inside structs
player1
Start with a letter: You can use numbers inside the name, but never at the start (e.g., player1 is fine, but 1player will cause an error).
Lesson 75The syntax of a declarationLesson 604Copying structs with the assignment operatorLesson 630Declaring an array of structs
Player1 500
But what if your file is a list of high scores, like Player1 500?
Lesson 719Formatted file input with fscanf
player1_score
Imagine you are building a game and need to create several variables like player1_score, player2_score, and player3_score.
Lesson 777The token-pasting operator `##`
player2
Declaring individual variables like player1, player2, and player3 becomes messy very quickly.
Lesson 604Copying structs with the assignment operatorLesson 630Declaring an array of structs
player2_score
Imagine you are building a game and need to create several variables like player1_score, player2_score, and player3_score.
Lesson 777The token-pasting operator `##`
player3
Declaring individual variables like player1, player2, and player3 becomes messy very quickly.
Lesson 630Declaring an array of structs
player3_score
Imagine you are building a game and need to create several variables like player1_score, player2_score, and player3_score.
Lesson 777The token-pasting operator `##`
playerInventorySpaceRemaining
Readability: When variables have long names, like playerInventorySpaceRemaining, repeating the name on both sides of the equals sign makes the line cluttered.
Lesson 194Compound multiplication and division
players
In the example above, printf sees the %d first, so it grabs players.
Lesson 54Multiple specifiers in one line
playerScore
When you tell C to look at playerScore, the computer automatically goes to the correct "locker," opens it, and reads the value inside.
Lesson 73What is a variable?Lesson 75The syntax of a declaration
playerScored
It simply looks at what is currently inside playerScored (which is 10), makes a copy of it, and drops that copy into totalPoints.
Lesson 78Assigning values with `=`
PLY_
By adopting this habit early, you ensure that your "Module A" can never accidentally overwrite a function in "Module B." It also makes your code searchable; if you want to find every function related to the player, you simply search your project for PLY_.
Lesson 801Naming conventions for large projects
Point
If your Point struct grows later to include a z coordinate, the nested braces ensure your radius doesn't accidentally get assigned to the wrong field.
Lesson 609Creating a shorthand for struct namesLesson 614Improving code readability with typedefLesson 625Initializing nested structures
Point p
Point p; is much easier to scan visually than struct Point p;.
Lesson 614Improving code readability with typedef
pointer arithmetic
Under the hood, when you write scores[1], C performs pointer arithmetic.
Lesson 986Accessing elements by index
pointer to a character type
If you truly need to look at the raw bits of a variable (for example, to send them over a network), the only safe way is to use a pointer to a character type (char or unsigned char) or the memcpy function.
Lesson 527Pointer type-punning dangers
pointer to a constant
In C, a pointer to a constant works exactly like that glass.
Lesson 493Pointer to a constant (`const int *p`)
pointer to a pointer
A pointer to a pointer (declared as int ptr) is like a locked box that contains a second treasure map.
Lesson 502Visualizing pointer chainsLesson 504Dynamic 2D array structures
pointer to a struct
In C, a pointer to a struct is that sticky note.
Lesson 615Declaring a pointer to a struct
pointer to an array
A pointer to an array is a single pointer variable that points to a whole block of memory formatted as an array.
Lesson 927Arrays of pointers vs Pointers to arrays
pointer to an integer
If you wrote int *ptr(int, int);, C would think you are declaring a normal function named ptr that returns a pointer to an integer.
Lesson 514Syntax of function pointers
pointer to the postcard itself
To let a function change the address on your original postcard, you must give the function a pointer to the postcard itself.
Lesson 503Modifying a pointer inside a function
pointerB = pointerA
When you set pointerB = pointerA, you aren't making a copy of the data (secretCode).
Lesson 460Multiple pointers to the same address
pointers to those elements
When qsort compares two elements, it passes pointers to those elements to your comparison function.
Lesson 878Writing a string comparison function for `qsort`
points
In the code above, points and Points coexist as two separate variables.
Lesson 23Case sensitivity in C
points2
For example, points2 is fine, but 2points is illegal.
Lesson 74Naming rules and identifiers
Pool
Instead of asking the "General Store" for a new tiny box every time a bullet is fired, you can buy one giant crate of memory (a Pool) at the start.
Lesson 578Motivation for custom allocators
Pop calculate
Pop calculate: calculate uses that value, finishes, and its frame is deleted.
Lesson 348Pushing and popping frames
Pop square
Pop square: square finishes and returns a value.
Lesson 348Pushing and popping frames
POPCNT
Modern CPUs, however, have a single instruction called POPCNT (population count) that does this instantly.
Lesson 964Compiler intrinsics as an alternative
Portability
Environment variables are the gold standard for portability.
Lesson 948The `#pragma pack` directiveLesson 1071Environment variables in C
position
Even if the data types are the same (both integers), the position determines which value represents the width and which represents the height.
Lesson 337Positional matching of arguments
positive
A positive number if the first item comes after the second.
Lesson 1059Using C library 'qsort' function
Positive Integer
The Parent: Receives a Positive Integer (the Process ID of the child).
Lesson 1074Handling fork() return values
Post
For example, a User struct might need to know about a Post struct, and the Post needs to know who the User is.
Lesson 794Circular dependency issues
post-it note
To fix this, we don't put the box inside the box; we put a post-it note inside the box.
Lesson 627Limitations of self-referential definitions
Post-order
In Post-order traversal, you visit the Left subtree, then the Right subtree, and finally the Root (L-R-Root).
Lesson 1026Pre-order and Post-order traversal
post-test
In programming, we call this post-test logic.
Lesson 275Guaranteed execution: why do-while is different
post.h
When the compiler looks at user.h, it tries to resolve post.h first.
Lesson 794Circular dependency issues
postfix
Use postfix (i++) only when your logic strictly requires the original value before the increase.
Lesson 205Performance: Prefix vs Postfix
Postfix (i++)
Postfix (i++) is like being handed a copy of your old unpaid bill, paying it, and then the shop updates their records later.
Lesson 205Performance: Prefix vs Postfix
pow()
A crucial detail to remember is that pow() works with doubles (floating-point numbers).
Lesson 861Basic power and square root: `pow` and `sqrt`
power of 2
In binary, each position represents a power of 2, starting from the right.
Lesson 179Understanding binary representation
PPID
This "creator" is known as the Parent Process, and its identification number is the PPID (Parent Process ID).
Lesson 1069Parent processes and getppid()
Pre-order
Use Pre-order (Root-L-R) to copy or clone a tree, and Post-order (L-R-Root) to safely delete or free a tree.
Lesson 1026Pre-order and Post-order traversal
precedence
In previous lessons, we learned about precedence, which is like the "order of operations" in math (multiplication happens before addition).
Lesson 213Left-to-right associativityLesson 215Parentheses for clarityLesson 217Precedence of assignmentLesson 219Common precedence errorsLesson 227Order of evaluation vs Precedence
prefix
However, it is a "best practice" to default to prefix (++i) unless you specifically need the old value.
Lesson 205Performance: Prefix vs Postfix
Prefix (++i)
Prefix (++i) is like paying for your coffee, getting your receipt, and then walking away.
Lesson 205Performance: Prefix vs Postfix
prefix decrement
When you place them before the variable name (like --x), it is called a prefix decrement.
Lesson 200Prefix decrement `--x`
prefix increment
When you place this operator before the variable name (like ++x), it is called a prefix increment.
Lesson 198Prefix increment `++x`
prefixes
If there is only one person named John, you just call him "John." If the town grows and three more Johns move in, you have to start calling them "Baker John," "Fisher John," and "Smith John." In C, we do this using prefixes.
Lesson 801Naming conventions for large projects
Prefixing
Since C won't provide the "bedrooms" for us, programmers have developed a convention to stay organized: Prefixing.
Lesson 666Scoped enum limitations in C
PREMIUM
Here is a program that behaves differently depending on whether a PREMIUM flag is set during the build:
Lesson 786Feature toggles via command line `-D`
preprocessor directive
This is because it is a preprocessor directive, not a standard C statement.
Lesson 765The `#include` directive for standard headers
preprocessor macros
To write professional, low-level C, you use preprocessor macros.
Lesson 963Platform-specific assembly (x86 vs ARM)
prev_node
When you insert a new node (new_node) between two existing nodes (prev_node and next_node), you must update four pointers to maintain the integrity of the chain.
Lesson 995Inserting after a specific nodeLesson 1002Inserting in a doubly linked list
prev_node->next
If you update prev_node->next before you've saved its value into new_node->next, you lose your connection to the rest of the list—it’s like dropping the hand of the person behind you before the new person has grabbed it.
Lesson 995Inserting after a specific nodeLesson 1002Inserting in a doubly linked list
price
By looking at the output, you can confirm if price was actually 100 or if it somehow changed to 0 earlier in the code.
Lesson 72Debugging by printingLesson 447Memory as a linear sequence of bytesLesson 622The importance of NULL checks for struct pointers
price = 10
Imagine you are sitting at a desk with a sticky note that says price = 10.
Lesson 138Shadowing: Nested scope name clashes
price = 5
Inside that box, you put a new sticky note that also says price = 5.
Lesson 138Shadowing: Nested scope name clashes
price = items * 1.0825
But if you write price = items * 1.0825, someone reading your code later (including future you) will have no idea that 1.0825 represents the local sales tax rate for a specific city.
Lesson 771Avoiding magic numbers with macros
pricePerCookie
Even though cookies started as a whole number, C recognized that the pricePerCookie held more precise information (decimals).
Lesson 126The 'Usual Arithmetic Conversions'
prices
By understanding that prices is just a pointer to prices[0], you unlock the ability to navigate through memory quickly and efficiently.
Lesson 471Array names as constant pointers
prices[0]
By understanding that prices is just a pointer to prices[0], you unlock the ability to navigate through memory quickly and efficiently.
Lesson 471Array names as constant pointers
prices[1]
You might wonder why we would use *(prices + 1) when prices[1] is easier to read.
Lesson 472Accessing arrays with pointer notation
primeNumbers[0]
In this example, primeNumbers[0] becomes 2, primeNumbers[1] becomes 3, and so on.
Lesson 391Initialization with curly braces `{}`
primeNumbers[1]
In this example, primeNumbers[0] becomes 2, primeNumbers[1] becomes 3, and so on.
Lesson 391Initialization with curly braces `{}`
print items - i
You can even print complex expressions, like print items - i, and GDB will do the math for you based on the current state of the program.
Lesson 832Checking variable values with `print`
print ptr
Even though the program is dead, you can type print ptr to see its value at the moment of impact.
Lesson 837Debugging a Segfault from a core dump
print secret_key
If you are paused inside process_data, typing print secret_key will result in an error because that variable doesn't exist in the current frame.
Lesson 834Moving between frames with `up` and `down`
print secret_number
If you compile the code above with gcc -g, and then open it in GDB, you can type list to see your actual C code or print secret_number to see its value.
Lesson 825Compiling with debug symbols `-g`
print_float(float x)
You usually end up writing print_int(int x) and print_float(float x).
Lesson 977Type-based function overloading simulation
print_int
It then looks at the _Generic list, finds the int: label, and replaces the macro with the print_int function.
Lesson 977Type-based function overloading simulation
PRINT_INT(10 + 5)
If you pass PRINT_INT(10 + 5), the #x will literally become the string "10 + 5".
Lesson 776The stringizing operator `#`
print_int(int x)
You usually end up writing print_int(int x) and print_float(float x).
Lesson 977Type-based function overloading simulation
print_total
When print_total is called, the computer needs to remember to come back to the "Program continues..." line later.
Lesson 350Return addresses in memory
print_type(score)
When the compiler sees print_type(score), it notices score is an int.
Lesson 973Introduction to the `_Generic` keyword
print_val
If the print_val macro above wasn't written to handle your custom struct, you cannot use it.
Lesson 978Comparing `_Generic` to C++ templates
print_value
Let’s say we want a single name, print_value, that works for both integers and strings.
Lesson 974The syntax of a generic selection
print()
We can combine _Generic with a macro to create a universal print() function.
Lesson 975Implementing a generic 'Print' macro
print(age)
When the compiler sees print(age), it checks the type of age.
Lesson 975Implementing a generic 'Print' macro
printf
It tells the preprocessor: "If you reach this line, stop everything, display a specific message, and fail the build." Unlike a standard printf which happens while your program is running, #error happens while your program is being created.
Lesson 17The 'Hello World' codeLesson 18The `#include` directiveLesson 19What is a Header File?Lesson 27Phase 3: Assembly to Object CodeLesson 28Phase 4: The LinkerLesson 30Creating an executable binaryLesson 41The `stdio.h` libraryLesson 42Basic `printf` syntaxLesson 43Printing literal stringsLesson 44Newline character `\n`Lesson 45Horizontal tab `\t`Lesson 46Escaping double quotesLesson 47Escaping the backslashLesson 49Introduction to Format SpecifiersLesson 50Printing integers with `%d`Lesson 51Printing characters with `%c`Lesson 52Printing decimals with `%f`Lesson 53The `%s` specifier for stringsLesson 54Multiple specifiers in one lineLesson 55Argument-specifier matchingLesson 56Basic field width formattingLesson 64Library linking basicsLesson 65Syntax errors vs Logic errorsLesson 66Reading compiler error messagesLesson 68Warnings vs Fatal errorsLesson 72Debugging by printingLesson 82Short vs. Long integersLesson 83The `long long` typeLesson 94Format specifiers for unsigned intsLesson 97Single precision `float`Lesson 98Double precision `double`Lesson 99The `long double` typeLesson 108Escape sequences like `\n` and `\t`Lesson 112Printing chars with `%c`Lesson 199Postfix increment `x++`Lesson 200Prefix decrement `--x`Lesson 201Postfix decrement `x--`Lesson 202Differences in expression resultsLesson 207Ternary as an expressionLesson 222Sequence points at `;`Lesson 237The Address-of operator `&`Lesson 243Truthiness: 0 is false, non-zero is trueLesson 263Grouping multiple cases into one blockLesson 274The do-while syntax and the trailing semicolonLesson 292Continue in while vs for loopsLesson 298Introduction to loops inside loopsLesson 325Writing your first custom functionLesson 372The `stdarg.h` libraryLesson 376How `printf` works internallyLesson 417The Null Terminator `\0` character
printf("\n")
Crucially, the printf("\n") sits outside the inner loop but inside the outer loop.
Lesson 300Using nested loops to print 2D gridsLesson 412Printing a 2D matrix to the console
printf("5 + 5")
If you type printf("5 + 5");, the screen won't show 10; it will show the literal characters 5 + 5.
Lesson 43Printing literal strings
printf("Battery: 85%")
If you type printf("Battery: 85%");, the compiler gets confused.
Lesson 690Escaping the percent sign %%
printf("C:\Windows")
If you type printf("C:\Windows");, C will see the \W and wonder what secret command you are trying to trigger.
Lesson 47Escaping the backslash
printf("Hello")
When you type printf("Hello"), the first quote tells the compiler, "A message is starting," and the second one says, "The message is over."
Lesson 42Basic `printf` syntaxLesson 46Escaping double quotes
printf("Here\n")
When your code crashes or gives the wrong answer, it’s tempting to sprinkle printf("Here\n"); everywhere.
Lesson 1157Strategic `printf()` debugging
printf("My age is age")
If you type printf("My age is age");, the computer will literally print the word "age." To fix this, we use a special placeholder called a format specifier.
Lesson 50Printing integers with `%d`
printf("Reached point A\n")
If your program crashes halfway through, you can place "breadcrumbs" (like printf("Reached point A\n");) to find the exact line where it dies.
Lesson 72Debugging by printing
printf("Your text here")
Use printf("Your text here"); to send a message to the screen, always remembering to wrap your text in double quotes and end the line with a semicolon.
Lesson 42Basic `printf` syntax
printf(Hello)
In C, if you type printf(Hello), the computer looks for a command or a variable named Hello.
Lesson 42Basic `printf` syntax
printHello
Like variable names, these should be descriptive verbs, like calculateTax or printHello.
Lesson 323Anatomy of a function definition
printString(const char *s)
When you write a function like printString(const char *s), you are promising other programmers (and yourself) that the function will only read the string, not destroy it.
Lesson 496When to use `const` with pointers
prntf
Misspelling a keyword (like writing prntf instead of printf).
Lesson 68Warnings vs Fatal errors
procedural
Think of the ternary operator as a functional tool and if-else as a procedural tool.
Lesson 210Ternary vs If-Else for assignments
Process ID
To manage hundreds of processes simultaneously—like your web browser, your music player, and your code—the OS assigns each one a unique identification number called a Process ID, or PID for short.
Lesson 1067What is a process ID (PID)Lesson 1068Getting PID with getpid()
Process ID (PID)
Every running program on your computer is assigned a unique number called a Process ID (PID).
Lesson 838Attaching GDB to a running process
Process the data again
Process the data again. You have now duplicated your last entry.
Lesson 722Why feof inside a loop condition is bad
process_age(-5)
When process_age(-5) runs, the program stops and tells you exactly which file and which line failed.
Lesson 901Using `assert` for internal debugging
process_data
If you are paused inside process_data, typing print secret_key will result in an error because that variable doesn't exist in the current frame.
Lesson 833Inspecting the call stack with `backtrace`Lesson 834Moving between frames with `up` and `down`
process_data_v2()
When you work on large-scale projects, you won't remember why you created process_data_v2() six months ago.
Lesson 1199Writing effective Doxygen comments
processor ticks
This isn't a measurement of seconds; it’s a count of processor ticks.
Lesson 896Measuring CPU ticks with `clock`
profiler
In programming, a profiler is your kitchen stopwatch.
Lesson 1182Introduction to the `gprof` profiler
profilers
In C, we use tools called profilers (like gprof or Valgrind's Callgrind) to identify these spots.
Lesson 1183Identifying 'Hot Spots' in your code
program break
When you start your program, the fence is set at a specific location called the program break.
Lesson 592Introduction to brk and sbrk
program.exe
When you compiled your code, you likely created a file named a.out (the default name on Linux and macOS) or program.exe (on Windows).
Lesson 28Phase 4: The LinkerLesson 31How the OS runs a programLesson 32Executing from the command line
Project-Path-Name
To prevent this, follow the Project-Path-Name pattern.
Lesson 1203Header guard best practices
PROT_EXEC
For example, when your program loads a shared library, the actual machine code instructions are mapped as PROT_READ (and PROT_EXEC).
Lesson 595Memory protection constants (PROT_READ)
PROT_READ
For example, when your program loads a shared library, the actual machine code instructions are mapped as PROT_READ (and PROT_EXEC).
Lesson 595Memory protection constants (PROT_READ)
protection constants
This is done through protection constants.
Lesson 595Memory protection constants (PROT_READ)
prototype
In C, a prototype is like a movie trailer—it tells the compiler the title, the stars (parameters), and the genre (return type) before the full feature begins.
Lesson 330Function prototype syntaxLesson 332Matching prototypes with definitionsLesson 334Common errors with missing prototypes
ps
You can use the ps command in your terminal:
Lesson 838Attaching GDB to a running process
pthread_barrier_t
In POSIX threads (pthreads), we use pthread_barrier_t.
Lesson 1137Thread barriers
pthread_barrier_wait()
When a thread reaches the checkpoint, it calls pthread_barrier_wait().
Lesson 1137Thread barriers
pthread_cond_broadcast
You use pthread_cond_broadcast when a state change in your program might be relevant to every single thread currently waiting.
Lesson 1135Broadcasting to all threads
pthread_cond_signal
The Mutex Matters: You should generally hold the mutex when calling pthread_cond_signal to ensure the "signaler" and the "waiter" don't have a race condition regarding the state change.
Lesson 1132Introduction to condition variablesLesson 1134Signaling with pthread_cond_signalLesson 1135Broadcasting to all threads
pthread_cond_wait
In previous lessons, we learned that pthread_cond_wait puts a thread to sleep while it waits for a specific condition (like a queue filling up).
Lesson 1132Introduction to condition variablesLesson 1133Waiting with pthread_cond_waitLesson 1134Signaling with pthread_cond_signalLesson 1135Broadcasting to all threads
pthread_create
Because pthread_create only accepts one argument, if you need to pass multiple pieces of information (like a username and a user ID), you should wrap them in a struct.
Lesson 1117Creating threads with pthread_createLesson 1118Passing arguments to threadsLesson 1119Waiting for threads with pthread_joinLesson 1120Returning values from threadsLesson 1121Detaching threads
pthread_detach
The main reason to use pthread_detach is to prevent resource leaks.
Lesson 1121Detaching threads
pthread_detach(thread_id)
You can detach a thread using pthread_detach(thread_id).
Lesson 1121Detaching threads
pthread_exit()
Use pthread_exit() when you have complex logic where a thread needs to stop deep inside a nested function, or when you want the main thread to finish its own work but allow background threads to keep processing in the background.
Lesson 1123The pthread_exit function
pthread_join
pthread_join pauses the current thread until the specified thread terminates, ensuring the program doesn't exit prematurely and cleaning up thread resources.
Lesson 1119Waiting for threads with pthread_joinLesson 1120Returning values from threadsLesson 1121Detaching threads
pthread_join()
The pthread_join() function acts as a synchronization point.
Lesson 1119Waiting for threads with pthread_join
pthread_mutex_destroy()
Always call pthread_mutex_destroy() on an unlocked mutex when it is no longer needed to free up system resources.
Lesson 1131Cleaning up mutex resources
pthread_mutex_init
When you initialize one using pthread_mutex_init, the operating system sets aside a small amount of memory and potentially some kernel-level handles.
Lesson 1131Cleaning up mutex resources
pthread_mutex_init()
Before a mutex can protect shared data, it must be initialized using either PTHREAD_MUTEX_INITIALIZER for globals or pthread_mutex_init() for local variables.
Lesson 1125Initializing a pthread_mutex_t
PTHREAD_MUTEX_INITIALIZER
Before a mutex can protect shared data, it must be initialized using either PTHREAD_MUTEX_INITIALIZER for globals or pthread_mutex_init() for local variables.
Lesson 1125Initializing a pthread_mutex_t
pthread_mutex_lock
Normally, when you call pthread_mutex_lock, your thread is like a person standing in line for a single-occupancy bathroom.
Lesson 1126Locking and unlocking mutexesLesson 1127Critical section best practicesLesson 1129Using pthread_mutex_trylockLesson 1146Lock-free programming concepts
pthread_mutex_lock()
pthread_mutex_lock(): The thread attempts to grab the key.
Lesson 1126Locking and unlocking mutexes
pthread_mutex_t
If you try to lock a pthread_mutex_t that hasn't been initialized, your program will likely crash or exhibit "undefined behavior." Think of it like trying to turn a key in a lock that hasn't been installed in a door yet; the mechanism simply isn't there to catch the bolt.
Lesson 1125Initializing a pthread_mutex_tLesson 1138Read-write locks basics
pthread_mutex_trylock
Think of pthread_mutex_trylock as walking up to the door, giving the handle a quick turn, and—if it doesn't open immediately—walking away to do something else rather than standing in the hallway.
Lesson 1129Using pthread_mutex_trylock
pthread_mutex_unlock
Use pthread_mutex_lock before accessing shared data and pthread_mutex_unlock immediately after to ensure thread safety.
Lesson 1126Locking and unlocking mutexes
pthread_mutex_unlock()
pthread_mutex_unlock(): The thread puts the key back, waking up any other threads that are waiting for it.
Lesson 1126Locking and unlocking mutexes
pthread_rwlock_t
A Read-Write Lock (pthread_rwlock_t) distinguishes between reading and writing.
Lesson 1138Read-write locks basics
pthread_t
The ID: A variable of type pthread_t to act as the thread's "name tag."
Lesson 1117Creating threads with pthread_create
pthreads
Because pthreads is designed to be flexible, it communicates using void * (a generic pointer).
Lesson 1120Returning values from threadsLesson 1137Thread barriers
ptr != NULL
By checking ptr != NULL, you ensure that the *ptr operation only happens when there is a valid destination to visit.
Lesson 462Checking for NULL before dereferencing
ptr + (2 * 4 bytes)
When you work with an int pointer, C treats ptr + 2 as ptr + (2 * 4 bytes).
Lesson 468Scaling factor in pointer math
ptr + 1
If you have an integer pointer and you write ptr + 1, C knows that to get to the next integer, it must skip over 4 bytes.
Lesson 468Scaling factor in pointer math
ptr + 2
When you work with an int pointer, C treats ptr + 2 as ptr + (2 * 4 bytes).
Lesson 468Scaling factor in pointer math
ptr + n
By using ptr++ or ptr + n, you are manually steering through memory.
Lesson 465How data types affect step sizeLesson 470Navigating memory blocks manually
ptr = NULL
Always follow free(ptr); with ptr = NULL; to ensure you don't accidentally use an address that no longer belongs to you.
Lesson 559Setting pointers to NULL after free
ptr = numbers
In the example above, ptr = numbers is perfectly valid.
Lesson 475Array decay explained
ptr_array
Here, ptr_array is an array of 5 elements.
Lesson 927Arrays of pointers vs Pointers to arrays
ptr_as_int
This often fails because the compiler doesn't expect ptr_as_int to affect my_float.
Lesson 527Pointer type-punning dangers
ptr->member
Use ptr->member as a readable shortcut for (*ptr).member when working with pointers to structures.
Lesson 616The arrow operator `->` syntax
ptr.age
In C, if you have a pointer ptr, you can’t just say ptr.age because a memory address doesn’t have an age—the folder sitting at that address does.
Lesson 239Member access `.` and `->`
ptr.speed
The parentheses are mandatory because, without them, C would try to look for a pointer named ptr.speed, which doesn't exist.
Lesson 616The arrow operator `->` syntax
ptr[i]
If you use ptr[i], the base address of ptr stays exactly where it belongs.
Lesson 555Invalid pointer increments before free
ptr1
It can’t optimize or reorder instructions because it fears that changing data through ptr1 might unexpectedly change the data being read by ptr2.
Lesson 915The `restrict` pointer qualifier
ptr1 + ptr2
In C, adding two pointers (like ptr1 + ptr2) is illegal.
Lesson 469Illegal pointer arithmetic operations
ptr2
It can’t optimize or reorder instructions because it fears that changing data through ptr1 might unexpectedly change the data being read by ptr2.
Lesson 915The `restrict` pointer qualifier
ptrdiff_t
The Result Type: The result of this operation is an integer type called ptrdiff_t.
Lesson 466Subtracting two pointers
ptrToPtr
The actual integer value at the final destination (the second hop).
Lesson 500Concept of double indirection
putchar
If you are building a system where every byte of memory counts, or if you are writing a loop that processes text one letter at a time (like an animation or a custom text filter), putchar is your best friend.
Lesson 679Basic character output with putchar
putchar('x')
Use putchar('x'); to output one single character to the console quickly and efficiently.
Lesson 679Basic character output with putchar
putchar()
However, when we perform input/output (I/O) using functions like getchar() or putchar(), you will notice something strange: these functions often deal with int instead of char.
Lesson 683Relationship between char and int in I/O
puts
The puts function stands for "put string." Its behavior is very predictable: it takes the string you give it, prints it to the console, and then automatically adds a newline character (\n) at the end.
Lesson 700Printing strings with puts

Q

qsort()
Because qsort() is designed to handle any data type (from integers to complex structures), it uses "void pointers" (void *).
Lesson 877Writing an integer comparison functionLesson 879Sorting structs by multiple fields
quadruples
When you double the amount of data, the time it takes to sort doesn't just double—it quadruples.
Lesson 1050Time complexity of O(n^2) sorts
quantity
In the example above, the compiler inserts 3 bytes of empty padding after label so that quantity starts on a memory address that is a multiple of 4.
Lesson 637The sizeof operator on structs
Quarter-Rule
A common industry standard is the Quarter-Rule: only shrink the capacity by half when the size drops to one-quarter of the capacity.
Lesson 987Popping and shrinking logic
Queue
To keep track of who to visit next without getting lost, BFS uses a Queue (First-In, First-Out).
Lesson 1014Queue abstract data type conceptLesson 1044Breadth-First Search (BFS) logic
queue[front]
If you try to access queue[front] when no data exists, your program will crash or behave unpredictably.
Lesson 1017Dequeue operation logic
Quick Sort
While algorithms like Bubble Sort and Insertion Sort are typically stable, Quick Sort is often unstable because it swaps elements over long distances.
Lesson 1052Stability in sorting algorithms
quit
To leave the debugger and return to your normal terminal, type quit or press Ctrl+D.
Lesson 826Starting GDB with an executableLesson 1161Starting a program in `gdb` or `lldb`

R

R-value
To understand how this works, you need to understand the two "sides" of the operation: the L-value and the R-value.
Lesson 191L-values vs R-values
Race Condition
In C programming, this is known as a Race Condition.
Lesson 763Temporary file security risks
radians
C's trigonometric functions require angles in radians, so always multiply degrees by (PI / 180.0) before passing them to sin(), cos(), or tan().
Lesson 863Trigonometric functions in radians
radius radius 3.14159
Readability: radius radius PI is much clearer to a human than radius radius 3.14159.
Lesson 768Defining constants with `#define`
radius radius PI
Readability: radius radius PI is much clearer to a human than radius radius 3.14159.
Lesson 768Defining constants with `#define`
RAND_MAX
RAND_MAX defines the upper limit of C’s random number generator, allowing you to scale raw random integers into useful ranges or percentages.
Lesson 884The importance of the `RAND_MAX` constant
rand() % N
By using rand() % N, you force the result to stay within the range of 0 to N-1.
Lesson 887Scaling `rand` results to a specific range
Random access
You use random access for large databases or complex file formats.
Lesson 720Sequential vs random access concepts
rank
The hero variable is instantly populated with its original level, health, and rank as if it never left.
Lesson 729Reading structs back into memory
rbx
It often keeps important variables inside CPU registers (like eax or rbx) because accessing registers is faster than accessing RAM.
Lesson 960The 'Clobber' list explained
reaches
The inner loop starts and says, "Run until j reaches i." Since i is 1, it runs once and prints one star.
Lesson 303Controlling the inner loop with outer loop variables
reaches 3
In this code, when i reaches 3, numbers[3] tries to read memory just outside the allocated block.
Lesson 1166Identifying 'Invalid Read' errors
read
The read and write system calls move raw sequences of bytes between file descriptors and memory buffers without any formatting or translation.
Lesson 386Accessing elements with the `[]` operatorLesson 1097Reading and writing raw bytesLesson 1101Creating pipes with pipe()
read end
Imagine a literal physical pipe: you pour water into one end (the write end), and it flows out the other (the read end).
Lesson 1100Anatomy of a pipe
read-only
It simply means the variable is read-only.
Lesson 910The `const` qualifier on variables
read-only window
When you combine them, you create a read-only window into a piece of hardware or a shared memory location.
Lesson 917Combining `const` and `volatile`
read()
Non-blocking I/O allows your program to remain responsive by forcing read() and write() to return immediately with a "try again" status instead of pausing execution.
Lesson 1092File descriptors vs FILE pointersLesson 1098Non-blocking I/O basicsLesson 1104Piping data between parent and childLesson 1106Introduction to named pipes (FIFOs)
readability
First, readability: you can scan a header file in seconds to see what a library is capable of without scrolling through hundreds of lines of logic.
Lesson 796Splitting code into `.c` and `.h`
readelf
If you were to peak inside the resulting my_program file with a tool like readelf, you would see the OS-level headers that turn your simple C logic into a formal, runnable application.
Lesson 808The executable ELF format
Reader
A Reader that looks at every character in the original string.
Lesson 444Removing a character from a string
reading
A Read-Write Lock (pthread_rwlock_t) distinguishes between reading and writing.
Lesson 1138Read-write locks basics
realloc()
If you forget to update that size variable after a realloc(), your program will likely crash or corrupt memory.
Lesson 561Heap buffer overflowsLesson 981Structure for dynamic arrays
rear
If your array size is 5 and your rear is at index 4, the next position isn't 5 (which is out of bounds); it’s (4 + 1) % 5, which equals 0.
Lesson 1015Front and Rear pointersLesson 1016Enqueue operation logicLesson 1017Dequeue operation logicLesson 1018Circular array implementationLesson 1020Linked list queue implementation
recipe
A Makefile consists of "rules." Each rule has a target (what you want to create), a dependency (what is needed to create it), and a recipe (the command to run).
Lesson 1211Writing the Makefile for the project
recipe book
Think of your executable file like a recipe book stored on a shelf (the Hard Drive).
Lesson 31How the OS runs a program
recipes
A Makefile rule uses targets (the goal), dependencies (the requirements), and recipes (the commands) to automate your build process efficiently.
Lesson 819Targets, dependencies, and recipes
Record
If every Record occupies the same number of bytes, we can calculate the memory address of the 100th record with simple math.
Lesson 1213Defining the Record structLesson 1218Loading data from a binary file
Record data[100]
If we declare a standard array like Record data[100];, we hit a wall the moment the 101st item arrives.
Lesson 1214Implementing a dynamic array for storage
record_count
By always checking your record_count against your MAX_RECORDS, you ensure that your data store remains stable.
Lesson 1216Adding and Deleting records safely
record_count < MAX_RECORDS
Before adding a record, you must check if record_count < MAX_RECORDS.
Lesson 1216Adding and Deleting records safely
Records
In our data store project, we aren't just saving raw bytes; we are saving Records.
Lesson 1213Defining the Record struct
recursion
Recursion is like standing on the first step and saying, "To climb this staircase, I will step up once, and then I will perform the 'climb a staircase' task for the remaining steps." Each step creates a new, slightly smaller version of the original problem.
Lesson 349Storage of local variablesLesson 354Importance of the Base CaseLesson 358Iteration vs. Recursion comparisonLesson 531Understanding stack overflowLesson 1055Merge Sort: Recursive splittingLesson 1130Recursive mutexes
recursive
To represent this in C, we need a structure that is recursive.
Lesson 1022Recursive tree node structure
recursive approach
In the recursive approach, we treat the problem as a series of identical sub-problems.
Lesson 1063Binary Search: Recursive approach
recursive function
While losing a few bytes once might not be noticed, losing those bytes inside a loop or a recursive function creates a compound interest effect that can quickly consume every byte of RAM your computer has available.
Lesson 552Leaking in loops and recursion
recursive mutex
A recursive mutex solves this by keeping track of two things:
Lesson 1130Recursive mutexes
recursive step
If the base case is the emergency brake that stops a function from running forever, the recursive step is the engine that keeps it moving toward that stop.
Lesson 355The Recursive Step
recv()
Use send() to push byte arrays out and recv() to pull them in, always checking the return value to see how much data actually moved.
Lesson 1115Sending and receiving over sockets
redefinition of 'struct Player'
The compiler will stop immediately and throw an error: redefinition of 'struct Player'.
Lesson 789The 'duplicate definition' error
reentrant
A reentrant function is one that can be safely interrupted and then "re-entered" by a signal handler without causing errors.
Lesson 1089Signal safety and reentrant functions
register
The register keyword was created as a hint to the compiler: "This variable is going to be used constantly (like a loop counter), so please keep it in a CPU register instead of RAM to make the program faster."
Lesson 147Introduction to the `register` keywordLesson 923Storage class specifier precedenceLesson 924The `register` keyword and its modern relevanceLesson 961Direct register access
registers
To speed things up, the CPU has a few private drawers right on its desk called registers.
Lesson 961Direct register access
Registry
Every Registry and every variable using those types updates automatically.
Lesson 930Complex nested `typedef` structures
regression testing
The power of this approach is regression testing.
Lesson 1178Automating tests with a Shell script
Rehashing
Once the table hits this limit, it’s time to move to a bigger "closet." This process is called Rehashing.
Lesson 1037Load factor and rehashing
relational operators
This is where relational operators come in.
Lesson 242Relational operators: <, <=, >, and >=
remove
If you try to delete a file that doesn't exist, remove won't crash your program; it will simply return a non-zero value, allowing you to handle the error gracefully.
Lesson 757Deleting files with remove
remove("filename")
Use remove("filename") from <stdio.h> to permanently delete a file, checking for a return value of 0 to confirm success.
Lesson 757Deleting files with remove
remove()
Just remember: if you create it, you are responsible for deleting it with remove() before your program exits!
Lesson 759Generating temp filenames with tmpnam
rename
Moving Files: You can use rename to move a file across directories on the same disk drive (e.g., from data/old.txt to archive/old.txt).
Lesson 756Renaming files with rename
requestedIndex
By checking that requestedIndex is at least 0 and strictly less than the array size, we ensure the program never touches memory it doesn't own.
Lesson 437Checking bounds before array access
reserved
By using a reserved or padding field, you achieve two things:
Lesson 575Manual padding in structures
reserved parking space
Think of field width as a reserved parking space.
Lesson 56Basic field width formatting
reserves
The librarian reserves a long row of 100 tables just for you.
Lesson 590Growing an arena with virtual memory
Resident Set Size
The Resident Set Size is the actual amount of physical RAM your program is occupying at this very moment.
Lesson 598Page faults and resident set size
resource leaks
The main reason to use pthread_detach is to prevent resource leaks.
Lesson 1121Detaching threads
responsiveness
The primary advantage is responsiveness.
Lesson 1129Using pthread_mutex_trylock
restrict
I better reload a[i] from memory every single loop iteration just in case the last write to result[i] changed it."* With restrict, the compiler can load values much more efficiently, knowing they won't change unexpectedly.
Lesson 915The `restrict` pointer qualifierLesson 916Optimization benefits of `restrict`
result = 10
In the example above, C doesn't look at result = 10 and stop there.
Lesson 217Precedence of assignment
result = 5
In the example above, result = giveMeFive(); eventually becomes result = 5;.
Lesson 327The `return` statement flow
result = a * b
You place the operator between two operands, like this: result = a * b;.
Lesson 152Multiplication `*` mechanics
result = giveMeFive()
In the example above, result = giveMeFive(); eventually becomes result = 5;.
Lesson 327The `return` statement flow
result = value >> n
In C, the syntax looks like this: result = value >> n; where n is how many seats everyone moves.
Lesson 184Right shift `>>` mechanics
result_a
It looks at result_a and result_b and insists they be compatible.
Lesson 209Type consistency in ternary branches
result_b
It looks at result_a and result_b and insists they be compatible.
Lesson 209Type consistency in ternary branches
result[i]
I better reload a[i] from memory every single loop iteration just in case the last write to result[i] changed it."* With restrict, the compiler can load values much more efficiently, knowing they won't change unexpectedly.
Lesson 915The `restrict` pointer qualifier
return (val1 - val2)
You will often see a shortcut for this logic: return (val1 - val2);.
Lesson 877Writing an integer comparison function
return 0
When you run this code, the computer enters the "Front Door" (main), grabs the "Printer" (stdio), shouts your message to the screen, and then closes the shop (return 0).
Lesson 17The 'Hello World' codeLesson 24The `return 0;` statementLesson 313Exiting the program with exit()Lesson 1072Process termination and exit codesLesson 1077Capturing child exit status
return a - b
A frequent pitfall is using simple subtraction for comparison, like return a - b;.
Lesson 882Common pitfalls in comparison function logic
Return Address
The CPU looks at the Return Address stored in that frame to find its way back to main and continue where it left off.
Lesson 347What is a Stack Frame?Lesson 350Return addresses in memory
return code
If they come back and say, "We can't make that dish," that is a return code.
Lesson 903When to use `errno` vs return codes
Return codes
Return codes are for flow control (did it work?).
Lesson 903When to use `errno` vs return codes
return n + sum(n - 1)
In the line return n + sum(n - 1);, the (n - 1) is the magic ingredient.
Lesson 355The Recursive Step
return value
Instead of crashing or leaving you guessing, fgets communicates this to you through its return value.
Lesson 707Checking for NULL return in fgetsLesson 1074Handling fork() return values
Return values
You might wonder: "Why not just provide one number for the total bytes?" While you could technically set size to 1 and count to the total bytes, splitting them provides a major advantage: Return values.
Lesson 727The size and count parameters
return_type (*name)(args)
A function pointer's declaration must mirror the target function's return type and arguments, with the pointer name wrapped in parentheses: return_type (*name)(args).
Lesson 514Syntax of function pointers
return_type (*pointer_name)(parameter_types)
return_type (*pointer_name)(parameter_types);
Lesson 514Syntax of function pointers
returns
The upward slope represents the returns as the functions finish.
Lesson 363Visualizing recursive depth
reusable
By separating concerns, you make your code reusable.
Lesson 1210Structuring the project into multiple `.c` files
reverse order
You can register multiple functions; they will run in the reverse order they were registered (the last one added is the first one executed).
Lesson 875Cleaning up at exit with `atexit`
rewind
If you want to hear that same song again, you don't throw the tape away and buy a new one; you simply hit the rewind button to spin the tape back to the beginning.
Lesson 723Rewinding a file to the start
rewind()
For instance, you might read a file once to count how many lines it has, rewind() to the start, and then read it a second time to actually process the content now that you know the size.
Lesson 723Rewinding a file to the startLesson 737Resetting with rewindLesson 758Creating temporary files with tmpfile
rewind(file_pointer)
Use rewind(file_pointer); to instantly reset your position to the beginning of a file and clear any end-of-file errors.
Lesson 723Rewinding a file to the start
rewind(filePointer)
Use rewind(filePointer); to instantly jump back to the beginning of a file and clear any end-of-file indicators.
Lesson 737Resetting with rewind
rewind(fptr)
When you call rewind(fptr), two important things happen:
Lesson 723Rewinding a file to the start
right
When you are standing at a node, the left pointer leads you to a whole new subtree where all the values are smaller, and the right pointer leads to a subtree where all the values are larger.
Lesson 78Assigning values with `=`Lesson 190Simple assignment `=`Lesson 1022Recursive tree node structureLesson 1026Pre-order and Post-order traversal
right subtree
In a BST, every single value in the left subtree must be smaller than the Root, and every single value in the right subtree must be larger than the Root.
Lesson 1023Properties of a Binary Search Tree
right-aligned
By default, the text is right-aligned (padded on the left).
Lesson 685Specifying field width for alignment
Right-Left Rule
To stay sane, we use the Right-Left Rule.
Lesson 926Reading declarations with the 'Right-Left' rule
right-to-left associativity
In C, the assignment operator (=) has right-to-left associativity.
Lesson 196Chained assignments `a = b = c`
round()
Use ceil() to round up, floor() to round down, and round() for standard mathematical rounding.
Lesson 864Rounding with `ceil`, `floor`, and `round`
row
The break successfully stopped the col loop for row 2, but the row loop kept right on ticking.
Lesson 305Breaking out of nested loops: the limitation of breakLesson 410Accessing elements using `[row][col]`
row = 2
The outer loop moves to row = 2, and the process repeats.
Lesson 298Introduction to loops inside loops
Row 3
If you run this, you will notice that even after the program finds (2,2) and hits the break, it immediately starts checking Row 3.
Lesson 305Breaking out of nested loops: the limitation of break
row of five lockers
Instead, you would ask for a row of five lockers right next to each other.
Lesson 383Declaring an array with `type name[size]`
row-major order
C stores multi-dimensional arrays in row-major order, meaning it lays out all elements of the first row, then the second, in one continuous line in memory.
Lesson 408Memory layout: Row-major order
Rows
In programming terms, we call these Rows and Columns.
Lesson 407Declaring 2D arrays: Rows and Columns
rowSum
By declaring rowSum or colSum inside the outer loop, you ensure each line starts with a clean slate.
Lesson 413Summing rows and columns individually
run input.txt
If your program requires command-line arguments (like filenames or flags), you provide them directly after the command, like run input.txt.
Lesson 827The `run` and `quit` commands
rwlock
You should use them when you have a high "read-to-write ratio." If your threads are writing just as often as they are reading, the extra complexity of the rwlock might actually slow your program down.
Lesson 1138Read-write locks basics

S

s + 1
Because according to the C standard, s + 1 is never allowed to overflow, so the compiler assumes it will always be greater than s.
Lesson 935Signed integer overflow vs Unsigned wrap
s with a single
This "flips" the mask, creating a row of 1s with a single 0 at our target position.
Lesson 188Common bitwise idioms
s1
Think of the dot as "belonging to." If you say s1.age = 20, you are telling C: "Go to the s1 folder and change the age that belongs to it."
Lesson 239Member access `.` and `->`
s1.age = 20
Think of the dot as "belonging to." If you say s1.age = 20, you are telling C: "Go to the s1 folder and change the age that belongs to it."
Lesson 239Member access `.` and `->`
sa_flags
sa_flags: Special options, like SA_RESTART, which tells the OS to resume interrupted system calls automatically.
Lesson 1088The sigaction struct and function
sa_handler
sa_handler: The pointer to your custom function.
Lesson 1088The sigaction struct and function
sa_mask
sa_mask: A set of signals to block while your handler is running (to prevent "signal overlap").
Lesson 1088The sigaction struct and function
SA_RESTART
sa_flags: Special options, like SA_RESTART, which tells the OS to resume interrupted system calls automatically.
Lesson 1088The sigaction struct and function
safe
That key opens a safe containing the gold (the actual data).
Lesson 505Accessing data through double dereference
safety during teamwork
The most common reason is safety during teamwork.
Lesson 493Pointer to a constant (`const int *p`)
SALES_TAX_RATE
Instead of using 0.07 everywhere, you create a symbol like SALES_TAX_RATE to represent it.
Lesson 118Literal vs. Symbolic constants
Salt
You might have one jar labeled Cinnamon and another labeled Salt.
Lesson 667Type safety concerns with enums
same
Use memmove whenever you are shifting data around within the same array.
Lesson 857Handling overlapping regions with `memmove`
same array
However, this is generally only safe and meaningful when both pointers point to elements within the same array.
Lesson 467Pointer comparison with `==` and `<`
same starting memory address
All members of a union share the same starting memory address.
Lesson 654Memory layout of a union
save.dat
When you write a struct this way, the resulting file (save.dat) isn't meant to be read by humans in a text editor like Notepad—it will look like gibberish.
Lesson 728Writing entire structs to disk
saveData()
Instead, you want one saveData() function that accepts a pointer to anything.
Lesson 510Implicit conversion to `void *`
saveFloat()
You don't want to write one function for saveInt(), another for saveFloat(), and another for saveStudent().
Lesson 510Implicit conversion to `void *`
saveInt()
You don't want to write one function for saveInt(), another for saveFloat(), and another for saveStudent().
Lesson 510Implicit conversion to `void *`
saveStudent()
You don't want to write one function for saveInt(), another for saveFloat(), and another for saveStudent().
Lesson 510Implicit conversion to `void *`
say_hello
The compiler looks at main.c, sees that it needs a function called say_hello, finds that function inside greetings.c, and stitches them together.
Lesson 59Compiling multiple source files
sayHello
Whether you use sayHello or the explicit "address of" operator &sayHello, C treats them as the same thing: a pointer to the code.
Lesson 325Writing your first custom functionLesson 515Taking the address of a function
sayHi()
If main() calls greet(), and greet() calls sayHi(), the stack grows upward.
Lesson 351Visualizing the stack during nested calls
sbrk
The program break is the boundary of your heap, and brk/sbrk are the system-level tools used to push that boundary forward to claim more memory.
Lesson 592Introduction to brk and sbrk
sbrk(0)
When you call sbrk(0), it doesn't move the fence at all; it just tells you where the fence is currently located.
Lesson 592Introduction to brk and sbrk
sbrk(increment)
This moves the "fence" by a relative amount. If you call sbrk(1024), you are asking the OS to move the fence 1024 bytes further away, growing your heap.
Lesson 592Introduction to brk and sbrk
scanf("%d")
While functions like scanf("%d") are smart enough to skip over whitespace to find a number, the character formatter—%c—is different.
Lesson 694Reading characters with ' %c' spacing
scansets
If you try to type "New York," it only captures "New." To fix this without jumping to more complex functions, we use scansets.
Lesson 698Using scansets with %[...]
Scientific Notation
In C, floating-point types (float and double) solve this using Scientific Notation.
Lesson 100Scientific notation in C
scope
Where you place your declaration determines its scope—the area of code where the function is "visible" and ready to be used.
Lesson 138Shadowing: Nested scope name clashesLesson 306Defining labels in C codeLesson 333Scope of function declarationsLesson 551Losing the last pointer to a block
scoping control
The most common reason is scoping control.
Lesson 769Removing definitions with `#undef`
score += 10
When you see score += 10;, your brain instantly recognizes that the score is growing.
Lesson 192Compound addition `+=`
score = 10
If you want to put the number 10 inside that box, you write score = 10;.
Lesson 190Simple assignment `=`
score > 10
If you use score > 10, the player who earns exactly 10 points would be very frustrated to find they haven't won yet!
Lesson 160Greater or equal `>=` and less or equal `<=`
score >= 10
These operators are the backbone of "range checking." If you are building a game where a player wins if they have 10 or more points, you use score >= 10.
Lesson 160Greater or equal `>=` and less or equal `<=`
scores = NULL
Notice the line scores = NULL; after the free command.
Lesson 988Freeing the dynamic array
scores[0]
If you accidentally try to access scores[0] later, your program might crash or behave unpredictably.
Lesson 402Finding the maximum value in an arrayLesson 988Freeing the dynamic array
scores[1]
Under the hood, when you write scores[1], C performs pointer arithmetic.
Lesson 986Accessing elements by index
scores[1] = 95
When you write scores[1] = 95;, the computer calculates the memory address of the second slot in the scores array and overwrites whatever was there with the number 95.
Lesson 387Modifying individual array elements
scores[1][2]
When you write scores[1][2], the computer does a little mental math.
Lesson 410Accessing elements using `[row][col]`
scores[3]
Because the computer knows the elements are in a perfect line, it doesn't have to "search" for scores[3].
Lesson 384Visualizing memory layout of arrays
scores[5]
Crucially, the condition i < 5 prevents the program from trying to access scores[5], which doesn't exist.
Lesson 400Printing array elements in a sequenceLesson 405Avoiding off-by-one errors in loops
scores[500]
Unlike some modern languages, C will not stop you if you try to access scores[500] on an array that only has 3 slots.
Lesson 986Accessing elements by index
scores[i]
Think of scores[i] as the box itself, while &scores[i] is the GPS coordinate of that box.
Lesson 390Reading array values from user input
SCREAMING_SNAKE_CASE
We use a style called SCREAMING_SNAKE_CASE.
Lesson 117Naming conventions for constants
SDL
You might write a perfect program, but because you are using a standard library (like stdio.h) or a graphics library (like SDL), Valgrind might flag memory management patterns inside those pre-compiled files.
Lesson 570Suppressing known tool warnings
SDL_
The OpenGL library prefixes everything with gl (e.g., glVertex3f), and the SDL library prefixes everything with SDL_.
Lesson 801Naming conventions for large projects
search_key
A robust lookup function will check if the key stored at that index actually matches the search_key you provided.
Lesson 1036Hash table lookup
second
The second number [10] tells you how many items are inside each list.
Lesson 407Declaring 2D arrays: Rows and Columns
SECONDS_IN_A_DAY
If they see SECONDS_IN_A_DAY, they understand instantly.
Lesson 115Defining constants with `#define`
secret_number
Now, when you run this program through a debugger (like GDB or LLDB), the tool can say, "You are currently on line 5, and the variable secret_number is equal to 42."
Lesson 1075Process duplication and copy-on-writeLesson 1160Compiling with `-g` for debug symbols
secret_stuff
The compiler now has two places to look: the current directory and the secret_stuff directory.
Lesson 63Header search paths
secretCode
When you set pointerB = pointerA, you aren't making a copy of the data (secretCode).
Lesson 460Multiple pointers to the same address
security gate with two locks
Think of it like a security gate with two locks.
Lesson 175Bitwise AND `&`
security guard
This is like a security guard who stays at their post as long as the building is open.
Lesson 321Choosing the right loop for the task
seed
If you provide the same starting point—called a seed—using srand(), the formula will spit out the exact same sequence of "random" numbers every time.
Lesson 886Why you should only seed onceLesson 888Getting a unique seed with `time(NULL)`
SEEK_CUR
SEEK_SET, SEEK_CUR, and SEEK_END define your starting point: the beginning, your current spot, or the end of the file.
Lesson 733Moving the pointer with fseekLesson 734The SEEK_SET, SEEK_CUR, SEEK_END constants
SEEK_END
Using these named constants makes your code "readable." When another programmer sees SEEK_END, they instantly know you are navigating relative to the end of the file, making your intentions clear without needing a comment.
Lesson 734The SEEK_SET, SEEK_CUR, SEEK_END constantsLesson 736Finding file size using seek and tellLesson 739Risks of seeking in text mode
SEEK_SET
Stick to constants: It is always safe to seek to the very beginning (SEEK_SET with offset 0) or the very end (SEEK_END with offset 0).
Lesson 733Moving the pointer with fseekLesson 734The SEEK_SET, SEEK_CUR, SEEK_END constantsLesson 739Risks of seeking in text mode
Segmentation Fault
In C, if your code tries to assign a new value to a memory address protected by PROT_READ, the OS triggers a Segmentation Fault.
Lesson 595Memory protection constants (PROT_READ)Lesson 1209Error handling for missing files
Segmentation fault (core dumped)
Once enabled, if your program crashes, you’ll see a message like Segmentation fault (core dumped).
Lesson 352Identifying a Stack OverflowLesson 837Debugging a Segfault from a core dump
self-referential structure
This leads to a unique C programming concept called a self-referential structure.
Lesson 989Defining the self-referential node struct
self-referential structures
In C, we use self-referential structures to do exactly this.
Lesson 626Self-referential structs for linked lists
Semaphores
To solve this in C, we use two tools: Mutexes (to ensure only one thread touches the rack at a time) and Semaphores (to track how many spots are full or empty).
Lesson 1136The producer-consumer problem
semicolon
When you declare multiple variables on one line, you state the data type once, followed by your chosen names separated by commas, and ending with a single semicolon.
Lesson 75The syntax of a declarationLesson 76Multiple declarations in one lineLesson 221Sequence point definition
send()
Use send() to push byte arrays out and recv() to pull them in, always checking the return value to see how much data actually moved.
Lesson 1115Sending and receiving over sockets
Sensor
The rest of your program, which just passes the Sensor struct around, doesn't need to change at all.
Lesson 674Information hiding using void pointers
sensor.c
We tell the compiler: "When building the test, don't use the real sensor.c file; use my mock_sensor.c file instead."
Lesson 1176Mocking simple dependencies
sequence point
It discards the result of the left expression, moves past a sequence point, and evaluates the expression on the right.
Lesson 222Sequence points at `;`Lesson 223Sequence points in logic `&&` and `||`Lesson 224The comma operator `,`Lesson 226Function call sequence points
sequence points
To keep things sane, the language uses sequence points.
Lesson 937Sequence point violations
Sequential access
You use sequential access for most tasks, such as reading a configuration file or processing a list of names.
Lesson 720Sequential vs random access concepts
SERVER_ERROR
In the example above, because SERVER_ERROR was set to 500, the compiler automatically assigned UNKNOWN the next integer in sequence: 501.
Lesson 663Explicitly assigning enum values
server_fd
You can keep the client_fd in a list of active customers while your main loop goes back to accept() on the server_fd to wait for the next person in line.
Lesson 1113Accepting client connections
setbuf
What you'll learn: How to use the simplified setbuf function to quickly toggle between fully buffered and unbuffered output.
Lesson 753The setbuf shorthand
setup()
Safety: You can reuse common names like setup() or clean_up() in every single file of your project without them ever bumping into each other.
Lesson 798Static functions for file scoping
setvbuf
Alternatively, if you were writing a massive 1GB file, you might create a large char array of 64KB and pass it to setvbuf with _IOFBF to make the process much faster by reducing the number of hardware writes.
Lesson 752Setting custom buffers with setvbufLesson 753The setbuf shorthand
seven days
If you are printing a calendar, the outer loop moves through the weeks, and for every week, the inner loop moves through the seven days.
Lesson 299Inner loop vs outer loop execution order
shallow copy
If you perform a shallow copy (Person2 = Person1), C simply copies the memory address stored in the pointer.
Lesson 604Copying structs with the assignment operatorLesson 629Deep vs shallow copies of nested structs
shared global variables
Use volatile whenever you are dealing with memory-mapped I/O, interrupt service routines, or shared global variables in multi-threaded applications where a value might change without the compiler seeing it happen in the current code block.
Lesson 914How `volatile` prevents compiler optimization
shared libraries
This is fine for a standalone program, but it creates a massive headache for shared libraries.
Lesson 814Position Independent Code `-fPIC`
shared library
Unlike a static library, which gets physically shoved inside your program's executable file, a shared library stays separate.
Lesson 813What is a shared library `.so` / `.dll`
Shared memory
Shared memory accessed by multiple processors.
Lesson 913The `volatile` qualifier for hardware mapping
shared_cookies
Only after that change does it assign the value to shared_cookies.
Lesson 198Prefix increment `++x`
Shifting (<< or >>)
Shifting (<< or >>): To move that byte to its new position.
Lesson 953Manual byte swapping techniques
should never happen
You use assert to check for things that should never happen if your logic is correct.
Lesson 901Using `assert` for internal debugging
SHRT_MAX
The maximum value for a short.
Lesson 88Minimum and maximum values
SHRT_MIN
C provides these limits in a library called <limits.h>, which gives us nicknames like INT_MAX (the largest possible int) or SHRT_MIN (the smallest possible short).
Lesson 132Safe downcasting techniques
side effect
This change to the environment is called a side effect.
Lesson 220Definition of a side effect
side effects
It guarantees that all side effects (like increments or assignments) from the left side are fully completed and saved to memory before it even looks at the right side (B).
Lesson 223Sequence points in logic `&&` and `||`Lesson 227Order of evaluation vs PrecedenceLesson 775Side effects in macro arguments
sig
The signal constant (like SIGTERM to ask it to stop, or SIGUSR1 for a custom message).
Lesson 1086Sending signals with kill()
SIG_BLOCK
How: Usually SIG_BLOCK (to add to the current mask) or SIG_UNBLOCK (to remove).
Lesson 1090Blocking signals with sigprocmask()
SIG_UNBLOCK
How: Usually SIG_BLOCK (to add to the current mask) or SIG_UNBLOCK (to remove).
Lesson 1090Blocking signals with sigprocmask()
sigaction
The sigaction struct provides a portable and reliable way to define complex signal-handling behavior that won't reset or glitch under pressure.
Lesson 1088The sigaction struct and function
sigaction()
To use it, you create the struct, fill it with your preferences, and pass it to the sigaction() function.
Lesson 1088The sigaction struct and function
SIGALRM
When you call alarm(5), you are telling the Operating System: "Hey, let me go back to my work, but please poke me in exactly five seconds." When the time is up, the OS sends a SIGALRM signal to your process.
Lesson 1091Handling alarms with alarm()
SIGFPE
The system detecting a math error, like dividing by zero (SIGFPE).
Lesson 1084What are Unix signals
SIGINT
The Registration: By calling signal(SIGINT, handle_sigint), you are telling the kernel: "If you send me a SIGINT, don't kill me immediately.
Lesson 1084What are Unix signalsLesson 1085Common signals: SIGINT, SIGTERM, SIGKILLLesson 1087Basic signal handling with signal()Lesson 1090Blocking signals with sigprocmask()
SIGKILL
Use SIGINT and SIGTERM for graceful shutdowns, and reserve SIGKILL for emergencies when a process refuses to die.
Lesson 1085Common signals: SIGINT, SIGTERM, SIGKILL
sign and the type specifier (like
To set a field width, you place a number between the % sign and the type specifier (like d for integers).
Lesson 56Basic field width formatting
signal
If you have ten threads waiting for a "Go!" signal and you use signal, nine of them will keep sleeping while the winner starts working.
Lesson 1084What are Unix signalsLesson 1135Broadcasting to all threads
signal = 0
Readability: signal = RED is much easier to understand than signal = 0.
Lesson 664Enums vs constant integers
signal = RED
Readability: signal = RED is much easier to understand than signal = 0.
Lesson 664Enums vs constant integers
Signal Name
To send a signal, you need two pieces of information: the PID (Process ID) of the target and the Signal Name (the type of message).
Lesson 1086Sending signals with kill()
signal()
The signal() function allows you to redirect system interrupts to custom functions, giving your program a chance to respond to events rather than simply being terminated.
Lesson 1087Basic signal handling with signal()Lesson 1088The sigaction struct and function
signals
It is simply the primary tool used to send signals—small notifications—between processes.
Lesson 1086Sending signals with kill()Lesson 1087Basic signal handling with signal()
signature
To point to a function, your pointer must exactly match the function's signature (its return type and its parameters).
Lesson 514Syntax of function pointers
signed
If you are writing a program where some numbers must be positive (unsigned) and others must allow negatives, using the signed keyword side-by-side with unsigned makes your code much easier to read and less prone to mistakes.
Lesson 89The `signed` keywordLesson 94Format specifiers for unsigned intsLesson 111Signed vs. Unsigned charsLesson 649Signed vs unsigned bit-fieldsLesson 846The importance of casting to `unsigned char` in `ctype` functionsLesson 945The significance of `char` signness
signed char
Magnitude Overflow: If you try to force a large number into a type that physically cannot hold a value that high, the bits "wrap around." For example, trying to fit the number 300 into a signed char (which usually only goes up to 127) will result in a weird, often negative number.
Lesson 111Signed vs. Unsigned charsLesson 125Risks of narrowing conversionsLesson 157Basic arithmetic overflowLesson 945The significance of `char` signness
signed int
However, if you declare it as a signed int, C uses the leftmost bit as a "sign bit" to track whether the number is positive or negative.
Lesson 89The `signed` keywordLesson 646Syntax for declaring bit-fieldsLesson 647Restrictions on bit-field typesLesson 649Signed vs unsigned bit-fields
signed integer
Think of a signed integer like a thermometer.
Lesson 95When to choose unsigned over signed
Signed integers
Signed integers (like a standard int) are different.
Lesson 935Signed integer overflow vs Unsigned wrap
signpost
Instead of building a house, you have created a signpost.
Lesson 484Memory layout of string pointers
sigprocmask
Once sigprocmask unblocks the signal, the kernel checks for pending signals and triggers the handler immediately.
Lesson 1090Blocking signals with sigprocmask()
sigprocmask()
Use sigprocmask() to protect sensitive code from interruptions by putting signals on hold until your work is done.
Lesson 1090Blocking signals with sigprocmask()
SIGSEGV
The program trying to access memory it doesn't own (SIGSEGV).
Lesson 1084What are Unix signals
sigset_t
To block signals, we use a sigset_t variable to hold a list of signals we want to mask.
Lesson 1090Blocking signals with sigprocmask()
SIGTERM
Use SIGINT and SIGTERM for graceful shutdowns, and reserve SIGKILL for emergencies when a process refuses to die.
Lesson 1084What are Unix signalsLesson 1085Common signals: SIGINT, SIGTERM, SIGKILL
silk shirt
If you later decide to shove a silk shirt into that same tiny space, the boots are tossed out.
Lesson 659The danger of reading the wrong union member
sin
What you'll learn: How to use the sin, cos, and tan functions from the math library and why you must convert degrees to radians first.
Lesson 863Trigonometric functions in radians
sin_addr
In this example, sin_family, sin_port, and sin_addr are the specific fields within the structure.
Lesson 1109The sockaddr_in structure
sin_family
In this example, sin_family, sin_port, and sin_addr are the specific fields within the structure.
Lesson 1109The sockaddr_in structure
sin_port
In this example, sin_family, sin_port, and sin_addr are the specific fields within the structure.
Lesson 1109The sockaddr_in structure
sin()
C's trigonometric functions require angles in radians, so always multiply degrees by (PI / 180.0) before passing them to sin(), cos(), or tan().
Lesson 863Trigonometric functions in radians
sin(90)
If you try to calculate the sine of 90 degrees by writing sin(90), C won't give you 1.0.
Lesson 863Trigonometric functions in radians
single
Single quotes ('A') are for a single character.
Lesson 416Defining strings with double quotes
single character
%c: Used for a single character (like 'A' or '$').
Lesson 49Introduction to Format Specifiers
single marble
Think of a single character literal (using single quotes) as a single marble.
Lesson 418Difference between `'a'` and `"a"`
Singly Linked List
In a Singly Linked List, those doors are one-way turnstiles.
Lesson 999Updating the node struct
Size - 1
To traverse an array backwards, initialize your loop at size - 1, decrement the index, and continue until you have processed index 0.
Lesson 386Accessing elements with the `[]` operatorLesson 401Reverse traversal of an array
Size (sizeof)
Size (sizeof): This tells you how much memory the array occupies.
Lesson 423Getting length with `strlen`
size < capacity
Always verify that size < capacity before adding an element; if they are equal, you must resize the array to prevent memory corruption.
Lesson 983Pushing elements and capacity checks
size <= 1
In our doll example, the base case is size <= 1.
Lesson 353Concept of self-calling functions
size == capacity
When you need to resize the array, you can check if size == capacity.
Lesson 981Structure for dynamic arrays
size_t n
size_t n: This is the most important part.
Lesson 512The `memcpy` function signature
size_t offset
In an arena allocator, you typically have a large char buffer and a size_t offset that tracks where the next allocation starts.
Lesson 589Handling alignment within an arena
size_t size
The input, size_t size, is the only information malloc asks for.
Lesson 537The malloc function signature
sizeof myVariable
You might see some programmers use sizeof without parentheses when they are measuring a variable (like sizeof myVariable).
Lesson 235The `sizeof` operator with types
sizeof()
Never assume the size of a struct is simply the sum of its parts; always use sizeof() to let the compiler tell you the truth for that specific architecture.
Lesson 85Platform dependency of sizesLesson 420Length vs Size of a string arrayLesson 644Platform dependency of struct sizeLesson 647Restrictions on bit-field typesLesson 949Writing code for 32-bit vs 64-bit
sizeof(age)
In this code, sizeof(age) looks at the variable age, sees that it is an integer, and returns the number of bytes your system uses for integers (usually 4).
Lesson 236The `sizeof` operator with variables
sizeof(arr) / sizeof(arr[0])
To find the number of elements in an array, divide the total size of the array by the size of its first element: sizeof(arr) / sizeof(arr[0]).
Lesson 388Calculating array size with `sizeof`
sizeof(array) / sizeof(array[0])
By using the sizeof(array) / sizeof(array[0]) formula, your code stays flexible.
Lesson 388Calculating array size with `sizeof`
sizeof(city)
In this example, sizeof(city) will be 20, because that is how much room you requested.
Lesson 420Length vs Size of a string array
sizeof(destination) - strlen(destination) - 1
Always calculate your limit as sizeof(destination) - strlen(destination) - 1.
Lesson 436Using `strncat` for safer concatenation
sizeof(int)
In C, if you want to save an array of 5 integers to a file, the "size" is the size of one integer (sizeof(int)), and the "count" is 5.
Lesson 235The `sizeof` operator with typesLesson 537The malloc function signatureLesson 725Writing raw bytes with fwriteLesson 727The size and count parameters
sizeof(matrix)
If you have int matrix[5][10], sizeof(matrix) will not return 50 (the total number of integers).
Lesson 931The `sizeof` operator with complex types
sizeof(name)
The maximum capacity: sizeof(name) tells C to read no more than 19 characters (leaving 1 spot for the null terminator \0).
Lesson 433Safe input reading with `fgets`
sizeof(numbers)
When used with the sizeof operator: sizeof(numbers) returns the size of the entire block, not the size of a pointer.
Lesson 475Array decay explained
sizeof(Resource)
Even though a char is 1 byte and an int is 4 bytes, sizeof(Resource) will likely return 8 bytes, not 5.
Lesson 573Struct padding for alignment
sizeof(struct Data)
However, if you run sizeof(struct Data), you will likely see 12 bytes.
Lesson 638Understanding memory alignment
sizeof(struct FastFood)
Even though you only defined 6 bytes of data (1 + 4 + 1), sizeof(struct FastFood) will likely return 12 bytes.
Lesson 640Structure holes and performance
sizeof(struct Node)
Use malloc with sizeof(struct Node) to carve out space in memory for each new link in your list.
Lesson 991Allocating a new node in memory
sizeof(struct Player)
By passing sizeof(struct Player) as the size, you tell C exactly how big one "unit" of data is.
Lesson 727The size and count parameters
sizeof(struct YourStruct)
Use malloc with sizeof(struct YourStruct) to create persistent data on the heap, and always access its members using the arrow (->) operator.
Lesson 620Allocating structs on the heap with malloc
sizeof(struct)
You don't usually have to manage alignment manually, but understanding it explains why sizeof(struct) is often larger than the sum of its parts.
Lesson 643Alignment requirements for different typesLesson 947Structure padding and alignment issues
sizeof(type)
By using void*, we tell the compiler: "I'm going to give you a memory address, but don't worry about what type of data is stored there yet." By passing sizeof(type), we tell the function exactly how many bytes to grab.
Lesson 235The `sizeof` operator with typesLesson 513Implementing a generic swap functionLesson 538Calculating size with sizeof
sizeof(username)
In the example above, sizeof(username) would be 20 because the array was declared with 20 slots.
Lesson 423Getting length with `strlen`
sketchbook
Think of this like writing in a sketchbook.
Lesson 480Mutable vs immutable string memory
sleep
After the sleep command—during which the parent has already exited—the second call to getppid() will typically return 1.
Lesson 1079Handling orphaned processes
sleep(100)
If you removed the wait(NULL) line and told the parent to sleep(100), the child would remain a zombie for 100 seconds.
Lesson 1078Preventing zombie processes
sleep(5)
If you put a sleep(5) command in the middle of that code, the Wall Time would increase by five seconds, but the cpu_time_used would remain almost exactly the same.
Lesson 1184Understanding CPU cycles vs. Wall time
slip of paper
You have a slip of paper (the double pointer).
Lesson 505Accessing data through double dereference
smallBox
Even worse, because a short usually only goes up to 32,767, assigning 40000 to it causes an overflow, resulting in smallBox becoming -25536.
Lesson 125Risks of narrowing conversions
smaller
Right Scout: Starts at the end and moves left until it finds a value smaller than the pivot.
Lesson 1056Quick Sort: Partitioning logic
smart_print(age)
In the code above, the compiler looks at smart_print(age).
Lesson 977Type-based function overloading simulation
snake_case
In C, the two most common styles are snake_case (all lowercase with underscores) and camelCase (capitalizing the first letter of each word except the first).
Lesson 74Naming rules and identifiersLesson 1197Meaningful variable naming conventions
snprintf
If the formatted text is too long, snprintf simply cuts it off (truncates it) and ensures the string still ends with a proper null terminator (\0).
Lesson 704Safe string formatting with snprintf
so that
Rather than letting b start at byte 1 (which is "misaligned"), it adds three invisible bytes after a so that b starts at byte 4.
Lesson 639Why the compiler adds padding bytes
SOCK_DGRAM
By changing AF_INET to AF_UNIX or SOCK_STREAM to SOCK_DGRAM, you completely change how your program interacts with the world without changing the rest of your logic.
Lesson 1108Socket domains and typesLesson 1110Creating a socket with socket()
SOCK_DGRAM (UDP)
Think of this like sending postcards. Each packet (datagram) is independent. They might arrive out of order, or not at all. It is incredibly fast, making it perfect for video streaming or gaming where speed matters more than perfect reliability.
Lesson 1108Socket domains and types
SOCK_STREAM
By changing AF_INET to AF_UNIX or SOCK_STREAM to SOCK_DGRAM, you completely change how your program interacts with the world without changing the rest of your logic.
Lesson 1108Socket domains and typesLesson 1110Creating a socket with socket()
SOCK_STREAM (TCP)
Think of this like a phone call. You establish a dedicated connection, and data arrives in the exact order it was sent. If a packet gets lost, the system automatically asks for a retry. It is reliable but has a little bit of overhead.
Lesson 1108Socket domains and types
sockaddr
While the operating system uses a generic structure called sockaddr for all types of communication (like Bluetooth or internal files), we use the specialized sockaddr_in specifically for IPv4 internet networking.
Lesson 1109The sockaddr_in structure
sockaddr_in
While the operating system uses a generic structure called sockaddr for all types of communication (like Bluetooth or internal files), we use the specialized sockaddr_in specifically for IPv4 internet networking.
Lesson 1109The sockaddr_in structureLesson 1111Binding to a port with bind()
socket file descriptor
Both functions require three main ingredients: the socket file descriptor (the ID of your connection), a buffer (the actual data), and the length of that data.
Lesson 1115Sending and receiving over sockets
socket()
The socket() function initializes a network endpoint and returns a file descriptor that you will use for all future networking tasks.
Lesson 1108Socket domains and typesLesson 1110Creating a socket with socket()Lesson 1111Binding to a port with bind()
sort<T>
They allow you to write a single function, like sort<T>, and the compiler automatically generates a new version for every data type you use.
Lesson 978Comparing `_Generic` to C++ templates
sorted
It treats the array as two parts: a sorted section on the left and an unsorted section on the right.
Lesson 1048Selection Sort: Finding the minimumLesson 1064Importance of sorted data
sorting algorithms
This is the secret sauce behind advanced C features like sorting algorithms.
Lesson 518Passing functions as arguments
source
The function takes two arguments: the destination (where the text is going) and the source (where the text is coming from).
Lesson 424Copying strings with `strcpy`
Source Code
The Source Code is your English recipe (your .c file).
Lesson 6C as a compiled languageLesson 9Role of the Compiler
Source File
The source file (ending in .c) is the kitchen; it contains the definitions, which is the actual logic and code that performs the work.
Lesson 15The concept of a Source FileLesson 382Sharing functions across modulesLesson 788The purpose of header filesLesson 824Incremental builds and file timestamps
Source file (.c)
The Source file (.c) is the chef in the kitchen.
Lesson 796Splitting code into `.c` and `.h`
Source Files
To stay organized, C programmers split code into two specific types of files: Source files (.c) and Header files (.h).
Lesson 378Separating interface from implementationLesson 796Splitting code into `.c` and `.h`
space
The most important thing to remember is space.
Lesson 425Concatenating strings with `strcat`
spaghetti code
This is why programmers call messy, unorganized logic spaghetti code.
Lesson 311The dangers of 'spaghetti code'
specifier (like
Add a number inside your % specifier (like %10d) to set a minimum width, creating "invisible columns" for clean, professional output.
Lesson 685Specifying field width for alignment
speed
In the example above, if emergency_system() runs, every other function using speed is suddenly affected.
Lesson 142The dangers of global variablesLesson 583Allocation strategies: First-fit
sprintf
sprintf allows you to format text and store the result in a string variable for later use, rather than sending it directly to the output console.
Lesson 703Formatting strings in memory with sprintfLesson 704Safe string formatting with snprintf
sqrt
Before calling a library function (like strtol or sqrt), wipe the slate clean.
Lesson 900Resetting `errno` before library calls
sqrt()
If you try to pass a negative value to sqrt(), your program might return "NaN" (Not a Number).
Lesson 861Basic power and square root: `pow` and `sqrt`
sqrt(16.0)
It asks the question: "What number, multiplied by itself, gives me this value?" For example, sqrt(16.0) will return 4.0.
Lesson 861Basic power and square root: `pow` and `sqrt`
sqrt(x)
Use pow(x, y) for exponents and sqrt(x) for square roots, ensuring you include <math.h> and use double data types for precision.
Lesson 861Basic power and square root: `pow` and `sqrt`
square
It feels like extra work now, but unit tests are your "safety net." If you decide to optimize your square function later to make it faster, you can re-run your tests instantly.
Lesson 347What is a Stack Frame?Lesson 348Pushing and popping framesLesson 1172Principles of Unit Testing
square_double()
You end up writing square_int(), square_float(), and square_double().
Lesson 973Introduction to the `_Generic` keyword
square_float()
You end up writing square_int(), square_float(), and square_double().
Lesson 973Introduction to the `_Generic` keyword
square_int()
You end up writing square_int(), square_float(), and square_double().
Lesson 973Introduction to the `_Generic` keyword
square()
Once square() finishes, its frame—and every variable inside it—is "popped" and gone forever.
Lesson 348Pushing and popping frames
SQUARE(++i)
If you pass it something weird—like a variable with a side effect (SQUARE(++i))—the stamp repeats that side effect twice, often breaking your logic.
Lesson 778Macros vs inline functions
SQUARE(2 + 2)
Because macros are just text substitution, they can be "tricky." If you defined it as #define SQUARE(x) x x and then wrote SQUARE(2 + 2), the preprocessor would give you 2 + 2 2 + 2.
Lesson 772Defining function-like macros
SQUARE(a++)
However, the preprocessor expands SQUARE(a++) into ((a++) * (a++)).
Lesson 775Side effects in macro arguments
square(x)
When square(x) is called, the computer pauses main and creates a new frame for square.
Lesson 347What is a Stack Frame?
srand()
If we give srand() a different number every time the program starts, rand() will start at a different point in its list, giving us a different result.
Lesson 885Seeding the generator with `srand`Lesson 886Why you should only seed onceLesson 888Getting a unique seed with `time(NULL)`
srand(1)
If you write srand(1);, your game or simulation will be identical every single time it starts.
Lesson 888Getting a unique seed with `time(NULL)`
srand(time(NULL))
When you call srand(time(NULL)), you are telling the computer: "Look at the clock, take the current second, and use that number to scramble the starting point of your random formula."
Lesson 885Seeding the generator with `srand`Lesson 886Why you should only seed onceLesson 888Getting a unique seed with `time(NULL)`
src
You might have a src folder for your code and an include folder for your headers.
Lesson 63Header search paths
src/main.c
Here is how the top of src/main.c would look:
Lesson 800Organizing /src and /include folders
sscanf
A common "best practice" is to read an entire line of input into a string first (using a function like fgets), validate that the string isn't empty or broken, and then use sscanf to parse the actual data.
Lesson 705Parsing data from strings with sscanf
St
The name stands for Standard Input/Output, and the .h indicates it is a "header" file.
Lesson 18The `#include` directiveLesson 678The header file <stdio.h>
ST_
By adding TL_ and ST_, you’ve created unique names.
Lesson 666Scoped enum limitations in C
ST_RED
Now the "living room" has a TL_RED lamp and an ST_RED lamp, and the compiler can easily tell them apart.
Lesson 666Scoped enum limitations in C
stability
For professional developers, C17 is actually one of the most important versions because it represents stability.
Lesson 968C17: The bug-fix standard
stable
A stable sorting algorithm guarantees that if two items have the same key (the value you are sorting by), their relative order remains unchanged.
Lesson 1052Stability in sorting algorithms
stack trace
Below that, Valgrind provides a stack trace, showing the specific function and line number where the trespass occurred.
Lesson 568Finding invalid reads and writes
Stack Underflow
A Stack Underflow occurs when your code calls a pop operation (removing the top item) on a stack that contains no data.
Lesson 1013Handling Stack Underflow
stack[-1]
Without the if (isEmpty()) check, the line stack[top] would try to access stack[-1].
Lesson 1013Handling Stack Underflow
stack[top]
Without the if (isEmpty()) check, the line stack[top] would try to access stack[-1].
Lesson 1013Handling Stack Underflow
Standard Headers
When we want to use tools that are built into the C language itself (like the ability to print text to the screen), we use Standard Headers.
Lesson 765The `#include` directive for standard headersLesson 795Standard header search paths
Standard Input
The name stands for "get character." It is the simplest way to interact with the Standard Input (stdin), which is usually your keyboard.
Lesson 680Basic character input with getchar
Standard Input and Output
The name looks a bit cryptic, but it is just shorthand for Standard Input and Output.
Lesson 41The `stdio.h` library
Standard Input/Output
The name stands for Standard Input/Output.
Lesson 19What is a Header File?
Standard integers
Standard integers (int) are the baseline.
Lesson 229Usual arithmetic conversions
Standard Libraries
The Linker searches through Standard Libraries (pre-written collections of code) to find the implementation of printf.
Lesson 28Phase 4: The Linker
standard output
The name putchar stands for "put character." It takes exactly one character and sends it to the standard output (usually your monitor).
Lesson 679Basic character output with putchar
Standards
In programming, these updates are called Standards.
Lesson 4Standards: ANSI C vs C99 vs C11
Starship
If you define the Pilot struct first, the compiler will complain because it hasn't seen the Starship struct yet.
Lesson 628Forward declarations of structs
start
We swap the characters at these positions using a temporary variable, increment start, decrement end, and repeat until they cross paths.
Lesson 20The `main()` function entry pointLesson 440Reversing an array in placeLesson 441Reversing a string in placeLesson 836Using `watch` for memory changes
start < end
In the code above, the condition start < end is vital.
Lesson 442Checking if a string is a palindrome
Start at the identifier
Start at the identifier and say its name.
Lesson 926Reading declarations with the 'Right-Left' rule
starting rule
While sizeof tells you the size, alignof tells you the starting rule.
Lesson 572The alignof operator
State
Instead, think of printf() as leaving a trail of breadcrumbs that tell you two specific things: State and Flow.
Lesson 1157Strategic `printf()` debugging
statement
In programming terms, we call a single instruction a statement.
Lesson 22Semicolons as statement terminators
statement would look for a
Without the !, the if statement would look for a true value and skip the warning.
Lesson 249Logical NOT (!) for inversion
static char
static char becomes '\0' (the null character)
Lesson 148Default initialization of static variables
static inline
If you put a massive, complex function as a static inline in a header, your final program size will bloat because that massive code block is being duplicated everywhere you use it.
Lesson 368Inline functions in header files
static library
In C programming, a static library (ending in .a for "archive" on Linux/macOS or .lib on Windows) is that toolbox.
Lesson 810What is a static library `.a`Lesson 812Linking with static librariesLesson 813What is a shared library `.so` / `.dll`
static memory pool
A static memory pool is like a buffet tray already sitting on your table.
Lesson 580Managing a static memory pool
statically typed
C is a statically typed language, which means it is very picky about the "shape" of the data you pass in.
Lesson 338Type checking in function calls
status
If we removed volatile from the code above, a high-level optimization might see while (status == 0) and think: "Since status is 0 and nothing in this loop changes it, I'll just treat this as while (true)." Your program would then hang forever, even if the memory at the status address changed to 1.
Lesson 165Common pitfall: `=` vs `==`Lesson 914How `volatile` prevents compiler optimizationLesson 1197Meaningful variable naming conventions
status = status | mask
Using status |= mask instead of status = status | mask makes your code cleaner and reduces the chance of typos, especially when your variable names are long (like player_attribute_flags).
Lesson 195Compound bitwise assignments
status |= mask
Using status |= mask instead of status = status | mask makes your code cleaner and reduces the chance of typos, especially when your variable names are long (like player_attribute_flags).
Lesson 195Compound bitwise assignments
status.dayOfWeek
You can treat status.dayOfWeek just like a normal variable in your code, but if you try to assign the number 10 to it, it will overflow because 10 (binary 1010) requires 4 bits, and you only allocated 3.
Lesson 648The colon operator and bit width
std::vector
When you use a dynamic array (like a std::vector in C++ or your own malloc implementation), you eventually run out of space.
Lesson 985Amortized time complexity
stdarg.h
Variadic functions use ... and the stdarg.h library to accept any number of arguments, provided you have at least one fixed parameter to start the process.
Lesson 371Functions with unknown argumentsLesson 372The `stdarg.h` libraryLesson 376How `printf` works internally
stderr
C uses stdin for input, stdout for normal output, and stderr for error messages to keep communication organized and separable.
Lesson 677Introduction to stdin, stdout, and stderrLesson 750Unbuffered output (stderr)Lesson 761Standard stream redirection in shells
STDERR_FILENO
In C, these streams are represented by the constants STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO.
Lesson 1093Standard streams (0, 1, 2)
stdin
The freopen function redirects a standard stream (like stdin or stdout) to a file, allowing standard functions like scanf and printf to read from or write to files automatically.
Lesson 433Safe input reading with `fgets`Lesson 677Introduction to stdin, stdout, and stderrLesson 701Reading safe strings with fgetsLesson 760Redirecting streams with freopenLesson 761Standard stream redirection in shells
STDIN_FILENO
In C, these streams are represented by the constants STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO.
Lesson 1093Standard streams (0, 1, 2)
stdint.h
Use limits.h to check the boundaries of standard types and stdint.h to define variables with specific size requirements, ensuring your code runs safely on everything from a toaster to a supercomputer.
Lesson 942Limits of `limits.h` and `stdint.h`
stdio
When you run this code, the computer enters the "Front Door" (main), grabs the "Printer" (stdio), shouts your message to the screen, and then closes the shop (return 0).
Lesson 17The 'Hello World' code
stdlib.h
The functions defined in stdlib.h—primarily malloc, calloc, realloc, and free—act as the bridge between your code and the computer's Operating System.
Lesson 536Header file stdlib.h for allocation
STDOUT_FILENO
In C, these streams are represented by the constants STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO.
Lesson 1093Standard streams (0, 1, 2)
step
Using the recipe analogy, if the instruction says "Make the secret sauce," and you use step, you are now looking at a separate page that explains how to mix the mayo, mustard, and spices.
Lesson 830Stepping through code with `next` and `step`
Step Over
Click Step Over, and you’ll see sum instantly change to 1.
Lesson 1162Setting breakpoints and stepping through code
sticky note
When you open an envelope and follow that address, you find a sticky note with a word written on it (that’s the second pointer, leading to a character).
Lesson 506Command line arguments `char **argv`
stone tablet
A const variable is more like a stone tablet.
Lesson 910The `const` qualifier on variables
str
mem functions outperform str functions because they skip the "null-terminator check" and allow the CPU to move data in large, optimized chunks.
Lesson 847Finding string length with `strlen`Lesson 860Performance differences between `str` and `mem` functions
str_utils.c
Let's say you have two utility files, math_utils.c and str_utils.c.
Lesson 811Creating archives with the `ar` tool
str2 = str1
If you have char str1[] = "Hello"; and char str2[10];, writing str2 = str1; will cause a compiler error.
Lesson 424Copying strings with `strcpy`
strcat
What you'll learn: How to join two strings together by appending the contents of one to the end of another using the strcat function.
Lesson 425Concatenating strings with `strcat`Lesson 436Using `strncat` for safer concatenationLesson 849Concatenating strings with `strncat`
strchr
Use strchr to find the memory address of the first occurrence of a character, returning NULL if the character doesn't exist.
Lesson 427Searching for characters with `strchr`Lesson 851Searching for characters with `strchr` and `strrchr`Lesson 859Searching memory bytes with `memchr`
strcmp()
When searching for strings instead of integers, remember to use strcmp() from the <string.h> library rather than ==, but the logic of looping through the array remains identical.
Lesson 636Searching through struct arrays
strcoll
Use strcoll for accurate, language-sensitive comparisons, and use strxfrm to pre-process strings for high-performance sorting in global applications.
Lesson 854Understanding the `strcoll` and `strxfrm` locale functions
strcpy
If the source is too long, strcpy will keep writing data into memory it doesn't own, causing a "buffer overflow." This often leads to program crashes or serious security vulnerabilities.
Lesson 424Copying strings with `strcpy`Lesson 435Using `strncpy` for safer copyingLesson 561Heap buffer overflowsLesson 848Copying strings safely with `strncpy`Lesson 860Performance differences between `str` and `mem` functions
strcspn
If the user types a very long string that fills the buffer without a newline, strcspn will simply return the index of the existing null terminator, and you'll be replacing a \0 with a \0—which does no harm!
Lesson 434Removing newlines from `fgets` resultsLesson 702Removing the newline from fgets
strdup
However, if your struct contains pointers to other blocks of memory (like a string created with strdup or another dynamic array), the struct becomes like a Russian nesting doll.
Lesson 621Freeing dynamically allocated structs
stream
Instead, C uses an abstraction called a stream.
Lesson 676The concept of a stream in C
streams
In C, these pathways are called streams.
Lesson 677Introduction to stdin, stdout, and stderr
street address for a recipe
Think of a function name as a street address for a recipe.
Lesson 515Taking the address of a function
strength
When levelUp(heroStrength) is called, the value 50 is plucked out of main's memory and pasted into a new memory location labeled strength inside levelUp.
Lesson 344Visualizing the stack frame copy
strerror
Just read it, print it, or copy it if you need to save it for later, as subsequent calls to strerror might overwrite the buffer in some implementations.
Lesson 740Introduction to errnoLesson 742The strerror functionLesson 899Getting error strings with `strerror`
strerror(errno)
Use strerror(errno) to turn confusing numeric system errors into readable strings that tell you exactly what went wrong.
Lesson 742The strerror functionLesson 899Getting error strings with `strerror`
strftime
Instead of building strings by hand, we use strftime (short for "string format time"), which works much like printf but uses specific codes for dates.
Lesson 894Formatting time strings with `strftime`
Strict Aliasing
Modern C compilers use a rule called Strict Aliasing.
Lesson 527Pointer type-punning dangers
Strict Aliasing Rule
In C, the Strict Aliasing Rule is that same assumption for the compiler.
Lesson 936Strict aliasing rule violations
Strict Weak Ordering
To keep the "scale" balanced, your logic must follow Strict Weak Ordering.
Lesson 882Common pitfalls in comparison function logic
string literal
To make life easier, C provides a shorthand: the string literal.
Lesson 416Defining strings with double quotes
string manipulation
By limiting the scope to an array of structs, you focus on the most important C concepts: string manipulation (copying names into the keys), looping (searching for a specific key), and memory layout.
Lesson 1212Project scope: A simple Key-Value store
string_utils.c
When you write code, you often split functions into different files (e.g., math_utils.c, string_utils.c).
Lesson 810What is a static library `.a`
string.h
Use strstr() from string.h to quickly locate a substring within a larger string, checking for NULL to determine if the search failed.
Lesson 1207Implementing string pattern matching
string[0]
The Risk: If your logic assumes string[0] is always a letter, or if you use a loop that skips the check for the null terminator, your program might read past the end of the intended memory.
Lesson 1179Edge case testing: Empty strings and zeros
strings
Double quotes are used for strings (sequences of characters).
Lesson 107Single quotes vs. double quotes
strlen
It is important to remember one crucial rule: strlen does not count the null terminator. If your string is "Cat", strlen returns 3, even though the computer is technically using 4 bytes of memory to store it (C, a, t, and \0).
Lesson 423Getting length with `strlen`Lesson 847Finding string length with `strlen`Lesson 1193Avoiding redundant calculations in loops
strlen()
Use strlen() from <string.h> to find the number of visible characters in a string, excluding the null terminator.
Lesson 420Length vs Size of a string arrayLesson 423Getting length with `strlen`
strlen(city)
However, strlen(city) will be 5, because "Tokyo" only occupies five slots.
Lesson 420Length vs Size of a string array
strncat
To use strncat safely, you must tell it exactly how many slots are remaining in your array, minus one (to leave room for the invisible \0 null terminator that marks the end of a string).
Lesson 436Using `strncat` for safer concatenationLesson 849Concatenating strings with `strncat`
strncpy
The strncpy function has a specific quirk you must remember: if the source string is longer than the limit you set, strncpy will fill the destination buffer but will not add the null terminator (\0) at the end.
Lesson 435Using `strncpy` for safer copyingLesson 848Copying strings safely with `strncpy`
strrchr
The strrchr function (the extra 'r' stands for "reverse") works identically, but it starts scanning from the end of the string and moves backward.
Lesson 851Searching for characters with `strchr` and `strrchr`
strstr
Whether you are filtering a list of names, checking if an email address contains an "@" symbol, or scanning a file for a specific keyword, strstr is your primary tool for scanning text data efficiently.
Lesson 428Searching for substrings with `strstr`Lesson 852Finding substrings with `strstr`Lesson 1204Project scope: A custom `grep` clone
strstr()
Use strstr() from string.h to quickly locate a substring within a larger string, checking for NULL to determine if the search failed.
Lesson 1207Implementing string pattern matching
strtod
Imagine a librarian reading a sentence: "The price is 49.99 dollars." If you ask them to find the number, they stop at the "d" in "dollars." strtod gives you that exact location so you can check if the conversion actually finished the whole string or got stuck on garbage text.
Lesson 873Converting strings to doubles with `strtod`
strtok
When strtok finds that character, it replaces it with a null terminator (\0), effectively ending the string right there and returning a pointer to the start of that "token."
Lesson 429Tokenizing strings with `strtok`Lesson 853Tokenizing strings with `strtok`
strtol
By setting errno = 0 right before strtol, you guarantee that if errno is non-zero afterward, it was definitely caused by that specific line of code.
Lesson 872Robust string-to-number conversion with `strtol`Lesson 900Resetting `errno` before library calls
struct
To make a struct refer to itself, we have to use a "struct tag." Usually, we might use typedef to skip writing the word struct, but because the compiler needs to know about the pointer inside the definition, we must name the struct explicitly.
Lesson 491Efficiency of passing large structs by pointerLesson 509Casting `void *` to specific typesLesson 511Generic functions in CLesson 512The `memcpy` function signatureLesson 567Identifying 'indirectly lost' memoryLesson 573Struct padding for alignmentLesson 577Using __attribute__((packed))Lesson 593Using mmap for large allocationsLesson 599Defining a struct with the struct keywordLesson 600Declaring struct variablesLesson 601The dot operator for member accessLesson 602Initializing structs with brace notationLesson 606Returning a struct from a functionLesson 607The syntax of typedefLesson 609Creating a shorthand for struct namesLesson 610Combining struct definition and typedefLesson 611Anonymous structs with typedefLesson 614Improving code readability with typedefLesson 615Declaring a pointer to a structLesson 618Passing struct pointers to functionsLesson 619Modifying struct members via pointersLesson 623Defining a struct inside another structLesson 626Self-referential structs for linked listsLesson 627Limitations of self-referential definitionsLesson 630Declaring an array of structsLesson 631Initializing arrays of structsLesson 636Searching through struct arraysLesson 637The sizeof operator on structsLesson 638Understanding memory alignmentLesson 640Structure holes and performanceLesson 641Reordering members to reduce paddingLesson 643Alignment requirements for different typesLesson 644Platform dependency of struct sizeLesson 645Purpose of bit-fields in memory-constrained systemsLesson 646Syntax for declaring bit-fieldsLesson 648The colon operator and bit widthLesson 653Defining a union with the union keywordLesson 654Memory layout of a unionLesson 655Accessing union membersLesson 656Overlapping memory in unionsLesson 658Size of a union vs size of a structLesson 669Tagged unions for type safetyLesson 670Combining structs and unionsLesson 729Reading structs back into memoryLesson 789The 'duplicate definition' errorLesson 947Structure padding and alignment issuesLesson 948The `#pragma pack` directiveLesson 949Writing code for 32-bit vs 64-bitLesson 954Bit-fields in structures and portabilityLesson 976Handling the `default` case in `_Generic`
struct Enemy
You cannot assign a struct Enemy to a struct Player, even if they have the same internal members.
Lesson 604Copying structs with the assignment operator
struct Name
To create a forward declaration, you simply write struct Name; on its own line before your actual definitions.
Lesson 609Creating a shorthand for struct namesLesson 628Forward declarations of structs
struct NavigationSatellite
If you defined a struct NavigationSatellite, you would have to type struct NavigationSatellite mySat; to declare a variable.
Lesson 614Improving code readability with typedef
struct NavigationSatellite mySat
If you defined a struct NavigationSatellite, you would have to type struct NavigationSatellite mySat; to declare a variable.
Lesson 614Improving code readability with typedef
struct Node
If a struct Node contains a struct Node member, the compiler calculates the size as: "Size of an integer + Size of a Node." But to find the size of that "Node," it needs to add "Size of an integer + Size of a Node" again.
Lesson 567Identifying 'indirectly lost' memoryLesson 627Limitations of self-referential definitionsLesson 989Defining the self-referential node structLesson 1022Recursive tree node structure
struct Node *next
In this example, struct Node *next is the magic ingredient.
Lesson 626Self-referential structs for linked lists
struct Node*
Notice the struct Node* syntax inside the definition.
Lesson 1022Recursive tree node structure
struct Node* head
Declaring struct Node* head; without setting it to NULL is dangerous.
Lesson 990Creating the head pointer
struct Node* prev
Adding struct Node* prev; might seem like a small change, but it is powerful.
Lesson 999Updating the node struct
struct Player *ptr
In the code above, struct Player *ptr tells the compiler: "I am creating a variable named ptr that is designed to point to a memory location where a struct Player is stored."
Lesson 615Declaring a pointer to a struct
struct Player hero = {'B'}
For example, if you wrote struct Player hero = {'B'};, the grade becomes 'B', but the score and health are automatically set to 0 and 0.0.
Lesson 602Initializing structs with brace notation
struct Player p1
When you write struct Player p1; inside a function, that memory is carved out of the "stack." The moment that function finishes, p1 is destroyed.
Lesson 620Allocating structs on the heap with malloc
struct Player player1
If you declare struct Player player1; inside the main() function, only main can see it.
Lesson 600Declaring struct variables
struct Player*
It is the ultimate shapeshifter; C allows it to be automatically converted into any other pointer type (like int or struct Player) without a complaint from the compiler.
Lesson 540Casting malloc return in C vs C++
struct Point
When we combine typedef with a struct, we tell C: "From now on, whenever I say Point, I mean struct Point."
Lesson 609Creating a shorthand for struct namesLesson 632Indexing into a struct array
struct Point p
Point p; is much easier to scan visually than struct Point p;.
Lesson 614Improving code readability with typedef
struct Point p1
In the previous lesson, you learned that every time you want to use a struct, you have to lug around the struct keyword, like writing struct Point p1;.
Lesson 611Anonymous structs with typedef
struct Point path[] =
Furthermore, if you leave the array size empty (e.g., struct Point path[] = ...), C will automatically count the number of initialized blocks you provided and set the array size for you.
Lesson 631Initializing arrays of structs
struct SpaceShip
Because qsort doesn't know if you are sorting ints or struct SpaceShips, it treats your array as a raw block of memory.
Lesson 876The generic signature of `qsort`
struct Starship *ship
Because a pointer (like struct Starship *ship) is always the same size in memory regardless of what it points to, the compiler doesn't need the full details of the struct to proceed—it just needs to know that the name represents a structure.
Lesson 628Forward declarations of structs
struct Student
Inside that function, we cast those pointers back to struct Student so we can access the .grade member.
Lesson 635Sorting an array of structs
struct Student classroom[100]
When you declare struct Student classroom[100], C sets aside a large "apartment building" of memory.
Lesson 630Declaring an array of structs
struct Task
That function takes an int and returns a pointer to a struct Task.
Lesson 930Complex nested `typedef` structures
struct Task (ptr)(int)
Self-documentation: The name Handler tells the next programmer what that function's purpose is, which the raw syntax struct Task (ptr)(int) fails to do.
Lesson 930Complex nested `typedef` structures
struct TransactionRecord
Writing struct UserNode or struct TransactionRecord repeatedly adds "visual noise" to your code.
Lesson 610Combining struct definition and typedef
struct User user1
Normally, every time you want to create a variable from a structure, you have to type the word struct before the name (e.g., struct User user1;).
Lesson 607The syntax of typedef
struct UserNode
Writing struct UserNode or struct TransactionRecord repeatedly adds "visual noise" to your code.
Lesson 610Combining struct definition and typedef
structs
Imagine your data store is a long shelf of identical boxes (an array of structs).
Lesson 1217Saving the data store to a binary file
structured parking lot
Think of a 2D char array as a structured parking lot.
Lesson 483Array of strings vs 2D char array
strxfrm
Use strcoll for accurate, language-sensitive comparisons, and use strxfrm to pre-process strings for high-performance sorting in global applications.
Lesson 854Understanding the `strcoll` and `strxfrm` locale functions
Student
You aren't limited to numbers; you can write comparators that sort a list of Student structs by GPA, or sort strings by their length rather than their alphabetical order.
Lesson 239Member access `.` and `->`Lesson 630Declaring an array of structsLesson 632Indexing into a struct arrayLesson 635Sorting an array of structsLesson 1060Writing a custom comparator for qsort
Student s1
If you have a variable Student s1;, you are holding the physical folder.
Lesson 239Member access `.` and `->`
studentA
If the function returns a negative number, studentA comes first.
Lesson 635Sorting an array of structs
studentB
If it returns a positive number, studentB comes first.
Lesson 635Sorting an array of structs
students[2]
Identify the item: Use the index (e.g., students[2]) to pick which specific struct you want to talk to.
Lesson 632Indexing into a struct array
style guide
A style guide is simply a shared agreement on how the "house" should be organized.
Lesson 40C coding style guides
suffix
To use a long double, you need to pay attention to two specific details: the suffix and the format specifier.
Lesson 99The `long double` type
sum
If the sum jumped from 10 to 1000 unexpectedly, you would know the error happened exactly during that iteration.
Lesson 403Calculating the sum and averageLesson 1162Setting breakpoints and stepping through codeLesson 1163Inspecting variable values at runtime
sum += i
If your output is unexpected, you can set a breakpoint on the sum += i; line.
Lesson 1162Setting breakpoints and stepping through code
sum(1)
sum(1) hits the base case and returns 1.
Lesson 355The Recursive Step
sum(3)
If you call sum(3), the recursive step breaks it down:
Lesson 355The Recursive Step
SUNDAY
For instance, if you have an enum for MONDAY through SUNDAY, MONDAY will be 0 and SUNDAY will be 6.
Lesson 662Default integer values in enums
suppression file
Valgrind allows you to create a "hush list" called a suppression file (usually ending in .supp).
Lesson 570Suppressing known tool warnings
swap
Without pointers, the swap function would only swap copies of x and y.
Lesson 490Swapping two numbers using pointers
switch statement
You should use a switch statement when you are comparing one single variable against a list of constant values (like integers or characters).
Lesson 264Comparing switch-case vs else-if ladders
switch(gear)
The Expression: Inside switch(gear), C looks at the value stored in the variable.
Lesson 258Basic switch syntax and cases
symbol table
During this process, the compiler creates a symbol table.
Lesson 809Symbol tables and visibility
Symbolic Constant
A Symbolic Constant is a way to give that literal value a meaningful name.
Lesson 118Literal vs. Symbolic constants
symbolic constants
We call these macros or symbolic constants.
Lesson 768Defining constants with `#define`
System Call
This is called a System Call, and it is "expensive" in terms of time and processing power.
Lesson 754Performance: Single char vs block I/O
System Paths
#include <stdio.h>: The angle brackets tell the compiler to look in System Paths (the "Standard Library" section of the library).
Lesson 63Header search paths
system("clear")
If you are writing a library that opens a database connection or a log file, you don't want a user's random system("clear") call to inherit your private database handle.
Lesson 1096The close-on-exec flag
system("dir")
First, it is "platform dependent"—a program using system("dir") will crash or fail on a Mac, which expects ls.
Lesson 874Communicating with the OS using `system`
system()
The system() function is a quick way to run terminal commands from your code, acting as a bridge between your program and the operating system.
Lesson 874Communicating with the OS using `system`
systemd
The child becomes an "orphan." In modern systems, the system adopts these orphans, usually reassigning them to a special system process (like systemd or init) which has a PID of 1.
Lesson 1069Parent processes and getppid()

T

Tab character
Make requires that every line in a recipe begins with a Tab character.
Lesson 820The importance of Tab characters
tab-indented command
A Makefile rule consists of a target to create, dependencies it requires, and a tab-indented command to perform the build.
Lesson 818Structure of a Makefile Rule
tag
In code, this means we omit the tag (the name that usually follows the word struct) and provide the alias at the very end.
Lesson 611Anonymous structs with typedef
tagged union
In the example below, we use a struct to hold the "Label" of our data, and a nested union to hold the "Value." This is often called a tagged union because the type variable tells us which part of the union is currently active.
Lesson 669Tagged unions for type safetyLesson 670Combining structs and unions
tail
Because every node in a doubly linked list has a prev pointer, having a tail pointer transforms the list into a two-way street.
Lesson 1000Handling the tail pointerLesson 1001Bidirectional traversal
tail call
A tail call happens when a function's very last action is returning the result of another function call.
Lesson 362Tail call optimization basics
Tail Call Optimization (TCO)
Tail Call Optimization (TCO) is a trick used by compilers.
Lesson 362Tail call optimization basics
tail_factorial
Since tail_factorial has nothing left to do but pass the result along, the compiler turns the recursion into a simple loop behind the scenes.
Lesson 362Tail call optimization basics
tail->next
In a standard doubly linked list, the head->prev and tail->next pointers both point to NULL.
Lesson 1004Circular doubly linked lists
tan
What you'll learn: How to use the sin, cos, and tan functions from the math library and why you must convert degrees to radians first.
Lesson 863Trigonometric functions in radians
tan()
C's trigonometric functions require angles in radians, so always multiply degrees by (PI / 180.0) before passing them to sin(), cos(), or tan().
Lesson 863Trigonometric functions in radians
tape recorder for your terminal
Think of a Shell script as a tape recorder for your terminal.
Lesson 1178Automating tests with a Shell script
target
In the code example above, GDB would stop on the line items[i] = 0; the moment i reached 3, revealing a buffer overflow that accidentally overwrote the target variable.
Lesson 818Structure of a Makefile RuleLesson 821Using variables in MakefilesLesson 822Automatic variables like `$@` and `$<`Lesson 836Using `watch` for memory changesLesson 1211Writing the Makefile for the project
target->health
You only need to perform the actual #include "enemy.h" inside your .c source file, where you finally need to access the enemy's specific fields (like target->health).
Lesson 792Forward declarations in headers
target->next
However, there is a small catch: if the node you are deleting is the Head, you must still update your Head pointer variable in your main program to point to target->next, otherwise your program will try to start the list at a memory location that no longer exists.
Lesson 1003Deleting without head traversal
Targets
A Makefile rule uses targets (the goal), dependencies (the requirements), and recipes (the commands) to automate your build process efficiently.
Lesson 61Introduction to `make` and MakefilesLesson 819Targets, dependencies, and recipes
tax
The program returns to main, and the memory previously used by tax and total is now considered "junk" or empty space.
Lesson 530Stack frame lifecycle and local variables
tells
The first % tells printf, "Get ready, something special is coming." The second % clarifies, "The special thing I want is actually just a regular percent sign."
Lesson 690Escaping the percent sign %%
temp_converter.c
Good: hello_world.c or temp_converter.c.
Lesson 16Naming conventions for .c files
temp_log.txt
For example, if your program creates a temporary log, you might check if temp_log.txt already exists.
Lesson 762Checking if a file exists
temp.txt
You might wonder, "Why not just create a regular file named temp.txt?"
Lesson 758Creating temporary files with tmpfile
temperature
When bake_cake finishes, the memory it used for temperature is instantly freed up for the next function call.
Lesson 255Common mistake: assignment (=) vs equality (==)Lesson 349Storage of local variables
template
It’s like a template rather than a stamp.
Lesson 778Macros vs inline functions
temporary
The most important thing to remember about a.out is that it is temporary.
Lesson 60Understanding the `a.out` default
temporary container
To prevent this, we use a temporary container.
Lesson 1047Bubble Sort: The swap logic
temporary_value
If you tried to access temporary_value from outside that function, the compiler would get confused because that variable’s life is tied strictly to the "automatic" scope of the function block.
Lesson 143The `auto` keyword
temporaryCount
However, temporaryCount is wiped from the Stack every time the function ends, so it resets to zero every single time the function is called.
Lesson 149Memory segments: Stack vs. Data
ternary operator
The ternary operator (? :) is C’s only operator that takes three parts.
Lesson 206Syntax of `? :`Lesson 210Ternary vs If-Else for assignmentsLesson 252The ternary operator (?:) as a shortcut
test_main.c
When you compile your test, you simply link logic.c and test_main.c with your mock_sensor.c instead of the real hardware driver.
Lesson 1176Mocking simple dependencies
test.sh
You can create a file named test.sh like this:
Lesson 1178Automating tests with a Shell script
testing phase
But during the testing phase, crashing is a gift.
Lesson 1173Writing a simple `assert()` check
tests_failed == 0
By returning tests_failed == 0 at the end of main, your program communicates with your computer.
Lesson 1174Building a minimal custom test harness
Text Editor
A Text Editor is like a manual transmission—you have to shift the gears yourself, but you learn exactly how the engine behaves.
Lesson 13Using a Text Editor vs IDE
Text mode
This distinguishes between Text mode (the default) and Binary mode (using the b flag).
Lesson 724Text mode vs Binary mode (b flag)Lesson 946Handling line endings across OSs
The "at" line
The "at" line confirms malloc was the source.
Lesson 566Reading 'definitely lost' reports
the code block
Normally, the guard checks your ID (the condition) and then lets you through to the courtyard (the code block) to do some work.
Lesson 272Common error: semicolon after while header
the condition
Normally, the guard checks your ID (the condition) and then lets you through to the courtyard (the code block) to do some work.
Lesson 272Common error: semicolon after while header
The first "by" line
The first "by" line is the "smoking gun." It points to example.c:5.
Lesson 566Reading 'definitely lost' reports
The first element (argv[0])
The first element (argv[0]) should traditionally be the name of the program itself.
Lesson 1082Passing arguments to execv()
The Hand-off
The Hand-off: When we call updateMessage(&myMessage), we aren't passing the NULL value; we are passing the location of the box itself.
Lesson 503Modifying a pointer inside a function
The Modification
The Modification: Inside the function, ptrToPtr means "go to the box located at this address." The assignment ptrToPtr = secretMessage puts a new address inside that box.
Lesson 503Modifying a pointer inside a function
The Setup
The Setup: char *myMessage is a box that holds a memory address.
Lesson 503Modifying a pointer inside a function
The Struct
The Struct: Inside the "packed" zone, every member follows the previous one immediately.
Lesson 948The `#pragma pack` directive
The top line
The top line tells you how much memory was leaked (40 bytes).
Lesson 566Reading 'definitely lost' reports
the very first time
In the example above, counter is guaranteed to be 0 the very first time checkStatic() is called.
Lesson 148Default initialization of static variables
thrd_create
C11 provides thrd_create to start a task and mtx_init for "mutexes" (locks), which ensure two chefs don't try to use the same knife at the exact same time.
Lesson 967C11: Multi-threading and Anonymous structures
Thread
A Thread is like hiring an extra chef to work in your current kitchen.
Lesson 1116Threads vs Processes
through
If an array has a size of 10, the valid slots are 0 through 9.
Lesson 319The Fencepost problem (off-by-one errors)
time()
When you want to see how fast a piece of code runs, you might be tempted to look at a wall clock or use basic functions like time().
Lesson 888Getting a unique seed with `time(NULL)`Lesson 889Getting system time with `time_t`Lesson 1181Precise timing with `clock_gettime()`
time(NULL)
Because the time is always moving forward, the value returned by time(NULL) will be different every time you hit the "Run" button on your code.
Lesson 886Why you should only seed onceLesson 888Getting a unique seed with `time(NULL)`Lesson 889Getting system time with `time_t`
timespec
It doesn't just return an integer; it fills a special structure called timespec that tracks two things: seconds and nanoseconds.
Lesson 1181Precise timing with `clock_gettime()`
timestamps
Instead, it looks at the timestamps—the date and time recorded by your operating system showing when a file was last modified.
Lesson 824Incremental builds and file timestamps
TL_
By adding TL_ and ST_, you’ve created unique names.
Lesson 666Scoped enum limitations in C
TL_RED
Now the "living room" has a TL_RED lamp and an ST_RED lamp, and the compiler can easily tell them apart.
Lesson 666Scoped enum limitations in C
tm
The tm struct translates a single raw timestamp into a readable format by separating time into logical components like hours, days, and years.
Lesson 891Breaking down time with `struct tm`Lesson 894Formatting time strings with `strftime`
tm_mon
Offset values: The tm_year field counts years since 1900 (so 2024 is stored as 124), and tm_mon is zero-indexed (January is 0, not 1).
Lesson 891Breaking down time with `struct tm`Lesson 892Converting `time_t` to local time with `localtime`Lesson 893Converting `time_t` to UTC with `gmtime`
tm_wday
It also fills in the tm_wday (day of the week) automatically, which is a great way to find out what day a specific date falls on.
Lesson 895Converting `struct tm` back to `time_t` with `mktime`
tm_year
Offset values: The tm_year field counts years since 1900 (so 2024 is stored as 124), and tm_mon is zero-indexed (January is 0, not 1).
Lesson 891Breaking down time with `struct tm`Lesson 892Converting `time_t` to local time with `localtime`Lesson 893Converting `time_t` to UTC with `gmtime`
tmpfile()
Because tmpfile() handles the filename and location for you, you cannot "find" this file in your file explorer while the program is running—it is often hidden or stored in a restricted system directory.
Lesson 758Creating temporary files with tmpfile
tmpnam
Because tmpnam only gives you a name and doesn't lock the file, there is a tiny window of time between getting the name and calling fopen where another program could theoretically grab that same name.
Lesson 759Generating temp filenames with tmpnam
tmpnam()
Older C functions like tmpnam() or mktemp() are dangerous because they only suggest a filename.
Lesson 763Temporary file security risks
tmpnam(filename)
When you call tmpnam(filename), the function fills your character array with a string like /tmp/fileA3bZ.
Lesson 759Generating temp filenames with tmpnam
to 0 and
Initialization (i = 0, j = max): Before the loop starts, we set i to 0 and j to 5 at the same time.
Lesson 240The comma operator in `for` loops
to a
If you want to bring the code back to life, you simply change the 0 to a 1.
Lesson 785Temporary code disabling with `#if 0`
to an
If an int takes up 4 bytes of memory, adding 1 to an int pointer actually shifts the address forward by 4 bytes.
Lesson 464Adding integers to pointers
to assign a value and
Use = to assign a value and == to compare two values.
Lesson 165Common pitfall: `=` vs `==`
to decimals for
Add an f to decimals for float variables and an L for long double variables to ensure your data fits its container perfectly.
Lesson 120Floating-point suffixes (f, L)
to find the "ceiling." If
In this code, we subtract b from INT_MAX to find the "ceiling." If a is already higher than that ceiling, adding b is guaranteed to overflow.
Lesson 1154Safe integer arithmetic and overflow checks
to literals and using the
Use long double for maximum decimal precision by appending an L to literals and using the %Lf specifier for input and output.
Lesson 99The `long double` type
to node
When we add an edge from node A to node B, we only perform one operation: we add B to A's list.
Lesson 1042Adding edges in directed graphs
to RED
If you define enum Colors {RED, GREEN, BLUE};, the compiler assigns 0 to RED, 1 to GREEN, and 2 to BLUE.
Lesson 663Explicitly assigning enum values
to turn bits on and
Bitwise flags allow you to store multiple boolean states in one variable by using | to turn bits on and & to check their status.
Lesson 189Using bitwise operators for flags
tokenizing
In C, the process of breaking this sequence into individual pieces is called tokenizing, and the strtok function is your primary tool for the job.
Lesson 853Tokenizing strings with `strtok`
tolower()
When you use the functions in <ctype.h>, such as isalpha(), isdigit(), or tolower(), you might assume they accept a standard char.
Lesson 443Counting vowels and consonantsLesson 845Converting case with `toupper` and `tolower`Lesson 846The importance of casting to `unsigned char` in `ctype` functions
tools.c
You can give your tools.h and tools.c to a teammate, and they can use your functions without ever needing to read your complex internal logic.
Lesson 382Sharing functions across modules
tools.h
You can give your tools.h and tools.c to a teammate, and they can use your functions without ever needing to read your complex internal logic.
Lesson 382Sharing functions across modules
total
For example, if you write int total = 10 + 5;, C first calculates 15 and then stores that final number into the variable named total.
Lesson 101Precision loss and rounding errorsLesson 150The addition operator `+`Lesson 530Stack frame lifecycle and local variables
total * 0.07
If a new programmer looks at your code and sees total * 0.07, they might wonder: "Is this a tax rate?
Lesson 118Literal vs. Symbolic constants
total = total - 500
In the example above, the computer behaves as if the line total = total - 500; doesn't exist.
Lesson 38Commenting out code for testing
Total $ Amount
While you might be tempted to name a variable Total $ Amount or 2nd_Place, the C compiler will throw an error.
Lesson 74Naming rules and identifiers
total_points
You have two integers: total_points (15) and number_of_tests (2).
Lesson 230The `(type)` cast operator
totalFruit
The + operator is the act of pouring both buckets into a new, larger bucket called totalFruit.
Lesson 150The addition operator `+`
totalItems
You aren't just letting the computer handle the math; you are explicitly stating that for this specific calculation, you want totalItems treated as a decimal.
Lesson 135Readability and intent in casting
totalPoints
It simply looks at what is currently inside playerScored (which is 10), makes a copy of it, and drops that copy into totalPoints.
Lesson 78Assigning values with `=`
totalPoints = playerScored
In the example above, totalPoints = playerScored doesn’t link the two variables forever.
Lesson 78Assigning values with `=`
toupper
What you'll learn: Why you must cast arguments to (unsigned char) when using functions like isdigit or toupper to prevent program crashes.
Lesson 846The importance of casting to `unsigned char` in `ctype` functions
toupper()
Think of these as automated "gates." If you send a lowercase 'b' through the toupper() gate, it comes out as a capital 'B'.
Lesson 845Converting case with `toupper` and `tolower`
track coach
Think of it like a track coach telling an athlete to run exactly 10 laps.
Lesson 321Choosing the right loop for the task
translation
When you open a file in text mode ("r" or "w"), C performs a little bit of magic behind the scenes called translation.
Lesson 739Risks of seeking in text mode
Treasure (the actual data)
Inside that Drawer is a Map that tells you exactly where the Treasure (the actual data) is hidden.
Lesson 500Concept of double indirection
trunc
Use trunc to discard decimals without rounding, and use fmod as the floating-point version of the modulo (%) operator.
Lesson 865Truncation and remainder: `trunc` and `fmod`
trunc()
The trunc() function is the simplest way to turn a decimal into a whole number.
Lesson 865Truncation and remainder: `trunc` and `fmod`
Try to read again
Try to read again. The read fails, but the variables still hold the data from the previous step.
Lesson 722Why feof inside a loop condition is bad
trylock
It can use trylock to see if the data is available; if not, it can skip the update for one frame or draw a loading icon instead of hanging the entire application.
Lesson 1129Using pthread_mutex_trylock
tv.channel = 5
In C, this is like using the dot operator (tv.channel = 5).
Lesson 619Modifying struct members via pointers
twice
The most mind-bending part of fork() is that it is called once, but it returns twice: once in the parent and once in the child.
Lesson 775Side effects in macro argumentsLesson 1073Introduction to the fork() system call
two
If you want two processes to talk back and forth (bidirectional communication), you actually need to create two separate pipes—one for each direction.
Lesson 1102Unidirectional flow in pipes
Two-Pointer technique
The most efficient way to do this is the Two-Pointer technique.
Lesson 441Reversing a string in place
Two’s Complement
They can't store a "dash." To solve this, C uses a system called Two’s Complement.
Lesson 91How bits represent negative numbers
type cast
However, C provides a "hammer" called a type cast.
Lesson 499Casting away `const` volatility
type declaration
The magic happens because of the pointer's type declaration.
Lesson 465How data types affect step size
type name
When you see int *ptr, the asterisk is just part of the type name, telling C that this variable is a pointer.
Lesson 457The Dereference operator `*`
type name[]
A flexible array member (type name[]) must be the last element in a struct and allows you to allocate a single, continuous block of memory for both a header and a variable-length payload.
Lesson 672Flexible array members in C99
type name[rows][columns]
A 2D array is declared using type name[rows][columns], creating a grid-based coordinate system for your data.
Lesson 407Declaring 2D arrays: Rows and Columns
type name[size]
An array declaration type name[size] tells C to reserve a fixed, side-by-side sequence of memory slots for a specific data type.
Lesson 383Declaring an array with `type name[size]`
type promotion
Instead, it follows a rule called Type Promotion.
Lesson 121What is type promotion?Lesson 158Mixing int and float in arithmetic
type punning
Normally, we use this to save space, but advanced programmers use it for type punning: looking at the raw bits of one data type as if they were another.
Lesson 657Using unions for type punningLesson 659The danger of reading the wrong union member
type_size_t
The names follow a simple pattern: type_size_t.
Lesson 86Fixed-width types from `<stdint.h>`
typedef [existing type] [new name]
The syntax follows a simple pattern: typedef [existing type] [new name];.
Lesson 608Using typedef with primitive types
typedef [original type] [new name]
Use typedef [original type] [new name]; to create a shorter alias for complex types to make your code more readable.
Lesson 607The syntax of typedef
typedef struct { ... } Name
Use typedef struct { ... } Name; to define a structure and its shorthand alias simultaneously, keeping your code concise and professional.
Lesson 610Combining struct definition and typedef
typedefs
Nested typedefs transform unreadable, complex declarations into a logical hierarchy of named types that are easier to maintain and debug.
Lesson 930Complex nested `typedef` structures
typeof
C23 introduces typeof, which acts like a "rubber stamp." Instead of looking up a type, you tell the compiler: "Whatever type this specific thing is, make my new variable the same."
Lesson 932Using `typeof` in C23
types
Before you can send data over a network, you have to decide two things: "Where is this going?" and "How should the data behave?" In C, we define these using Domains and Types.
Lesson 332Matching prototypes with definitionsLesson 1108Socket domains and types

U

u.decimal
When you assigned 3.14f to u.decimal, the program went to the union's memory address and wrote the binary pattern for that float.
Lesson 655Accessing union members
u.integer
If you try to read u.integer while the float is there, C won't stop you, but it will try to interpret the float’s binary bits as an integer.
Lesson 655Accessing union members
UCHAR_MAX
This header defines constants like INT_MAX, CHAR_BIT, and UCHAR_MAX.
Lesson 942Limits of `limits.h` and `stdint.h`
UI_
Use unique, module-specific prefixes (like UI_ or NET_) for all public functions and global variables to prevent naming conflicts in large projects.
Lesson 801Naming conventions for large projects
ui.c
This means if you define int global_score = 100; in game.c, you can access that exact same memory location in ui.c by using the extern keyword.
Lesson 146The `extern` keyword for multi-file codeLesson 918Internal vs external linkage basics
UINT_MAX
The largest possible unsigned int (the minimum is always 0).
Lesson 96The `<limits.h>` header file
uint16_t
An unsigned (positive only) 16-bit integer.
Lesson 86Fixed-width types from `<stdint.h>`
uint32_t
Here is how you can manually reverse the bytes of a uint32_t.
Lesson 612Naming conventions for typedef typesLesson 953Manual byte swapping techniques
uint64_t
An unsigned 64-bit integer.
Lesson 86Fixed-width types from `<stdint.h>`
uint8_t
Use <stdint.h> types like int32_t or uint8_t when you need guaranteed, consistent variable sizes across different computers.
Lesson 86Fixed-width types from `<stdint.h>`Lesson 945The significance of `char` signness
uintptr_t
Always use size_t for memory sizes and uintptr_t for pointer-to-integer conversions to ensure your code runs safely on both 32-bit and 64-bit systems.
Lesson 949Writing code for 32-bit vs 64-bit
ulimit -c unlimited
To tell Linux you want them, you usually run ulimit -c unlimited in your terminal.
Lesson 837Debugging a Segfault from a core dump
unaligned access
In programming, this "extra trip" is exactly what happens with unaligned access.
Lesson 574Performance cost of unaligned access
unary operators
Right-to-left associativity is also found in unary operators (operators that act on a single thing, like ++ or - for negative numbers).
Lesson 214Right-to-left associativity
unbuffered
It is unbuffered, meaning it bypasses the waiting bin and hits the screen the exact millisecond the command is executed.
Lesson 750Unbuffered output (stderr)
Undefined
If you push a signed integer past its maximum limit, the C standard does not say "it wraps around." Instead, it says the behavior is Undefined.
Lesson 935Signed integer overflow vs Unsigned wrap
Undefined Behavior
If you try to modify a variable and read it (or modify it again) in the same statement, you enter the territory of Undefined Behavior.
Lesson 204Common increment pitfallsLesson 915The `restrict` pointer qualifier
undefined reference
An undefined reference means you promised a function existed, but you failed to provide the actual code for it.
Lesson 28Phase 4: The LinkerLesson 64Library linking basicsLesson 70The 'undefined reference' linker error
underscores
Always name your files in lowercase, use underscores instead of spaces, and ensure they end in a lowercase .c.
Lesson 16Naming conventions for .c files
undirected
Edges can be undirected (like a two-way street where both people are friends) or directed (like a one-way street or a "follower" relationship on Twitter).
Lesson 1039Vertices and Edges definition
unidirectional
The most important rule to remember is that pipes are unidirectional.
Lesson 1102Unidirectional flow in pipes
uniformity
The beauty of the stream concept is uniformity.
Lesson 676The concept of a stream in C
uninitialized variable
When you declare a variable but do not assign it a value, it is called an uninitialized variable.
Lesson 77Garbage values and uninitialized variables
unique name
A declaration consists of a data type, a unique name, and a semicolon.
Lesson 75The syntax of a declaration
unistd.h
To find out the "passport number" of your own program while it is running, you use a simple function from the unistd.h library called getpid().
Lesson 1068Getting PID with getpid()Lesson 1069Parent processes and getppid()
Unit Testing
In C, Unit Testing is the practice of checking the smallest "units" of your code—usually individual functions—before they become part of the whole.
Lesson 1172Principles of Unit TestingLesson 1177Integration testing vs. Unit testing
Units
In the example above, Units behaves exactly like an unsigned int.
Lesson 608Using typedef with primitive types
universal remote control
Using a function pointer is like using a universal remote control.
Lesson 516Calling a function via a pointer
Unix timestamp
C provides a function called time() (found in the <time.h> header) that returns the Unix timestamp.
Lesson 888Getting a unique seed with `time(NULL)`
UNKNOWN
In the example above, because SERVER_ERROR was set to 500, the compiler automatically assigned UNKNOWN the next integer in sequence: 501.
Lesson 663Explicitly assigning enum values
unlock
To fully unlock the mutex, the thread must call unlock the exact same number of times it called lock.
Lesson 1126Locking and unlocking mutexesLesson 1130Recursive mutexes
unlocked
If the mutex is unlocked, the thread locks it and the function returns 0.
Lesson 1129Using pthread_mutex_trylock
unnamed bit-fields
This is where unnamed bit-fields come in.
Lesson 650Unnamed bit-fields for padding
unsigned char
There is a historical trap here: these functions are only defined to handle values that can be represented as an unsigned char (0 to 255), plus one special value: EOF (usually -1).
Lesson 111Signed vs. Unsigned charsLesson 683Relationship between char and int in I/OLesson 846The importance of casting to `unsigned char` in `ctype` functionsLesson 935Signed integer overflow vs Unsigned wrapLesson 945The significance of `char` signness
unsigned char*
If you truly need to look at the raw bits of a variable (for example, to send them over a network), the only safe way is to use a pointer to a character type (char or unsigned char) or the memcpy function.
Lesson 527Pointer type-punning dangers
unsigned int : 0
A zero-width bit-field is a special syntax—unsigned int : 0;—that tells the compiler: "Stop packing bits into the current unit.
Lesson 651Zero-width bit-fields for alignment
unsigned integer
Think of an unsigned integer like a tape measure.
Lesson 95When to choose unsigned over signed
unsigned integers
In C, unsigned integers work exactly like this.
Lesson 935Signed integer overflow vs Unsigned wrap
unsigned short
On most systems, an unsigned short maxes out at 65,535.
Lesson 92Understanding Integer Overflow
Unsigned wins
Instead, it follows a rule called "Usual Arithmetic Conversions." The most important rule to remember is: Unsigned wins.
Lesson 127Mixing signed and unsigned in math
unsorted
It treats the array as two parts: a sorted section on the left and an unsorted section on the right.
Lesson 1048Selection Sort: Finding the minimumLesson 1064Importance of sorted data
unstable
If you use an unstable sort, the 5 of Spades might end up first.
Lesson 1052Stability in sorting algorithms
up
Use up to inspect the variables of the function that called your current position, and down to return toward the active line of code.
Lesson 834Moving between frames with `up` and `down`Lesson 864Rounding with `ceil`, `floor`, and `round`
up 2
You can move multiple steps by typing up 2 or go back to the start by typing down.
Lesson 834Moving between frames with `up` and `down`
update expression
The third part of the for loop declaration is the update expression.
Lesson 287Using non-unit increments (e.g., i += 2)
update statement
To prevent this, you must include an update statement inside the loop body.
Lesson 268Updating the loop variable to avoid infinite loops
update()
If both files define a global variable or function named update(), the compiler will get confused, and the linker will throw a "multiple definition" error.
Lesson 801Naming conventions for large projects
updateMessage(&myMessage)
The Hand-off: When we call updateMessage(&myMessage), we aren't passing the NULL value; we are passing the location of the box itself.
Lesson 503Modifying a pointer inside a function
updateScore
The updateScore function was just playing with a clone.
Lesson 345Limitations of pass by value
Use After Free (UAF)
In C, a Use After Free (UAF) happens when you call free() on a pointer, but then try to read from or write to that pointer later in the program.
Lesson 1167Tracking down 'Use After Free' bugs
Use Extra Memory
Use Extra Memory when speed is the absolute priority and you have plenty of RAM to spare, as these algorithms often handle large datasets more efficiently.
Lesson 1051In-place sorting vs extra memory
Use In-place
Use In-place when memory is tight or when you want to avoid the overhead of allocating and freeing new memory buffers.
Lesson 1051In-place sorting vs extra memory
use_battery(50)
In this code, when use_battery(50) runs, the function creates its own local version of energy and sets it to 50.
Lesson 340Shadowing variables in functions
User Headers
Double Quotes "filename.h": These are for User Headers.
Lesson 795Standard header search paths
User user1
By using typedef, you can hide that requirement and treat your structure just like a standard type (e.g., User user1;).
Lesson 607The syntax of typedef
user_age
Other variables: Suddenly, your user_age variable changes to 9999 because a nearby pointer leaked into its space.
Lesson 40C coding style guidesLesson 524Buffer overflows via pointers
user_input.c
Level 2 (The Middle): Logic modules like physics.c or user_input.c.
Lesson 802Dependency graphing in your head
user_input()
In C, this means testing how different modules or files interact—for example, checking if your database_save() function correctly handles the data passed to it by your user_input() function.
Lesson 1177Integration testing vs. Unit testing
user.h
When the compiler looks at user.h, it tries to resolve post.h first.
Lesson 794Circular dependency issues
User*
Returning a pointer (User*) is more efficient than returning the entire struct.
Lesson 1215Writing the Search function
USER=alice
They are key-value pairs (like USER=alice) that exist outside your program's source code, allowing you to change a program’s behavior without recompiling it.
Lesson 1071Environment variables in C
userAge
It tells the debugger: "Hey, this chunk of machine code at address 0x7ffe actually belongs to the variable userAge on line 12 of main.c."
Lesson 37Readability best practicesLesson 40C coding style guidesLesson 825Compiling with debug symbols `-g`
userGuess != secretPin
However, userGuess != secretPin evaluates to true, so the "Access denied" message will appear on the screen.
Lesson 241Relational operators: == and !=
userGuess == secretPin
In the code above, userGuess == secretPin evaluates to false because 5555 is not 1234.
Lesson 241Relational operators: == and !=
username
In the first example, username is a container that can hold 10 characters.
Lesson 415Declaring arrays of type `char`Lesson 561Heap buffer overflows
using an
For example, instead of counting by 0.1, count from 1 to 10 using an int, then divide the result by 10.0.
Lesson 320Floating point precision issues in loop conditions
Usual Arithmetic Conversions
Since the computer’s CPU prefers to perform math on two values of the exact same type, C uses a set of behind-the-scenes rules called the Usual Arithmetic Conversions.
Lesson 126The 'Usual Arithmetic Conversions'Lesson 229Usual arithmetic conversions
UTC
Specifically, gmtime converts the time to UTC (Greenwich Mean Time), which is the global scientific standard, ignoring local time zones or daylight savings.
Lesson 893Converting `time_t` to UTC with `gmtime`
utils.c
This means if you change a small helper function in utils.c, you don't necessarily have to rethink how the entire main.c is written; you just ensure the connections stay the same.
Lesson 824Incremental builds and file timestampsLesson 1210Structuring the project into multiple `.c` filesLesson 1211Writing the Makefile for the project
utils.h
If your project grows, you might end up with two different utils.h files in different folders, leading to a "collision" where one file accidentally blocks the other from loading.
Lesson 1203Header guard best practices

V

va_arg
If you tried to use va_arg after that line, the program would fail, which is exactly what we want—it prevents us from accidentally reaching into memory where we don't belong.
Lesson 371Functions with unknown argumentsLesson 372The `stdarg.h` libraryLesson 374Extracting arguments with `va_arg`Lesson 375Cleaning up with `va_end`Lesson 376How `printf` works internally
va_end
On certain hardware architectures or specific compilers, failing to call va_end can lead to "undefined behavior"—a fancy way of saying your program might crash, corrupt memory, or act unpredictably.
Lesson 372The `stdarg.h` libraryLesson 375Cleaning up with `va_end`
va_end(args)
In the example above, va_end(args) invalidates the pointer.
Lesson 375Cleaning up with `va_end`
va_list
By passing the last known fixed argument (like count in the example above) to va_start, you are telling C: "Look at the memory address of this variable, skip over it, and start reading right after that." Without at least one fixed argument to act as a lighthouse, va_list would be lost at sea.
Lesson 371Functions with unknown argumentsLesson 372The `stdarg.h` libraryLesson 373Using `va_list` and `va_start`Lesson 374Extracting arguments with `va_arg`Lesson 375Cleaning up with `va_end`Lesson 376How `printf` works internally
va_start
By passing the last known fixed argument (like count in the example above) to va_start, you are telling C: "Look at the memory address of this variable, skip over it, and start reading right after that." Without at least one fixed argument to act as a lighthouse, va_list would be lost at sea.
Lesson 371Functions with unknown argumentsLesson 372The `stdarg.h` libraryLesson 373Using `va_list` and `va_start`Lesson 374Extracting arguments with `va_arg`Lesson 375Cleaning up with `va_end`Lesson 376How `printf` works internally
val1
If val1 is 5 and val2 is 10, the result is -5 (negative), placing 5 first.
Lesson 877Writing an integer comparison function
val2
If val1 is 5 and val2 is 10, the result is -5 (negative), placing 5 first.
Lesson 877Writing an integer comparison function
val2 - val1
By changing just the logic inside compare_ints (for example, returning val2 - val1), you can flip the entire sorting order to descending without ever touching the complex sorting algorithm itself.
Lesson 519The `qsort` callback mechanism
Valgrind
Tools like Valgrind act like a private investigator, tracking every key you've been issued to make sure you returned them all to the front desk before the program ended.
Lesson 564Installing Valgrind MemcheckLesson 1164What is a memory leak?Lesson 1185Profiling memory allocation frequencyLesson 1219Final memory leak check and cleanup
valgrind --leak-check=full ./my_program
When you run this through Valgrind with the command valgrind --leak-check=full ./my_program, the tool tracks the address returned by malloc().
Lesson 528Tools for pointer debugging (Valgrind)Lesson 1170Cleaning up heap memory before exit
valgrind --leak-check=full ./your_program
By running valgrind --leak-check=full ./your_program, the computer tracks every single byte.
Lesson 1219Final memory leak check and cleanup
valgrind --version
Once installed, you can check if it's ready by typing valgrind --version.
Lesson 1165Installing and running `valgrind`
valgrind ./my_program
When you run your program through Valgrind using valgrind ./my_program, it acts like a security guard watching your every move.
Lesson 568Finding invalid reads and writesLesson 569Detecting uninitialized value usageLesson 1167Tracking down 'Use After Free' bugsLesson 1168Detecting uninitialized memory reads
valgrind ./your_program
Run valgrind ./your_program on code compiled with the -g flag to catch hidden memory leaks before they crash your software.
Lesson 565Running a program under ValgrindLesson 1165Installing and running `valgrind`
valgrind-3.18.1
If you see a version number (like valgrind-3.18.1), you are ready to go.
Lesson 564Installing Valgrind Memcheck
Valgrind's Callgrind
In C, we use tools called profilers (like gprof or Valgrind's Callgrind) to identify these spots.
Lesson 1183Identifying 'Hot Spots' in your code
validate()
You might have a helper function called validate() that is specific to your math logic and shouldn't clash with a validate() function in your networking logic.
Lesson 918Internal vs external linkage basics
value > 10
value > 10 is false (10 is not strictly bigger than 10).
Lesson 242Relational operators: <, <=, >, and >=
value >= 10
value >= 10 is true (10 is equal to 10).
Lesson 242Relational operators: <, <=, >, and >=
varargs
The function uses varargs (variable arguments).
Lesson 367Compiler discretion with inlining
variable
A variable is like putting a Post-it note label on one of those lockers.
Lesson 50Printing integers with `%d`Lesson 73What is a variable?
variable += value
Use variable += value; as a shorthand to add a number to an existing variable and save the result instantly.
Lesson 192Compound addition `+=`
Variable Values
To trace a loop, draw three columns: Iteration Number, Variable Values, and Condition Check (True/False).
Lesson 273Tracing while loop execution on paper
variable.member
Use the dot operator (variable.member) to access or modify specific pieces of data stored inside a structure.
Lesson 601The dot operator for member access
variables and an
Add an f to decimals for float variables and an L for long double variables to ensure your data fits its container perfectly.
Lesson 120Floating-point suffixes (f, L)
variadic arguments
Most functions require a fixed number of inputs, but printf uses a feature called variadic arguments.
Lesson 376How `printf` works internally
variadic functions
To do this in C, we use variadic functions powered by the <stdarg.h> header.
Lesson 372The `stdarg.h` library
Variant
When you pass a Variant struct to a function, that function doesn't have to guess what's inside.
Lesson 669Tagged unions for type safety
vending machine
Think of it like a vending machine: you enter your selection first, and then the machine checks if you have enough credit.
Lesson 321Choosing the right loop for the taskLesson 328Returning values from functions
VERBOSE
Debug Modes: Running specific logs only when DEBUG is on and VERBOSE is also enabled.
Lesson 782The `defined()` operator
VERSION
In the example above, because VERSION is defined as 2, the preprocessor will keep the "Pro Version" line and literally erase the "Free Version" line.
Lesson 781Basic logic with `#if` and `#else`
Vertex
A Vertex (plural: Vertices) is a single data point in your graph.
Lesson 1039Vertices and Edges definition
Vertices
A Graph is a collection of Vertices (the data points) connected by Edges (the relationships between them).
Lesson 1039Vertices and Edges definition
very first one
If you have ten errors, always focus on the very first one.
Lesson 66Reading compiler error messages
VIP guest list
Think of it like a VIP guest list at a club.
Lesson 791How `#pragma once` works
virtual address space
We ask the OS for a huge range of virtual address space (Reserving).
Lesson 590Growing an arena with virtual memory
VirtualAlloc
On modern systems, we can do this using mmap (Unix) or VirtualAlloc (Windows).
Lesson 590Growing an arena with virtual memory
Visit
Visit a node and mark it so you don't visit it twice.
Lesson 1044Breadth-First Search (BFS) logic
visited
We typically use a visited array—an array of booleans where visited[i] is true if we have already processed node i.
Lesson 1045Depth-First Search (DFS) recursion
visited[i]
We typically use a visited array—an array of booleans where visited[i] is true if we have already processed node i.
Lesson 1045Depth-First Search (DFS) recursion
visitor_count
In the code above, every time greet() is called, the variable visitor_count is born, assigned the value 1, incremented to 2, and then deleted from existence.
Lesson 140Automatic duration variables
void *base
void base*: This is a pointer to the start of your array.
Lesson 876The generic signature of `qsort`
void *dest
void dest*: This is the destination address.
Lesson 512The `memcpy` function signature
void calculate(int age)
When you define a function like void calculate(int age), that age variable is "local" to the function's curly braces {}.
Lesson 141Function parameters as local scope
void celebrate_success(int count)
Simply writing void celebrate_success(int count); (a function prototype) acts as an implicit extern.
Lesson 922Using `extern` with functions
void greet()
When you write a function like void greet(), the compiler translates those lines of code into a series of machine instructions.
Lesson 515Taking the address of a function
void myFunction(int *arr)
void myFunction(int *arr) — The explicit pointer way.
Lesson 474Passing arrays to functions as pointers
void myFunction(int arr[])
void myFunction(int arr[]) — The "array-style" way (which the compiler turns into a pointer anyway).
Lesson 474Passing arrays to functions as pointers
void process_data(char *str)
If they see void process_data(char str), they have to wonder: Does this function modify my string?
Lesson 1198The role of `const` in documentation
volatile
If we removed volatile from the code above, a high-level optimization might see while (status == 0) and think: "Since status is 0 and nothing in this loop changes it, I'll just treat this as while (true)." Your program would then hang forever, even if the memory at the status address changed to 1.
Lesson 913The `volatile` qualifier for hardware mappingLesson 914How `volatile` prevents compiler optimizationLesson 917Combining `const` and `volatile`Lesson 957The `asm` keyword syntaxLesson 958The basic `volatile` asm blockLesson 1147Volatile vs Atomic
volatile sig_atomic_t
When writing handlers, keep them "small and simple." The best practice is often to just set a global flag of type volatile sig_atomic_t and handle the actual logic back in your main loop.
Lesson 1089Signal safety and reentrant functions
VS Code
If you want simplicity and speed, start with a text editor like VS Code.
Lesson 13Using a Text Editor vs IDE

W

wait()
If your parent process needs a file that the child is currently writing, wait() ensures the parent doesn't try to open that file until the child is officially finished.
Lesson 1076Waiting for children with wait()Lesson 1077Capturing child exit statusLesson 1078Preventing zombie processesLesson 1083Combining fork() and exec()
wait(&status)
Use wait(&status) to collect a child's exit info, then use WIFEXITED and WEXITSTATUS to decode how the child finished.
Lesson 1077Capturing child exit status
wait(NULL)
If you removed the wait(NULL) line and told the parent to sleep(100), the child would remain a zombie for 100 seconds.
Lesson 1078Preventing zombie processes
waiter_take_order()
The waiter_take_order() function is public—customers need to interact with it.
Lesson 798Static functions for file scoping
warning: implicit declaration of function 'calculate_area'
warning: implicit declaration of function 'calculate_area'
Lesson 334Common errors with missing prototypes
wash_dishes()
The customers don't need to know it exists, and two different restaurants can both have a wash_dishes() routine without interfering with each other.
Lesson 798Static functions for file scoping
watch
In GDB, the watch command sets a "watchpoint." Unlike a breakpoint (which stops at a specific line of code), a watchpoint stops the program whenever the value of a specific expression or variable changes.
Lesson 836Using `watch` for memory changes
watch <variable>
Use watch <variable> in GDB to automatically pause execution the instant a variable’s value is modified.
Lesson 836Using `watch` for memory changes
WatchStatus
In this example, the entire WatchStatus structure could fit into just 4 bits of a single byte.
Lesson 645Purpose of bit-fields in memory-constrained systems
wb
Because we used "write binary" mode (wb), C copies the actual bits and bytes from your RAM into the file.
Lesson 725Writing raw bytes with fwrite
wb+
The file is automatically opened in wb+ mode (binary read/write).
Lesson 758Creating temporary files with tmpfile
weeks
If you are printing a calendar, the outer loop moves through the weeks, and for every week, the inner loop moves through the seven days.
Lesson 299Inner loop vs outer loop execution order
weight
It will return 1, leaving the weight variable untouched.
Lesson 696Handling the return value of scanf
WEXITSTATUS
Use wait(&status) to collect a child's exit info, then use WIFEXITED and WEXITSTATUS to decode how the child finished.
Lesson 1077Capturing child exit status
WEXITSTATUS(status)
WEXITSTATUS(status): Extracts the actual return value (the 0 or 1) from the child.
Lesson 1077Capturing child exit status
what
The .h file defines what a tool does, while the .c file defines how it does it.
Lesson 377Role of the `.h` fileLesson 508Why you can't dereference `void *`
when they mean
The most common mistake for beginners is using = when they mean == inside an if statement.
Lesson 161The equality operator `==`Lesson 255Common mistake: assignment (=) vs equality (==)
wherever you typed
When you run this code, the output will only show a single \ wherever you typed \\.
Lesson 47Escaping the backslash
which
You must tell C which structure you are talking about before you can ask for a member inside it.
Lesson 633Combining array indexing and member access
while
Think of it like a car journey: a for loop is saying "drive exactly 50 miles," whereas a while loop is saying "keep driving as long as the gas tank isn't empty." You don't know the exact mileage, but you know the condition that keeps you moving.
Lesson 1What is a low-level language?Lesson 266The while loop syntax and execution flowLesson 267The loop condition: when to stopLesson 268Updating the loop variable to avoid infinite loopsLesson 269Using while for indeterminate iterationsLesson 270Reading input until EOF with whileLesson 271Infinite loops: while(1) and while(true)Lesson 272Common error: semicolon after while headerLesson 273Tracing while loop execution on paperLesson 274The do-while syntax and the trailing semicolonLesson 275Guaranteed execution: why do-while is differentLesson 276Using do-while for menu-driven programsLesson 277Using do-while for input re-promptingLesson 278Converting a while loop to a do-whileLesson 279Comparing while vs do-while use casesLesson 280Scope of variables declared inside do-whileLesson 281Pitfall: condition check occurs after executionLesson 292Continue in while vs for loopsLesson 296Readability: when to avoid excessive breaksLesson 308Why goto is generally discouragedLesson 311The dangers of 'spaghetti code'Lesson 314The Sentinel Value patternLesson 321Choosing the right loop for the taskLesson 352Identifying a Stack OverflowLesson 353Concept of self-calling functionsLesson 358Iteration vs. Recursion comparisonLesson 364When to avoid recursionLesson 429Tokenizing strings with `strtok`Lesson 441Reversing a string in placeLesson 446Merging two sorted arraysLesson 482Pointer-based `strcpy` implementationLesson 682Using while loops with getcharLesson 707Checking for NULL return in fgetsLesson 721Detecting the end of a file with feofLesson 722Why feof inside a loop condition is badLesson 992Traversing the list with a while loopLesson 1003Deleting without head traversalLesson 1028Finding Min and Max nodesLesson 1049Insertion Sort: Shifting elementsLesson 1062Binary Search: Iterative approachLesson 1063Binary Search: Recursive approachLesson 1084What are Unix signalsLesson 1132Introduction to condition variablesLesson 1133Waiting with pthread_cond_waitLesson 1134Signaling with pthread_cond_signalLesson 1196Consistency: K&R vs. Allman styleLesson 1206Opening and reading files line-by-line
while (!feof(file))
When you use while (!feof(file)), you are asking: "Have I already hit the end?" The problem is that the "end of file" status is only set after a failed read attempt.
Lesson 722Why feof inside a loop condition is bad
while (*ptr != '\0')
By using while (*ptr != '\0'), you can process strings of any length without needing to know the size of the array beforehand.
Lesson 481Iterating strings until `\0` with pointers
while (*ptr)
Experienced C programmers often shorten this to while (*ptr).
Lesson 481Iterating strings until `\0` with pointers
while (1)
Instead of trying to force a complex condition into the while(...) parenthesis, you can run a simple while (1) and put a break right after the line where you read the user's input.
Lesson 293Using break to exit infinite loops on condition
while (age < 0)
The Logic Check: Once the user hits enter, the program reaches the while (age < 0); line at the bottom.
Lesson 277Using do-while for input re-prompting
while (choice != 3)
Once the user types a number, the while (choice != 3) at the bottom evaluates the situation.
Lesson 276Using do-while for menu-driven programs
while (condition)
By avoiding these jumps, you ensure that the loop header while (condition) tells the whole truth.
Lesson 297Alternative patterns to avoid break and continue
while (cookies > 0)
Here is why: The computer sees while (cookies > 0); as a complete instruction.
Lesson 272Common error: semicolon after while header
while (current != NULL)
To traverse a list, initialize a temporary pointer to the head and use current = current->next inside a while (current != NULL) loop to move from node to node.
Lesson 992Traversing the list with a while loop
while (last->next != NULL)
Unlike an array where you can jump to array[last_index], a linked list requires a "walk." The while (last->next != NULL) loop is the heartbeat of this process.
Lesson 994Appending nodes to the tail
while (scanf(...) != EOF)
Use while (scanf(...) != EOF) to process data continuously until the user or a file signals that there is nothing left to read.
Lesson 270Reading input until EOF with while
while (status == 0)
If we removed volatile from the code above, a high-level optimization might see while (status == 0) and think: "Since status is 0 and nothing in this loop changes it, I'll just treat this as while (true)." Your program would then hang forever, even if the memory at the status address changed to 1.
Lesson 914How `volatile` prevents compiler optimization
while (true)
If we removed volatile from the code above, a high-level optimization might see while (status == 0) and think: "Since status is 0 and nothing in this loop changes it, I'll just treat this as while (true)." Your program would then hang forever, even if the memory at the status address changed to 1.
Lesson 914How `volatile` prevents compiler optimization
while (x < 10)
While we usually give loops a condition to check at the very start (like while (x < 10)), sometimes we don't know exactly when a loop should end until we are halfway through the code inside it.
Lesson 293Using break to exit infinite loops on condition
while( )
In a standard do-while loop, the code inside the braces executes first, and then the condition inside the while( ) parentheses is checked.
Lesson 280Scope of variables declared inside do-while
while(...)
Instead of trying to force a complex condition into the while(...) parenthesis, you can run a simple while (1) and put a break right after the line where you read the user's input.
Lesson 266The while loop syntax and execution flowLesson 293Using break to exit infinite loops on condition
while(1)
An infinite loop created with while(1) or while(true) runs its code block repeatedly until the program is manually stopped or an internal "break" command is triggered.
Lesson 271Infinite loops: while(1) and while(true)Lesson 296Readability: when to avoid excessive breaks
while(condition)
Take the while(condition) statement and move it to the very end of the code block.
Lesson 278Converting a while loop to a do-while
while(temp != NULL)
When working with these lists, a while(temp != NULL) loop will cause an infinite loop because NULL no longer exists!
Lesson 1004Circular doubly linked lists
while(true)
An infinite loop created with while(1) or while(true) runs its code block repeatedly until the program is manually stopped or an internal "break" command is triggered.
Lesson 271Infinite loops: while(1) and while(true)Lesson 296Readability: when to avoid excessive breaks
whiteboard
Think of a normal variable like a whiteboard in a meeting room that gets erased every time the meeting ends.
Lesson 910The `const` qualifier on variablesLesson 920The `static` keyword inside functions
whitespace
In C, the %s specifier reads characters until it encounters whitespace—this includes spaces, tabs, or new lines.
Lesson 422Scanning strings with `%s` and `scanf` limitations
whole bag
We take the weight of the whole bag and divide it by the weight of one apple.
Lesson 388Calculating array size with `sizeof`
wholeNumber
Even the wholeNumber of 10.0 gets the full treatment, appearing as 10.000000.
Lesson 52Printing decimals with `%f`
width
If you have three variables representing the dimensions of a box (length, width, height), putting them on one line makes it clear they belong together.
Lesson 76Multiple declarations in one lineLesson 624Accessing members of nested structs
WIFEXITED
Use wait(&status) to collect a child's exit info, then use WIFEXITED and WEXITSTATUS to decode how the child finished.
Lesson 1077Capturing child exit status
WIFEXITED(status)
WIFEXITED(status): Returns true if the child terminated normally (e.g., return 0 or exit()).
Lesson 1077Capturing child exit status
wild pointer
If you declare a pointer using int *ptr; without giving it an address, it becomes a wild pointer.
Lesson 459Initializing pointers to NULL
will force the result to be
Any position in the mask that is 0 will force the result to be 0 (the "blocked" part of the stencil).
Lesson 180Masking bits with `&`
will not
The strncpy function has a specific quirk you must remember: if the source string is longer than the limit you set, strncpy will fill the destination buffer but will not add the null terminator (\0) at the end.
Lesson 848Copying strings safely with `strncpy`
with signed integers (the standard
One quirk you will notice when using ~ with signed integers (the standard int) is that the result often looks like a completely different negative number.
Lesson 178Bitwise NOT `~` (Complement)
with the
If we simply swapped the 7 with the 12, our array would become {2, 5, 8, 7, 12}.
Lesson 1049Insertion Sort: Shifting elements
word
In reality, the CPU reads memory in "chunks"—usually 4 or 8 bytes at a time (called a word).
Lesson 337Positional matching of argumentsLesson 574Performance cost of unaligned access
words
In reality, the CPU has "large hands." It doesn't pick up one byte at a time; it grabs memory in fixed-size blocks called words.
Lesson 571CPU word size and alignment
Work from the inside out
To safely free a dynamically allocated struct, you must follow a simple rule: Work from the inside out. You must free every pointer member inside the struct before you free the pointer to the struct itself.
Lesson 621Freeing dynamically allocated structs
write end
Imagine a literal physical pipe: you pour water into one end (the write end), and it flows out the other (the read end).
Lesson 1100Anatomy of a pipe
write()
Non-blocking I/O allows your program to remain responsive by forcing read() and write() to return immediately with a "try again" status instead of pausing execution.
Lesson 1089Signal safety and reentrant functionsLesson 1092File descriptors vs FILE pointersLesson 1098Non-blocking I/O basicsLesson 1106Introduction to named pipes (FIFOs)
Writer
By placing a new \0 (null terminator) at the writer position, you tell C that the string ends earlier than it used to.
Lesson 444Removing a character from a string
writing
A Read-Write Lock (pthread_rwlock_t) distinguishes between reading and writing.
Lesson 1138Read-write locks basics
wrongWay
In the code above, wrongWay fails because the division happens between two integers before the result is moved into the float variable.
Lesson 154Floating-point division

X

x * 2
Modern compilers are excellent at optimizing x * 2 into x << 1 automatically.
Lesson 1195Bitwise operations for speed
x + 1
For example, if you try to write (x + 1) = 5;, the computer complains because x + 1 is just a temporary result (a letter), not a permanent mailbox where the 5 can live.
Lesson 191L-values vs R-valuesLesson 934What 'Undefined Behavior' actually means
x << -1
Don't shift by negative numbers: While it might seem logical that x << -1 should just be x >> 1, C does not work this way.
Lesson 187Shift operator constraints
x << 1
Modern compilers are excellent at optimizing x * 2 into x << 1 automatically.
Lesson 186Bit shifting as multiplication/divisionLesson 1195Bitwise operations for speed
x << 2
x << 2 is $x \times 2^2$ (or $x \times 4$)
Lesson 186Bit shifting as multiplication/division
x << 3
x << 3 is $x \times 2^3$ (or $x \times 8$)
Lesson 186Bit shifting as multiplication/division
x << n
Mathematically, x << n is equivalent to $x \times 2^n$.
Lesson 183Left shift `<<` mechanics
x = 10
If you try to use that variable before assigning it a value (e.g., x = 10;), you aren't starting at zero; you are starting with whatever random junk happens to be sitting in that memory slot.
Lesson 220Definition of a side effectLesson 339Local scope of parametersLesson 939Using uninitialized variablesLesson 1143Atomic load and storeLesson 1172Principles of Unit Testing
x = 10; x = 20
Normally, if you write x = 10; x = 20; in a row, a smart compiler skips the first line because it seems useless.
Lesson 1147Volatile vs Atomic
x = 5
In most programming languages, you think of an assignment (like x = 5;) as a command that just "happens." You tell the computer to put 5 into x, and it’s done.
Lesson 197Assignment expression return valueLesson 221Sequence point definitionLesson 255Common mistake: assignment (=) vs equality (==)
x = 5 + 2
This is why, in the line x = 5 + 2;, the computer calculates 5 + 2 first and then performs the assignment.
Lesson 212Operator precedence table
x = x - 1
If you are just subtracting 1 on a line by itself, --x; works exactly like x = x - 1;.
Lesson 200Prefix decrement `--x`
x = x | 5
For example, if you want to turn on a specific bit (using OR) or mask out certain bits (using AND), you might write x = x | 5;.
Lesson 195Compound bitwise assignments
x == 5
If you write x == 5, you are asking the computer, "Is the value currently inside x the same as 5?" The computer answers this question with either a 1 (true) or a 0 (false).
Lesson 255Common mistake: assignment (=) vs equality (==)
x > 10
The Condition: A test that results in true or false (e.g., x > 10).
Lesson 252The ternary operator (?:) as a shortcut
x > y + z
In the example x > y + z, C calculates y + z first because addition outranks comparison.
Lesson 219Common precedence errors
x >> 1
Don't shift by negative numbers: While it might seem logical that x << -1 should just be x >> 1, C does not work this way.
Lesson 187Shift operator constraints
x--
When you use x-- in a line of code, C uses the original value of x for whatever calculation is happening right now, and only after that calculation is finished does it subtract one from the variable.
Lesson 201Postfix decrement `x--`
x/nfu <address>
The command follows a specific pattern: x/nfu <address>.
Lesson 839Examining raw memory with `x`
x86
The two main titans you will encounter are x86 (Complex Instruction Set) and ARM (Reduced Instruction Set).
Lesson 963Platform-specific assembly (x86 vs ARM)

Y

y + z
In the example x > y + z, C calculates y + z first because addition outranks comparison.
Lesson 219Common precedence errors
YELLOW
Because C lacks true namespacing for enums, the names RED, YELLOW, and OK become global constants.
Lesson 662Default integer values in enumsLesson 666Scoped enum limitations in C
You cannot change the characters
There is one major rule when using pointers with string literals: You cannot change the characters.
Lesson 479String literals as `char` pointers
Your current accuracy is 95%
When you run this code, the output will look perfectly normal: Your current accuracy is 95%. The compiler consumes both symbols but only displays one to the user.
Lesson 690Escaping the percent sign %%
Your health is: -20
On one run, this might print Your health is: -20.
Lesson 939Using uninitialized variables
Your health is: 32747
On another, it might print Your health is: 32747.
Lesson 939Using uninitialized variables

Z

zero
The most important rule in C arrays is that we start counting from zero.
Lesson 386Accessing elements with the `[]` operatorLesson 410Accessing elements using `[row][col]`
zero extra memory
This technique is powerful because it requires zero extra memory beyond a single head pointer.
Lesson 579Building a simple free list
zero performance penalty
This happens before the program even runs, meaning there is zero performance penalty compared to calling printf manually.
Lesson 975Implementing a generic 'Print' macro
zero-based indexing
Remember that C uses zero-based indexing.
Lesson 387Modifying individual array elements
zero-indexed
Because C arrays are zero-indexed, a 10-element array starts at index 0 and ends at index 9.
Lesson 405Avoiding off-by-one errors in loops
zero-initialization overhead
This process is known as zero-initialization overhead.
Lesson 545Zero-initialization overhead
zeros
In C, those empty seats on the right are always filled with zeros.
Lesson 183Left shift `<<` mechanics
zipCode
It prevents "namespace pollution," where you might accidentally mix up a zipCode variable for a user with a zipCode variable for a shipping warehouse.
Lesson 623Defining a struct inside another struct