Programming with C

Undergraduate, Giresun University, Computer Engineering, 2024

procedural programming with c C is a general-purpose programming language widely used in systems programming, embedded software, operating systems, compilers, and performance-sensitive applications. It combines portable high-level constructs with low-level access to memory and hardware-related operations. C gives programmers substantial control over data representation, memory allocation, and program execution. This control can enable efficient software, but it also requires careful handling of memory, types, and undefined behavior.

This is a complete course in C, designed to take a reader from no prior programming experience to competent, independent, professional C development. It is organized as 56 weekly topics in four levels. Basic builds the language core, early programming habits, and the first command-line programs, ending with pointers and strings. Intermediate covers memory, aggregate data, files, error handling, modular design, and project workflow. Advanced covers libraries and tooling, data structures, performance, undefined behavior, portability, low-level systems interfaces, network programming, and concurrency. Professional covers working in existing codebases, collaboration and review, library distribution, language interoperability, scalable I/O, security testing, embedded and bare-metal development, and long-term maintenance. Each topic assumes only what earlier topics have already established.

Every week is anchored to a worked example that is built and explained in the lecture notes. The examples form a chain rather than a set of isolated exercises: the growable array of week 19 becomes the reusable module of week 28, the library of week 29, the installable package of week 45, and the target of a foreign-language binding in week 46; the socket server of week 40 becomes the event loop of week 47. The embedded material in weeks 49-55 targets an ARM Cortex-M image running under QEMU, so every example can be built and run without physical hardware.

Ders Öğretim Planı

Ders Öğretim Planı (HTML)


The book

The whole course as a single PDF: 758 pages, fifty-six chapters in four parts, with the worked examples, the figures and the self-check questions and answers in place.

📕 Programming with C (PDF, 8.7 MB)

It is generated from the notes on this site, so the two never drift apart; the listings are the same ones the companion repository compiles on every change.


Code repository

Every program in the lecture notes lives in a repository, one directory per week, each with a Makefile and a README that links back to the week it came from.

git clone https://github.com/sercankulcu/programming-with-c
cd programming-with-c
make week01

💻 sercankulcu/programming-with-c

Most weeks are plain standard C and build the same way on Linux, macOS and Windows. Weeks 31, 39-41, 47 and 48 use POSIX interfaces that Windows does not provide in a form that would teach the same lesson, so run those under WSL — which week 1 already sets up. Weeks 49-55 cross-compile for ARM Cortex-M and run under QEMU, so they need no hardware.


Announcements

There are currently no announcements.


Course resources


Preliminary Materials

  • Prerequisites for preparation html
  • Key figures who have shaped the field html

Basic (Weeks 1-14)

Week 1: Computers, Programs, and the C Toolchain

  • How a computer executes a program: processor, memory, and storage
  • Compilers, interpreters, and assemblers
  • Installing a compiler and setting up an editor or IDE
  • Writing, compiling, and running a first program
  • Reading and understanding compiler messages

Worked example: a first program compiled and run from the command line, then broken on purpose so the compiler’s diagnostics can be read and understood.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 2: History, Standards, and the Compilation Model

  • Origins of C: BCPL, B, and Unix
  • K&R C, ANSI C, and ISO standardization
  • C89, C99, C11, C17, and C23, and what each added
  • Translation phases: preprocessing, compilation, assembly, and linking
  • Selecting a standard and useful flags: -std=, -Wall, -Wextra, -g

Worked example: one source file compiled under -std=c89 and -std=c23, with the preprocessed, assembled, and linked output inspected at each stage.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 3: Program Structure, Style, and Basic Output

  • main, its return value, EXIT_SUCCESS, and EXIT_FAILURE
  • #include and the standard library
  • Statements, blocks, semicolons, and braces
  • Comments, indentation, naming, formatting, and readability
  • Producing output with printf
  • Basic source-code workflow: folders, files, Git repositories, commits, and .gitignore

Worked example: a badly formatted program rewritten for readability and placed under version control with a meaningful first commit.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 4: Data Representation

  • Binary, octal, and hexadecimal number systems
  • Bits, bytes, and words
  • Two’s complement representation of signed integers
  • IEEE 754 representation of floating-point numbers
  • Character encoding and the ASCII table

Worked example: a program that prints an integer in binary, octal, and hexadecimal, and dumps the raw bytes of a float to expose its sign, exponent, and mantissa.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 5: Variables, Types, and Constants

  • Identifiers, declarations, and initialization
  • int, char, float, double, _Bool, and <stdbool.h>
  • Type modifiers: short, long, signed, unsigned
  • sizeof, size_t, <limits.h>, and <float.h>
  • Literals, escape sequences, const, and #define

Worked example: a program that reports the size and value range of every basic type on the host machine, compared with the minimum guarantees the standard actually makes.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 6: Operators and Expressions

  • Arithmetic, relational, and logical operators
  • Assignment and compound assignment
  • Increment, decrement, and the comma operator
  • Precedence, associativity, and parenthesization
  • Evaluation order, sequence points, and side effects

Worked example: a set of expressions whose values are predicted on paper and then checked against the program, including one whose evaluation order the standard does not fix.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 7: Type Conversions

  • Implicit conversion and integer promotion
  • The usual arithmetic conversions
  • Explicit conversion with casts
  • Mixing signed and unsigned types
  • Integer overflow, truncation, and floating-point precision
  • Floating-point comparison, accumulated error, and the special values NaN and infinity

Worked example: a program in which a signed-unsigned comparison loops forever and a sum of decimal fractions fails an equality test, with both diagnosed and repaired.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 8: Formatted Input and Output

  • printf format specifiers, width, precision, and flags
  • scanf and reading user input
  • Input buffer problems and error recovery
  • Character-at-a-time input and output with getchar and putchar
  • Validating user input and reporting input errors clearly

Worked example: an interactive prompt that reads numbers, rejects malformed input, clears the input buffer, and never spins forever on bad data.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 9: Conditional Statements

  • if, else if, and else
  • Nested and chained conditions
  • The conditional (ternary) operator
  • switch, case, default, and fall-through
  • Short-circuit evaluation and common logic errors

Worked example: a classifier written twice, once as an if/else if chain and once as a switch, with the trade-offs of each made explicit.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 10: Loops and Flow Control

  • for, while, and do-while
  • Loop counters, invariants, and termination conditions
  • Nested loops
  • break, continue, and goto
  • Infinite loops and off-by-one errors

Worked example: a prime sieve and a formatted multiplication table, with the loop invariant of each stated explicitly and every boundary case checked.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 11: Functions

  • Declaration, definition, and calling
  • Parameters, return values, and void
  • Function prototypes and their placement
  • Pass-by-value semantics and the call stack
  • Designing small, single-purpose functions with clear success and failure behavior

Worked example: a monolithic program decomposed into named functions, ending with a swap function that fails and a first look at why it cannot work yet.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 12: One- and Multi-Dimensional Arrays

  • Declaring, initializing, and traversing arrays
  • Indexing with size_t and respecting array bounds
  • Out-of-range access and its consequences
  • Two-dimensional arrays and row-major memory layout
  • Common array algorithms: sum, minimum and maximum, reversal, linear search

Worked example: a matrix transpose with explicit bounds checking, and a demonstration of exactly what an out-of-range write corrupts.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 13: Introduction to Pointers

  • Memory addresses and the address-of operator
  • Pointer declaration, initialization, and dereferencing
  • NULL, uninitialized, and wild pointers
  • Pointer types, void *, and type safety
  • Array-to-pointer decay, passing arrays to functions, and why sizeof changes inside them

Worked example: the failed swap function from week 11 made to work, plus an array-length bug caused by sizeof on a parameter, found and explained.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 14: Strings and Character Handling

  • Character arrays, the null terminator, and string literals
  • Reading and printing strings with fgets and puts, and why gets must never be used
  • <string.h>: strlen, strcpy, strncpy, strcmp, strcat, strchr, strstr, strtok
  • Block memory operations: memcpy, memmove, memset, and memcmp
  • <ctype.h> classification, buffer sizes, truncation, and safe string building with snprintf

Worked example: a small string toolkit — length, copy, compare, search — written by hand and then replaced with the library versions, including the crash that follows from writing to a string literal.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Intermediate (Weeks 15-28)

Week 15: Pointer Arithmetic and Arrays

  • Incrementing, decrementing, and comparing pointers
  • Pointer subtraction and ptrdiff_t
  • Traversing arrays and strings with pointers
  • Pointers to pointers and arrays of pointers
  • Pointer-based versus index-based idioms

Worked example: an array traversal rewritten from indexing to pointer walking, with the readability and the generated code of both versions compared.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 16: Pointers as Function Parameters

  • Emulating pass-by-reference
  • Modifying caller variables and output parameters
  • const pointers, pointers to const, and const correctness as a discipline
  • Returning pointers safely and avoiding dangling returns
  • Passing strings and multidimensional arrays
  • Reading and writing complex declarations: the right-left rule and the cdecl tool

Worked example: a parse function that returns a status code and writes its result through an output parameter, plus a set of hairy declarations decoded one qualifier at a time.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 17: Scope, Lifetime, and Program Memory Layout

  • Block, file, and function scope
  • Automatic and static storage duration
  • Local variables, global variables, and static locals
  • The memory layout of a running program: text, rodata, data, bss, stack, and heap
  • The call stack: frames, parameters, locals, and return addresses
  • Shadowing, name conflicts, and the cost of global state

Worked example: a program that prints the address of a local, a static, a global, a string literal, and a heap block, revealing the whole memory map of the process in a single run.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 18: Recursion

  • Base cases and recursive cases
  • Call stack behavior and stack frames
  • Classic examples: factorial, Fibonacci, Towers of Hanoi, and binary search
  • Recursion depth, stack overflow, and tail calls
  • Recursive versus iterative solutions

Worked example: Towers of Hanoi and a recursive binary search stepped through in a debugger, watching the stack frames from week 17 grow and unwind.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 19: Dynamic Memory Allocation

  • The heap versus the stack
  • malloc, calloc, realloc, and free
  • Checking allocation failures
  • Growable arrays and dynamically allocated two-dimensional arrays
  • Ownership, lifetime, allocation conventions, and init/destroy pairs

Worked example: a growable array that doubles its capacity with realloc and frees cleanly — the data structure the rest of the course keeps coming back to.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 20: Memory Errors and Their Diagnosis

  • Memory leaks
  • Dangling pointers and use-after-free
  • Double free and invalid free
  • Buffer overruns and out-of-bounds access
  • Detecting memory errors with sanitizers and Valgrind

Worked example: the week 19 array seeded with a leak, a use-after-free, and a one-byte overrun, each found and fixed with a different tool.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 21: Structures

  • Declaration, initialization, and designated initializers
  • Member access with . and ->
  • Nested structures and arrays of structures
  • Structures as parameters and return values
  • Pointers to structures and self-referential types

Worked example: a record type held in an array of records, passed both by value and by pointer, with the copying cost of each measured.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 22: Unions, Enumerations, and Type Aliases

  • Enumerated types and their underlying representation
  • Type aliases with typedef
  • Unions, shared storage, and tagged unions
  • Structure padding and offsetof
  • Choosing between a structure, a union, and an enumeration

Worked example: a tagged union that holds an integer, a double, or a string, and a demonstration of how reordering struct members changes sizeof.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 23: Program Arguments and Input

  • Program arguments with argc and argv
  • String-to-number conversion: atoi, strtol, strtod, and the endptr argument
  • Validating arguments and rejecting malformed input
  • Taking input from arguments versus from stdin
  • Writing programs that compose well in a shell pipeline

Worked example: a numeric filter that takes its parameters as arguments, validates every one of them, and reads its data from standard input.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 24: The Standard Library Toolbox

  • <math.h>: numeric functions, linking with -lm, and floating-point pitfalls
  • <time.h>: time_t, struct tm, formatting, and measuring elapsed time
  • Pseudo-random numbers: rand, srand, and their limitations
  • What the standard library does and does not provide, and when to reach for a third-party library
  • Reading manual pages, library documentation, and the text of the standard itself

Worked example: a program that generates a random data set, computes statistics over it, and reports how long the computation took.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 25: Bit-Level Operations

  • Bitwise AND, OR, XOR, and NOT
  • Left and right shifts; arithmetic versus logical shift
  • Bit masks: setting, clearing, toggling, and testing flags
  • Bit-fields and their portability limitations
  • Practical uses: permissions, packing, and checksums

Worked example: a permission-flag system and a population-count routine, with several implementations compared for clarity and for speed.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 26: Files and Streams

  • Streams, the FILE type, fopen modes, and fclose
  • Text versus binary access; fprintf, fscanf, fgets, and fputs
  • fread and fwrite for binary records
  • Positioning with fseek, ftell, rewind, fgetpos, and fsetpos
  • Stream state and buffering: feof, ferror, clearerr, fflush, and setvbuf
  • stdin, stdout, stderr, redirection, temporary files, and portable file-processing pitfalls

Worked example: a program that reads a delimited text file line by line, and a second that stores the same data as fixed-size binary records and seeks directly to any one of them.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 27: Error Handling Strategies

  • Compile-time, link-time, and runtime errors
  • Checking function return values consistently
  • Error reporting with errno, perror, and strerror
  • Assertions with assert and _Static_assert
  • Resource cleanup with the goto idiom, error-reporting conventions, and API-level error contracts

Worked example: the file reader from week 26 rewritten around a single error contract with one cleanup path, leaking no handle on any failure route.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 28: The Preprocessor and Modular Programming

  • Object-like and function-like macros; parenthesization and double evaluation
  • Stringification with #, token pasting with ##, conditional compilation, and predefined macros
  • Separating declarations from definitions; designing header and source files
  • Include guards, #pragma once, extern, linkage, and multiple-definition errors
  • Information hiding with static and opaque pointers
  • Project layout, public versus private headers, API documentation, and ownership comments

Worked example: the growable array of week 19 turned into a reusable module with a public header, a hidden implementation, and debug logging the preprocessor can switch off entirely.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Advanced (Weeks 29-42)

Week 29: Building Programs and Libraries

  • Compiling and linking multiple translation units
  • Writing and maintaining a Makefile
  • Project configuration with CMake
  • Static libraries with ar, and shared and dynamic libraries
  • Debug and release builds, sanitizer builds, test targets, and warning profiles
  • Linking order, symbol resolution, and inspection with nm and objdump

Worked example: the week 28 module built as both a static and a shared library, driven first by a Makefile and then by an equivalent CMake configuration.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 30: Function Pointers, Callbacks, and Generic Programming

  • Declaring, assigning, and calling function pointers
  • Passing functions as arguments; qsort and bsearch with custom comparators
  • Arrays of function pointers, dispatch tables, and simulating polymorphic behavior
  • Generic containers with void *
  • Variable argument lists: va_list, va_start, va_arg, va_end, and writing a printf-like function
  • Type-based selection with _Generic and macro-based generic data structures

Worked example: the week 19 array generalized to hold any element type through void * and a comparison callback, then sorted with qsort.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 31: Command-Line Interfaces and the Environment

  • Parsing command-line options by hand and with getopt
  • Program termination: exit, _Exit, and atexit
  • Environment variables with getenv and setenv
  • Configuration through arguments, files, and environment variables
  • Designing scriptable, testable interfaces: exit status, stdout versus stderr, and clear diagnostics

Worked example: a filter tool with short and long options, a --help screen, configuration layered from file and environment, and exit codes a shell script can act on.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 32: Linked Data Structures

  • Self-referential structures and node allocation
  • Singly, doubly, and circular linked lists
  • Stacks and queues
  • Insertion, deletion, traversal, and reversal
  • The cost of list operations and when a list beats an array

Worked example: a doubly linked list and a queue built on one node type, benchmarked against the week 19 array for insertion in the middle.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 33: Trees, Hash Tables, and Abstract Data Types

  • Binary trees and binary search trees
  • Traversals and recursive tree algorithms
  • Hash functions, collision handling, and hash tables
  • Time and space complexity: big-O notation and practical cost
  • Designing an abstract data type with an opaque handle and a stable API boundary

Worked example: a hash table and a binary search tree placed behind the same opaque interface, with lookup cost measured against each other as the data set grows.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 34: Performance and Memory Layout

  • The memory hierarchy, cache behavior, and locality of reference
  • Data layout, structure packing, and their effect on speed
  • Optimization levels and when to trust the compiler
  • Reading the compiler’s generated assembly with Compiler Explorer and objdump
  • Profiling and measurement with perf, gprof, and callgrind
  • Trade-offs between clarity and speed

Worked example: a cache-hostile matrix loop rewritten for locality, with the speedup measured and the generated assembly of both versions read side by side.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 35: Debugging, Testing, and Analysis

  • Compiler warnings as the first line of defense
  • Interactive debugging with GDB: breakpoints, watchpoints, and backtraces
  • Static analysis tools
  • Unit testing in C and measuring code coverage
  • Reducing a failure to a minimal reproducible case
  • Continuous integration concepts: automated builds, tests, warnings, and sanitizer runs

Worked example: a test suite for the week 33 abstract data type, run under sanitizers with coverage reporting in a continuous integration configuration.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 36: Undefined Behavior and Secure Coding

  • What undefined behavior is and how compilers exploit it
  • Common sources: overflow, uninitialized reads, and invalid pointers
  • Strict aliasing, type punning, and the role of restrict
  • Implementation-defined and unspecified behavior
  • Buffer overflows, format-string flaws, and integer-overflow vulnerabilities
  • Defensive practices and secure-coding guidelines

Worked example: a program that behaves correctly at -O0 and incorrectly at -O2 because of undefined behavior, diagnosed with a sanitizer and then corrected.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 37: Portability and the Machine Model

  • Type sizes and fixed-width types in <stdint.h>
  • Endianness and byte-order conversion
  • Alignment with _Alignas and _Alignof, and packed data
  • Platform, compiler, and library differences
  • Character encoding: wide characters, <wchar.h>, and UTF-8
  • Portable serialization: binary file formats, padding, and versioned records

Worked example: a binary record format written on one machine and read back correctly on a machine with the opposite byte order and different alignment rules.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 38: Type Qualifiers, Inlining, and Modern C

  • volatile, restrict, and register
  • inline and static inline
  • Compound literals, and anonymous structures and unions
  • Variable-length arrays and their trade-offs
  • _Noreturn and non-local jumps with setjmp and longjmp

Worked example: a hot loop compiled with and without restrict, the difference read straight from the generated assembly, and a lookup table built as a compound literal.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 39: Processes, Signals, and Low-Level I/O

  • File descriptors and the open, read, write, and close interface
  • Creating processes with fork and exec
  • Waiting on child processes, exit status, and system
  • Signals, signal handlers, and async-signal safety
  • Pipes and redirection

Worked example: a miniature shell that forks, executes a command, connects two commands with a pipe, and handles an interrupt without leaving zombies behind.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 40: Network Programming with Sockets

  • The client–server model, TCP versus UDP, and the Berkeley socket API
  • Addresses, ports, byte-order conversion with htons and htonl, and name resolution with getaddrinfo
  • Writing a TCP client: socket, connect, send, and recv
  • Writing a TCP server: bind, listen, accept, and serving clients with a forked child per connection
  • Message framing, partial reads and writes, timeouts, and network error handling

Worked example: a line-based TCP server and a matching client talking to each other over a real socket, with partial reads and writes handled correctly.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 41: Concurrency and the C Memory Model

  • Threads with C11 <threads.h> and POSIX threads
  • Race conditions, mutexes, and condition variables
  • Thread-local storage with _Thread_local
  • Atomics, <stdatomic.h>, and the C memory model
  • Reentrancy and thread safety in the standard library: errno, strtok_r, and localtime_r
  • Deadlock, starvation, and debugging concurrent programs

Worked example: a producer–consumer pipeline guarded by a mutex and a condition variable, with a real data race first exposed by a thread sanitizer and then eliminated.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 42: Reading Excellent C

  • Why C looks the way it does: array decay, null-terminated strings, and declaration-mimics-use
  • Why undefined behavior exists, and what the standard deliberately leaves to the implementation
  • Reading real codebases: sqlite, musl, lua, redis, and git
  • Recognizing house style, error conventions, and ownership conventions in unfamiliar code
  • Extracting idioms worth borrowing, and recognizing ones that should not be

Worked example: one self-contained module from a well-regarded open-source C project read end to end, with its memory-ownership rules reconstructed from the code alone.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Professional (Weeks 43-56)

Week 43: Working in an Existing Codebase

  • Navigating unfamiliar C with grep, ctags, language servers, and call graphs
  • Reading before writing: entry points, data flow, and hidden invariants
  • Following conventions, naming, and error-handling contracts you did not choose
  • Locating a regression with git bisect
  • Making a minimal, reviewable change to code you did not write

Worked example: a real defect located and fixed in an unfamiliar open-source C project, with the change kept as small as the fix allows.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 44: Collaborative Development and Code Review

  • Branching, merging, rebasing, and resolving conflicts
  • Pull requests and the review workflow
  • Reviewing C for memory safety, ownership, and undefined behavior
  • Commit hygiene, changelogs, and semantic versioning
  • Issue reports, reproduction cases, and regression tests

Worked example: a change submitted as a pull request with a regression test, and a review written on someone else’s C patch against a memory-safety checklist.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 45: Packaging and Distributing C Libraries

  • Designing a stable public API and a versioning policy
  • Shared library versioning: soname, symbol versioning, and ABI compatibility
  • Installation layouts, pkg-config, and consuming a library from another project
  • Header hygiene for published headers: namespacing, includes, and visibility
  • Deprecation, migration paths, and backward compatibility

Worked example: the week 29 library given a version number, a pkg-config file, and an install target, then consumed from a separate project that sees only the public header.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 46: ABI and Interfacing with Other Languages

  • Calling conventions, stack frames, and the platform ABI
  • Name mangling, extern "C", and linking C with C++
  • Exposing a C API to Python with ctypes and CFFI, to Rust, and to Java with JNI
  • Passing structures, callbacks, and memory ownership across a language boundary
  • Inline assembly and compiler intrinsics

Worked example: the week 45 library called from Python and from C++, with ownership of every allocation made explicit at the boundary.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 47: Event-Driven and Scalable I/O

  • Blocking versus non-blocking file descriptors
  • Multiplexing with select, poll, and epoll or kqueue
  • Structuring an event loop
  • Timers and signal handling inside an event loop: signalfd and the self-pipe trick
  • Thread pools versus event loops, and the C10K problem

Worked example: the week 40 server rewritten as a single-threaded event loop serving thousands of simultaneous connections, then measured against the forking version.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 48: Fuzzing and Security Testing

  • Coverage-guided fuzzing with libFuzzer and AFL++
  • Writing a fuzz target and building a seed corpus
  • Combining fuzzing with sanitizers and assertions
  • Triaging crashes, minimizing inputs, and reporting vulnerabilities
  • Threat modeling and conducting a security review of C code

Worked example: a fuzz target for the input parser of week 23, run until it produces a crash, with the failing input minimized and the defect fixed.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 49: Embedded C and Cross-Compilation

  • Microcontroller architecture: flash, SRAM, and peripherals
  • Cross-compilers, target triples, and toolchain setup with arm-none-eabi-gcc
  • Freestanding versus hosted environments, and life without a full standard library
  • Startup code, the reset vector, and how main is reached
  • Building a bare-metal image and running it under QEMU, with no physical hardware required

Worked example: a blinking-LED program cross-compiled for an ARM Cortex-M target, run under qemu-system-arm, and traced from the reset vector to the first line of main.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 50: Memory-Mapped I/O and Hardware Registers

  • The memory map and peripheral register blocks of the emulated machine
  • Why hardware registers require volatile and what the compiler may otherwise do
  • Register access with bit masks, bit-fields, and register-description structures
  • Writing simple GPIO, timer, and UART drivers
  • Reading a datasheet and a reference manual

Worked example: a UART driver written directly against the emulated machine’s hardware registers, and the same driver breaking as soon as volatile is removed.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 51: Interrupts and Real-Time Behavior

  • The interrupt vector table and interrupt service routines
  • Priorities, nesting, and interrupt latency
  • Sharing data between an ISR and main code: volatile, atomics, and critical sections
  • Polling versus interrupts, and input debouncing
  • Measuring and bounding response time in simulated cycles

Worked example: a button handler implemented first by polling and then by interrupt, with the response latency of each measured in simulated cycles.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 52: Linker Scripts and Firmware Memory Layout

  • Sections: .text, .rodata, .data, and .bss
  • Linker scripts and placing code and data in specific memory regions
  • The startup sequence: copying .data and zeroing .bss
  • Stack and heap placement, and detecting stack overflow
  • Inspecting an image with size, objdump, and map files

Worked example: a firmware image taken apart section by section, its map file read, and a variable deliberately relocated into a different memory region.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 53: Constrained-Resource Programming

  • Programming without malloc: static allocation, memory pools, and ring buffers
  • Fixed-point arithmetic and avoiding floating point
  • Lookup tables, code size, and optimizing for space with -Os
  • Power management and low-power modes
  • Watchdog timers, brown-out detection, and fault handlers

Worked example: a fixed-size memory pool and a fixed-point filter running with no heap at all, with the code-size cost of each design choice measured.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 54: Real-Time Operating Systems

  • The bare-metal superloop versus an RTOS
  • Tasks, scheduling policies, and priorities
  • Queues, semaphores, and mutexes in an RTOS
  • Priority inversion and how to avoid it
  • Choosing between a superloop, an RTOS, and an event-driven design

Worked example: the same device firmware written first as a superloop and then as a set of FreeRTOS tasks running under the simulator, with the timing behavior of both compared.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 55: Embedded Debugging and Testing

  • Source-level debugging of a running image with GDB attached to the simulator’s gdb server
  • Tracing on constrained targets: semihosting, logging, and trace buffers
  • Hardware abstraction layers that make firmware testable on a host
  • Host-side unit tests and simulator-in-the-loop testing
  • Observing timing and peripheral traffic with simulator tracing and instruction counting

Worked example: the week 50 driver refactored behind a hardware abstraction layer, unit-tested on the host, and then stepped through inside the simulator under GDB.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)


Week 56: Maintaining Long-Lived C Codebases

  • Reading and modernizing legacy and K&R-era code
  • Writing characterization tests before refactoring
  • Refactoring incrementally without changing observable behavior
  • Migrating between C standards, compilers, and platforms
  • Managing technical debt, deprecation, and long-term support

Worked example: a piece of legacy C brought under characterization tests, then refactored and migrated to a modern standard with the tests proving the behavior never changed.

🗒️Lecture Notes (HTML) · 💻 Code (GitHub)