Detailed contents
The contents opened out to the section level --- for going straight to a place.
Searches headings and index terms --- not the full text
Part I — Ground: the machine and its memory
1.1What is programming1.2Where C is1.3The world of C is in motion right now1.4Why this book was made1.5How to read this book
2.1Drawing a computer as three parts2.2The bit — the smallest unit of information2.2.1And yet the mathematics points at three2.2.2A ternary computer that was actually built — Setun2.2.3So why did binary win?2.3C was born in the days of that simple machine
3.1The locker corridor3.2The word — the machine’s natural handful3.3Endianness — two orders for putting a number in several slots3.4Is this number a real address?3.5The archetype of C is here
4.1What is in locker 04.2The three nulls — three things alike only in name4.3Alignment — a two-slot load cannot go just anywhere4.4The trick of the low bits — tagged pointers
5.1Four regions, one metaphor5.2Why the static region divides in two — the strange name bss5.3The characters of the three regions contrasted5.4How far this sketch holds5.4.1The standard has no word “stack”5.4.2Machines that really are different — today5.5Where this sketch will be used
Part II — What the machine really is: representation and execution
6.1Unsigned integers — numbers that go round like a clock6.2Three agreements for holding negative numbers6.3The competition of the three, and C23′s decision6.4Shift — pushing bits wholesale6.5Sign extension — from a narrow container to a wide one6.6Seeing it with your own eyes
7.1Fixed point — pinning the decimal point down by agreement7.2Floating point — carrying the point in the data7.3How the bits are really divided7.4Three incidents caused by approximation7.5The chaos, and the contract called IEEE 7547.6Seeing it with your own eyes
8.1A character is a number8.2ASCII and EBCDIC — two tables8.2.1What “the alphabet is not consecutive” does to code8.2.2Which is why there are libraries — what an abstraction does8.3ISO 646 — national variants and C’s trigraphs8.4A hundred schools of eight bits, and Hangul8.5Unicode and UTF-8 — one table, a clever way of holding it8.6The two character sets C keeps apart — source and execution8.6.1Where the crossing happens — translation phases 1 and 58.6.2The more precise name C23 added — the literal encoding8.6.3Prefixes that nail the encoding down8.7Same letter, several representations — the security terrain hidden in text8.8Sequences of letters — ways of holding a string
9.1Punched cards — one card is one line9.2Line printers — output by the line9.3Printing terminals — a conversation on paper, and tty9.4Screen terminals, and streams as the legacy9.5There are tubs along the band — buffers9.6Swapping the band — redirection and pipelines
10.1A program is a noun, a process is a verb10.2How a process is born10.3The life of a process10.4Two modes — what may be done and what may not
11.1One core, several jobs — the idea of taking turns11.2When to cut off — three candidates11.2.1Why giving way is not enough by itself11.3The kinds of multitasking11.4What is carried across a switch — the context11.5Who announces the moment — the timer interrupt11.6The wires to a device — one after another, or all at once11.7When the outside speaks11.7.1When several things interrupt11.7.2What “asking the device” actually means11.7.3When another arrives mid-handler — how existing code copes11.8How the two differ — interrupts and multitasking11.9It was once almost everything a personal computer did11.10Where this story survives today
12.1A confession — that picture was too simple12.2Registers — the CPU’s own hand, there all along12.3The widening gap — between register and memory12.4The cache — a middle layer wedged into the gap12.5The cache divides — a ladder of layers12.6Measuring it — same count, different order12.7Another ladder — virtual memory12.7.1From segments to paging12.7.2The TLB — the translation gets a cache too12.7.3Page faults, and lazy memory12.7.4Protection — why a null dereference usually dies at once
13.1The pipeline — instructions on an assembly line13.2Branch prediction — guessing the fork in advance13.3Measuring it — an easy condition and a hard one13.4The limit of the clock, and multicore13.5The same era: C wrote a contract — pre-standard C and C8913.6The places the contract does not cover — grey areas13.7When a standard outside C promises instead
14.1The editor’s workshop — what a compiler does14.2The abstract machine — the virtual machine inside the contract14.3Visible edits — when a value is read and when it is written14.4Refining the contract — C99, C1114.5A false signal — the ghost summoned by breaking strict aliasing
15.1Completing the thesis — putting three pieces together15.2The current direction — pointers acquire an origin15.3A closing panorama — which machine’s language is C?15.4Closing Part II
Part III — The first program
16.1The first program16.2The terminal and the shell — where a program is run16.2.1Which windows and which workers16.2.2What a shell does — read, split, run16.2.3Running the executable you made16.2.4Passing arguments — how a program is handed values16.3Compiling and running from the command line16.3.1GCC (Linux, macOS, MinGW)16.3.2Clang (the macOS default; Linux and Windows too)16.3.3MSVC (Windows)16.4And so the first program comes out like this16.5Reading it line by line16.6But what happened between source and execution?
17.1From outside — a single command17.2Runner 1 — preprocessing: patchwork on text17.3Runner 2 — compiling: C into assembly17.4Inside runner 2 — the lexer and the parser17.5Runner 3 — assembling: stamping out machine instructions17.6Runner 4 — linking: joining pieces and filling holes
18.1Only three things are needed18.2If installing is too much — try it straight in a browser18.2.1Study options — the same on the web18.3Warnings — leave the free review switched on18.4When Korean text breaks — the chain of encodings18.4.1Nailing ② and ③ down for the compiler18.5The debugger — stopping to look inside18.6Debug-build options — -g and -O0 are different switches18.7The debugger’s blind spot — optimised builds18.8“It works in debug but is wrong only in release”18.9Tools for when the debugger cannot be trusted18.10Sanitizers — a net cast while the program runs18.11Where we have got to
19.1The big three — GCC, Clang/LLVM, MSVC19.2The compilers that sell performance — vendor compilers19.3Embedded — the doorway to the last chapter19.4An aside — Pelles C, the tidy alternative on Windows19.4.1Writing a Windows app in C19.4.2What comes in the one package — the resource editor and dialog designer19.5Others, and the retired names19.6Where the official material lives
Part IV — A minimal toolbox
20.1Comments — writing only humans read20.2Statements — one step, and the semicolon20.3Blocks — a bundle of statements20.4Lines that are not statements — the world of #20.5Putting it together — a two-statement program
21.1Literals — values written in source21.2Writing constants — all of them at a glance21.3Integer constants (§6.4.4.1)21.4The C23 digit separator '21.5Character constants (§6.4.4.5)21.6Floating constants (§6.4.4.3)21.7String literals (§6.4.5)21.8Things that look like constants21.9constexpr — the real constant C23 brought in21.9.1What it is you are writing21.9.2How it differs from const21.9.3The value has to fit exactly21.9.4What happens at file scope21.10Expressions — the things that are evaluated into values21.11Order — precedence, and the practice of parentheses21.12One caution planted early — the order in time may differ
22.1The call — say the name and a worker runs22.2The return value — used, or discarded22.3The standard library — workers made in advance
23.1printf — output with a format23.2Kinds of blank — a slot per kind of value23.3Lining things up — width and precision23.4Output has more than one window23.5Matching — the contract between format and materials23.6Closing Part IV — rereading hello world
Part V — Declarations: how names are made
24.1Declaration — type, name, and a first value24.2Assignment — the side effect that changes state24.3const — the promise not to change
25.1Definition — the syntax for making a worker25.2Declaration — signing the contract in advance25.3One more word a declaration takes — inline25.3.1inline is a request, not an order25.3.2Its address can be taken — and that is when a real one is needed25.3.3C’s inline model — one rule and its consequences25.3.4So in practice only two forms are used25.3.5Two restrictions on an inline definition25.4Scope — the range in which a name is visible25.5Settling the credit — int main(void)
26.1Two stages — read, then interpret26.2The contract of fgets — what it gives, and what it leaves to you26.2.1How do you know it was cut?26.3What stays in the tub — predict, take, leave26.4Interpretation can fail26.5Closing Part V
Part VI — Values and flow
27.1The four things a type settles27.2The first division — object types and function types27.2.1Complete and incomplete types27.3The basic types27.3.1There are three character types27.4Integer, real, arithmetic — the collective names27.5Derived types — made out of what is there27.6Qualifiers — a qualified edition of the same type27.6.1A qualified edition is the same size as the original27.6.2The order is free and they may be combined27.6.3Only _Atomic has two faces27.6.4A qualifier is a property of the route, not of the value27.6.5It may be added silently, never removed27.6.6Where it attaches flips the meaning27.7Types with their width in the name — <stdint.h>27.7.1What separates the three families is “what they demand”27.7.2The three traps of uint8_t27.7.3So which do you use27.8Summary — the standard’s words and this book’s chapters
28.1The family of integer types28.2The basic types at a glance — minimum and actual ranges28.3The type of an integer constant — the same value, typed by its notation28.4Where size is the contract — fixed-width types28.5Seeing the boundary with our own eyes
29.1Division and remainder — the direction of discarding29.2Bit operations — chapter 6′s world, in C’s syntax29.3The contracts these operators make29.4Conversion — crossing between containers
30.1Rule 1 — integer promotion30.2Rule 2 — the usual arithmetic conversions30.3Rule 3 — default promotions for variadic arguments
31.1bool — a type with two values31.2Comparison — expressions that make bools31.3Logical operators — weaving judgements together
33.1The three siblings of the loop33.1.1Where do-while really wins — counting backwards33.2The invariant — how a loop is trusted33.2.1Read it in three pieces33.2.2How people actually use the word33.2.3Binary search — where the invariant earns its keep33.2.4What it buys33.3for’s first slot — what may be declared there33.3.1How far the name is visible, and how long it lives33.4The condition slot is code, not a test33.5Loops that never end33.6Nested loops — double, triple33.7The reunion — Duff’s device
34.1Copying values — the original is safe34.2“Call by value” and “call by reference” — pinning the terms down34.3Side effects and evaluation order — collecting the seed34.4Recursion — a function that calls itself34.5The last operator, and the seed of the contract34.6Operators at a glance — what we have met so far
35.1Assignment is an expression, not a statement35.2Assignment does three things35.3The left side is evaluated too35.4Compound assignment — the left side is evaluated once35.5Where the contract ends35.6The conversion hidden in an assignment
Part VII — Memory
36.1Three notations — declaration, &, *36.2sizeof — asking the size of a container36.2.1The type sizeof yields — size_t36.3What kind of number is an address — name, size, order36.3.1Why an integer, of all things36.3.2The abstraction has thickened with time36.4How to print an address36.4.1The exact contract of %p36.4.2When it has to become a string — uintptr_t and PRIxPTR36.4.3But — does the address need printing at all?36.5The size of a pointer is not one number
37.1nullptr — the name of emptiness37.2How far may 0 be used — the exact definition of a null pointer constant37.2.1The bits it becomes are another matter37.3The trap — code that assumes the representation is 0
38.1Alignment and casts — the privilege of char*38.1.1The two privileges side by side38.2Provenance — the same number, a different origin38.2.1A word that is not in the standard yet38.2.2Where was the gap — two neighbouring variables38.2.3Where the word was born — DR26038.2.4Writing it down — from a questionnaire to a technical specification38.2.5What changes if it lands38.2.6Machines that carry the origin in hardware
39.1Declaration, access, traversal39.2Arrays and pointers — the truth about decay39.3There is no gap between one element and the next39.4Adding to a pointer is not ordinary addition39.4.1Three integer types that hold an address — ptrdiff_t, intptr_t, uintptr_t39.4.2How far it may go — the contract of the arithmetic39.5An array parameter is not an array39.6Arrays whose size is settled at run time — VLAs39.7[static N] in a parameter — “at least this many will arrive”39.7.1[static 1] — writing “not null” into the declaration39.7.2A qualifier inside the same brackets qualifies the pointer39.8The boundary — the life-or-death rule
40.1An array of arrays — the substance first40.2How a subscript unfolds40.3Parameters — why it is not int **40.4One address, different eyes — the contract of flattening40.5The patterns of practice40.5.1Flat plus a leading dimension — the lingua franca of numeric libraries40.5.2An array of row pointers — binding scattered rows into one40.6Traversal order and the cache — why the same computation differs several-fold
41.1Why this chapter has to exist41.1.1Why the misunderstanding is so tough41.1.2And discovery comes late41.2What differs — five axes41.3Steps to reach it — this is the real difference41.4The accident that crosses a file41.5When they do become the same — three rules41.6The places that do not decay41.7Why 6[a] is legal41.8Parameter conversion happens one layer only41.9Three working patterns — seen with addresses41.9.11. An array of pointers — tying together things of different lengths41.9.22. A pointer to a pointer — where someone else’s pointer is changed41.9.33. Changing the order without moving the data41.10So what C cannot do — and the half-told rest41.10.1What C99 changed41.11Choosing between them — judgement in practice
42.1Nested loops — walking a multidimensional array42.2Leaving nested loops42.3The two places goto is right42.3.1Place 1 — shedding several loops at once42.3.2Place 2 — gathering the cleanup of error handling42.4Making a macro one statement — do { } while (0)42.5Other idioms, and their traps42.5.1Walking backwards — i >= 0 is unusable with size_t42.5.2The rest of the idioms
43.1The identity — an array, and an agreement43.2String literals — read-only ground43.2.1The type is not const — C’s odd place43.3A literal is an array too43.4Concatenation — adjacent literals become one43.5Prefixes — a literal of which encoding
44.1Dissecting the accident — the epidemic called the boundary violation44.2How do the standard tools report failure44.3Five disciplines for handling a failed parse44.3.11. Return failure as a value44.3.22. Let the compiler speak when a check is forgotten44.3.33. Say how far it read44.3.44. Do not count truncation as success44.3.55. On failure, touch no output44.4Casting the discipline into a component
45.1The standard’s four axes — storage duration, scope, linkage, storage class45.1.1Storage duration — four45.1.2Scope — four45.1.3Linkage — three45.1.4The seven storage-class specifiers — gathered in one place45.1.5The slot holds one — and its exception45.2Two lifetimes45.3The layout of memory — the regions with our own eyes45.4The stack — the ledger of calls
46.1Borrowing, and giving back46.2How to write a malloc call — an old argument46.2.1Four reasons for form 146.2.2The case for form 2 — CERT MEM02-C46.2.3Form 3 and the “C++ compatibility” reason — is it real?46.3The alignment of the address returned — because it does not know what will go in46.4The price of two cheap-looking lines — why allocation is expensive46.5Borrowing in one dimension, using it as two46.6Ownership — who is responsible for giving it back46.7Closing Part VII
Part VIII — The shape of data
47.1Declaration, initialisation, access47.2The first surprise of sizeof — not the sum of the members47.3Zeroing the whole thing — { 0 } and { }47.3.1What is actually guaranteed47.3.2The fine difference between {0} and {} — padding47.4A struct is a value47.4.1Why assignment works but comparison does not47.5Header and data in one block — the flexible array member47.5.1sizeof and offsetof — which one sizes the allocation47.5.2So can it be assigned? — copying a struct with a flexible array member
48.1Nesting and access — reading dots and arrows mixed48.2The temporary struct — the compound literal48.3Named arguments — passing one struct48.4Padding — the empty space between members48.4.1Why tail padding exists — the answer is “an array of structs”48.4.2Arrays of structs and pointers to them — the working patterns48.5Why a struct must not be stored or sent whole48.5.1So how is it done — field by field48.6How to remove padding, how to force alignment48.6.1alignas — the fifth family, the alignment specifier48.7Members without names, and container_of48.7.1Anonymous structs and unions (C11)48.7.2container_of — from a member back to the whole48.8Passing an array by value — can it be done, and should it?
49.1The union — laying things over one another49.2Representation with our own eyes — endianness and padding49.3The active member and type punning — the same bits through another eye49.3.1Bit patterns that are not values — non-value representations49.3.2The size and alignment of a union, and the common initial sequence49.4Bit fields — cutting up one word49.4.1What the implementation decides about bit fields49.5The practical pattern of mixing structs and unions49.5.1One real specimen — two-byte Johab Hangul49.6Closing Part VIII
Part IX — Deep corners
50.1The four things an expression carries50.2Precedence and associativity50.3Places where people slip50.4Prefix and postfix — the same job, a different value50.4.1The contract the standard sets50.4.2When the value is settled, and when memory changes50.4.3The truth of “prefix is faster”50.4.4Two things change in C++50.5Operator by operator50.6Postfix operators50.7Unary operators50.8Arithmetic operators50.9Shift operators50.10Relational and equality operators50.11Bitwise and logical operators50.12Conditional, assignment, comma50.13Evaluation order and sequence points50.14The grey zones gathered50.14.1Undefined behaviour (UB)50.14.2Unspecified50.14.3Implementation-defined50.15Things that are not operators
51.1The five basic moves51.1.1Building masks, and the cliff51.2A set of flags — the commonest use51.3The idioms — and why they work51.4C23 — the idioms get names51.5Seven rules51.6The accidents that happen here51.7Before optimising by hand51.8In the flesh — where bits are used51.9Recap
52.1Choosing a type, and comparing52.2Special values — infinity and NaN52.2.1Opening the bits directly
53.1Errors are values — C’s way53.2The contract in code — assert and defence53.3const — the cheapest contract
54.1Three grey zones — UB, unspecified, implementation-defined54.2Why it exists54.3The real face of “anything at all”54.4Before the computation even begins — the UB of a file’s shape54.5Other curious pieces of UB54.6UB you create on purpose — unreachable54.7How to avoid it — discipline, tools, and components54.8Closing Part IX
Part X — Structure
55.1The three forms the standard permits55.2The contract of argc and argv55.3The value returned — three notations, one meaning55.4Conventions — who reads that number, and how
57.1Linking is not one step — three names57.2The real purpose of dynamic linking — not size but the ABI57.3What an ABI promises — item by item57.4What breaks an ABI57.5How to change it without breaking it57.6Position-independent code (PIC) — why libraries must be built this way57.7When names collide they get replaced — interpositioning
58.1What a scope is — not “where a name is visible” but “where it means that thing”58.2The four scopes58.3Where a scope begins — before the equals sign58.4Shadowing — the inner covers the outer58.5The preprocessor knows nothing of scope58.6Scope, lifetime, linkage — three that get confused
59.1The truth behind “C has no name spaces”59.2The four name spaces59.3Three axes — scope, linkage, name space59.4A habit born of tags living apart59.4.1typedef struct node node;59.4.2And yet there are conventions that forbid typedef59.5Enumeration constants are ordinary identifiers59.6Shadowing59.7Names you must not use — the reserved yard
60.1When and where collisions go off60.2The first weapon — do not export (static)60.3The second weapon — the prefix convention60.4The third weapon — symbol visibility60.5The fourth weapon — cut down the names themselves60.6How C++ solved this spot60.6.1The syntax60.6.2The anonymous namespace — the equivalent of C’s static60.6.3using namespace — the convenience and its price60.6.4ADL — looking in the yard of the argument60.6.5Name mangling and extern "C"60.7Good habits — a summary
61.1Macros — turning a name into a stream of tokens61.1.1Parentheses — the first thing anyone using macros learns61.2# — turning a token into a string (stringize)61.3## — joining two tokens into one (token paste)61.4Double expansion — why one layer does not unfold61.4.1The names first — four rules61.4.2Following the expansion by hand — a contrast of two lines61.4.3The standard’s own example — glue and xglue61.5The curious examples the standard gives61.5.1① Placemarkers — pasting an empty argument61.5.2② The expansion the standard declares “unspecified”61.5.3③ __VA_OPT__ — when is “empty” judged?61.6The X macro — several sets of code from one list61.7The translation phases — the eight steps the standard pins down
62.1Three things kept apart — declaration, definition, call62.1.1A call expression is an operator62.1.2What C23 tidied away62.2Arguments arrive as if by assignment62.3Order — what the standard declines to fix62.3.1Where there is an order62.4One call, five steps62.5Register or stack — there is a budget62.5.1When the cleaner differs, so does the name62.6Strange cases — ARM and the embedded world62.7Passing structures62.8Functions that return function pointers62.9The accidents that happen here62.10Recap
63.1The four tools63.2Default argument promotions — values crossing ... get fatter63.3The fundamental limit — the function knows nothing63.4_Generic — catching the type at compile time63.5proven’s PROVEN_ARG — the structure in the flesh63.6History — from varargs to stdarg
64.1The name decays into a pointer64.2The type is the contract64.3void * and function pointers are different worlds64.4How to print a function pointer64.4.1Why it cannot be passed to %p64.4.2The portable road — lift the bytes64.4.3The best answer — a name instead of an address64.5Dispatch tables — an array instead of a switch64.6The virtual function table — object orientation built in C
65.1The two readings65.2Reading ① in practice — connecting it in English word order65.2.1This reading has a name — boustrophedon65.2.2Reading by erasing65.3Reading ② — when there is no name65.4typedef — dividing into layers and naming them65.4.1Where to use it and where not to65.5The real thing — declarations that actually shipped65.5.1① The monster the standard itself produced — signal65.5.2② Rougher in the wild — X11′s error handler65.5.3③ And the genuinely pointless ones65.6Leaving it to a tool — cdecl
66.1The landscape — the toolboxes66.2Why it is thin — design and history66.3An anatomy of the output format string66.4The input format string — what differs66.5The places to beware
Part XI — Reading the standard library
68.1Open, write, close — failure can happen three times68.2How to know the end of a file — the misuse of feof68.3Lines longer than the buffer68.4Text mode and binary mode68.5File position and size
69.1The death of gets and its successors69.2The remaining traps of formatted input69.3The traps on the output side69.4The remaining functions that handle files
70.1The truth about strncpy70.2Then what is used70.3Overlapping regions — memcpy and memmove70.4strtok — it destroys the original and hides state70.5The traps of the remaining functions
71.1Conversion — why atoi is abandoned71.2Dynamic allocation — four functions and their contracts71.3Termination — the difference between four ways71.4Sorting and searching — qsort and bsearch71.5Random numbers — the limits of rand71.6The environment and processes
72.1The twelve functions at a glance72.2The first trap — never pass a char straight in72.3The second trap — EOF is mixed in72.4The judgement depends on the locale72.5It means nothing for multibyte characters
73.1Why it exists — conventions differ by country73.2A locale is process-global state73.3The six categories — what governs what73.4The exact contract of setlocale73.5The grammar of a locale name73.5.1Codeset names and normalisation73.5.2Names in other worlds — BCP 47 and Windows73.6The precedence of the environment variables73.7Where locale data lives
74.1One window — localeconv74.1.1The twenty-four members at a glance74.1.2How the grouping string is encoded74.1.3A monetary form is assembled from three values74.2LC_NUMERIC — where data is quietly corrupted74.3LC_TIME — writing dates and times74.4LC_COLLATE — strcmp is not dictionary order74.4.1strxfrm — why such a function exists74.5Locales and threads74.6Prescriptions
75.1What the standard fixes, and what it does not75.2Five kinds of character constant and string75.3MB_CUR_MAX and MB_LEN_MAX75.4Three layers of conversion function75.4.1Why mbstate_t has to exist75.5mbrtowc — taken apart one step at a time75.5.1A whole string at once — mbsrtowcs
76.1A wchar_t split into two camps76.2The limit of UTF-16 and the surrogate pair76.3Windows — the W functions and their exact encoding76.4Linux and glibc — large, and unused76.5What the toolkits chose — GTK and Qt76.6Streams have an orientation
77.1The principle — UTF-8 inside, conversion only at the boundary77.2Validation — what to reject even when the shape is right77.3The three layers of “how many characters”, and the fourth77.4Normalisation — the same letter, different bytes77.5Case and sorting depend on the language77.6Still alive — the legacy two-byte encodings77.7What does the converting
78.1The properties of NaN and infinity78.2Functions often got wrong78.3Rounding modes and floating-point exceptions — <fenv.h>78.4Type-generic — <tgmath.h>
79.1Three representations of time79.2The traps of struct tm79.3Printing to a string — strftime79.4What C23 added to the clock79.5Time zones and summer time — what the standard does not handle
80.1errno — the price of carrying errors in a global80.2assert — the cheapest way to write a contract as code80.3Signals — see the next chapter80.4Non-local jumps — setjmp/longjmp
81.1Where it came from — a Unix inheritance81.2Two functions — the exact shape81.2.1signal’s arguments and return value81.2.2raise’s argument and return value81.2.3The six signals the standard defines81.3What a handler may do81.3.1What sig_atomic_t really is81.4Handlers and memory — why malloc is not on the list81.5What a signal saves and restores — the register context and errno81.5.1errno is the exception — you must look after it yourself81.6The working pattern — raise a flag and return at once81.7POSIX’s sigaction — filling the standard’s gaps81.7.1Inside struct sigaction81.7.2siginfo_t — who sent it, and why81.7.3Blocking a signal for a while — the mask81.8Real uses81.8.1Graceful shutdown — the most widely used pattern81.8.2SIGPIPE — the signal whose right answer is to ignore it81.8.3Interrupted system calls — EINTR81.8.4Which software uses signals, and why
82.1Why it exists — the makeshift of a language without exceptions82.2The two words — the exact shape82.2.1setjmp — the macro that seems to return twice82.2.2Where setjmp may appear — four contexts only82.2.3longjmp — three things to check before jumping82.3What jmp_buf really is82.3.1Looking inside the eight slots82.3.2And that is the volatile rule82.4What happens to values on return — the volatile rule82.5Nobody cleans up the resources82.5.1The quieter accident — a pointer reverts to its old value82.6What is actually built on this82.6.1How do they cope with memory — the answer is always the same82.7POSIX’s sigsetjmp/siglongjmp82.8Its place today — when to use it, what to use instead
83.1What C99 added83.2What C11 added83.3What C23 added83.4Annex K — the failed attempt at “safe functions”83.5What the library tour leaves behind
84.1Why threads entered the standard84.2The first strand — making one and waiting84.3And then the value comes out wrong84.4Mutual exclusion — mtx_*84.5Waiting — cnd_*84.6Once only — call_once84.7One per strand — thread_local and tss_*84.8★ So why does practice still use pthread?84.9Where to use it and where not to
85.1The lost update — confirmed with the eyes85.2Why volatile is not the answer85.3Atomic types and operations85.3.1CAS — “if it is still what I saw, change it”85.4Memory order — not touching it is the default85.4.1Why “order” is a question at all85.4.2The pair builds a wall — release and acquire85.4.3Is ordering free? — measure it85.5The phrase “lock-free”85.6Where to use it and where not to85.7When the holder stops — priority inversion and spinlocks85.7.1The same illness, another face — spinlocks in user space
86.1The trap of checking afterwards86.2C23′s answer — ckd_add, ckd_sub, ckd_mul86.3Where it is used — size calculation comes first86.4Where this tool is absent
87.1What was promoted87.2bool — what does it prevent87.3nullptr — not a renaming87.4The remaining promotions and the new words87.5Attributes — writing down intent the type cannot carry87.5.1What an attribute does — measured87.5.2Unknown attributes are ignored87.6How to move existing code over
88.1Regions in the executable file, regions in the address space88.2Linux’s layout88.3Windows’ layout88.4The world with no operating system — freestanding implementations88.4.1Arm Cortex-M — the first word of the vector table is the stack88.4.2AVR — Harvard architecture, so even constants are copied88.4.3PIC — banks and the “compiled stack”
89.1Ledger ① — the header attached to each block89.2Ledger ② — lists by size and caches per strand89.3Fragmentation — memory grows though there is no leak89.4Swapping out the standard malloc — alternative allocators89.5Freeing with the size — C23′s free_sized89.6Alternative standard libraries89.7Alternatives that change the shape — arenas, pools, and the allocator as an argument
Part XII — proven — a new, stable foundation
90.1One — string functions do not know the size of the vessel90.2Two — there is no device that makes you confirm failure90.3Three — printf believes exactly what you tell it90.4Four — who frees this90.5Five — a callback nobody can type-check for you90.6And a sixth — bytes have types
91.1Why this library exists91.2What the name says — provenance91.3What it means to replace91.4The promises that shaped the design91.5What it is not91.6The choice of having nothing to install91.7How this book’s examples are built91.8The first program91.9Three rules — the key to this whole part91.10The life of one object91.11Attaching it to your own project — a minimal Makefile
92.1Two shapes of return92.1.1Every error code92.1.2The kinds of result bundle92.2Throw it away and the compiler protests92.3What remains after a failure — failure atomicity92.4Raising a failure upward — together with the cleanup92.5When there is nobody to return to — the panic
93.1Bytes have a name93.2Views — pointer and length as one93.3Slicing — the operation used most in this part93.4Size arithmetic that does not overflow93.5Alignment — pushing up to the next boundary
94.1An allocator is a value94.2Swapping the three sources94.3The arena — things of the same lifetime, all at once94.4Which to choose94.5Owning and borrowing, and state that points at itself
95.1Two types, one rule95.2Refuse, rather than truncate95.3Three kinds of writing — refuse, truncate, grow95.4The operations that mend a string95.5Finding and cutting — text handling without copying95.6The boundary of two worlds — NUL termination and UTF-16
96.1{} — the placeholder with no type96.1.1The whole grammar after the colon96.1.2Printing a type the library has never heard of96.2Which formatting function to use96.3The opposite direction — the scanner
97.1The life cycle of the four containers97.2The growing array97.3The intrusive list — linking without allocation97.4The ring buffer — a stream of fixed size97.5The hash map, and data structures under attack97.6Sorting with a worst-case guarantee97.7Bytes into letters — hashes and encodings
98.1The life of one file98.1.1The convenience functions that read and write in one go98.2Streams — reading and writing with a buffer98.3Time — two different clocks98.4Random numbers — the purpose settles the thing98.5Memory mapping
99.1Stackless coroutines — overlapping without threads99.2The job system — using several cores99.3Threads, allocators, and provenance99.4When there is no OS — freestanding99.4.1The actual build procedure99.5Where this library stands — what it is, and what it is not99.5.1What has been verified so far99.5.2Stability — what may still change99.5.3What is not there yet99.6When it is better not to use it
100.1What we are building100.2The plain C edition100.3The proven edition100.4One step further — nesting, without recursion100.4.1What was used where100.5So what actually changed
Part XIII — Closing
101.1make — what is to be remade101.2git — what changed and when101.3The rest of the toolbox — building, observing, checking101.3.1① What to make and how — building and dependencies101.3.2② What has gone wrong — debuggers and record-replay101.3.3③ Where is it slow — profilers101.3.4④ Is the code right — checking and testing101.3.5⑤ Is it good for people to read — formatting and documentation101.4What runs in C even today — and why101.5The industry’s rulebooks — MISRA (Motor Industry Software Reliability Association) C and its neighbours101.5.1MISRA C — out of the car industry and into everything101.5.2The neighbours — safety standards and coding rules101.5.3What gets forbidden — a common pattern101.6C and C++ — siblings, not parent and child101.7Put C code into a C++ compiler101.8Time flows faster on the C++ side101.9How to mix them — extern "C"101.10Embedded — C’s home ground101.11How to cross the limits — the knack of practice
102.1What changes when there is a build102.2How files are divided — the header is the contract, the source the circumstances102.3The boundary between public and internal102.4Folder layout — what the conventions are for102.5Kinds of test — what each one catches102.6Golden tests — freezing the answer in a file102.7How much, and what not to test102.8Working together — what the machine refuses and what people look at102.9What has to be written down
103.1Without names, the same thing gets called different things103.2The fixture — the ground a test stands on103.3The witness — a check is a defect’s gravestone103.4The negative check — does that check really bite103.5The ratchet — a number that must not grow103.6Tiers — you cannot always run everything103.7One piece of work — separating decision from execution103.8No silent failures — and the commit is the record of a decision
105.1Retrospect — the C23 already used105.2The practices of modern C — one sheet of guidance105.3Books to read next, and the standard document105.4Finally
Front and back matter
The skeletons of a format specificationOutput conversions — the whole listFlags, width, precisionLength modifiers — the place that tells the type’s widthFormats for fixed-width integersInput conversions — the whole listRules that hold in input aloneThe return values organisedA collection of common mistakes
Integer promotionThe usual arithmetic conversions (the common type of two operands)The default promotions of variadic argumentsInteger conversion rank (the criterion of the conversion rules)Which conversion calls down which dangerA list of the places quiet conversions happenOther conversions often met
Jens Gustedt, Modern C — a textbook that takes the standard as its criterionK. N. King, C Programming: A Modern Approach — the orthodox textbookChristopher Preschern, Fluent C — design and patternsOnline referencesRunning code straight in a browserTools that show youPlaces that hand you problemsWhat can be read for freeAn eye for material — spotting what is out of dateHow to obtain the standard documentThe roads after this book
The notation for writing grammar — from BNF to EBNFHow to read the C standard’s notationA.1 Lexical grammarA.1.1 Lexical elementsA.1.2 KeywordsA.1.3 IdentifiersA.1.4 Universal character namesA.1.5 ConstantsA.1.6 String literalsA.1.7 PunctuatorsA.1.8 Header namesA.1.9 Preprocessing numbersA.2 Phrase structure grammarA.2.1 ExpressionsA.2.2 DeclarationsA.2.3 StatementsA.2.4 External definitionsA.3 Preprocessing directivesWhat the grammar cannot answerHow to find the grammar in the standard document
<string.h> — strings and memoryWorking on blocks of memory (mem*)Copying and joining (strcpy and strcat families)Comparing, and lengthSearching and splittingError strings<ctype.h> — the kinds of a single byte<stdckdint.h> — arithmetic that answers about overflow<assert.h> — checking a contract while it runs<errno.h> — where a failure leaves its reason<setjmp.h> — writing down a place to come back to<stdarg.h> — walking arguments whose number is unknown<stddef.h> — names for the things that had none<stdbool.h> and <stdalign.h> — the two headers that emptied out<iso646.h> and <stdnoreturn.h> — one scar and one retired name<limits.h> — where the integers end<float.h> — where the reals end<stdint.h> — integers with the width nailed down<inttypes.h> — printing and reading those integers<stdbit.h> — counting bits, the standard way<locale.h> — changing whose conventions apply<wctype.h> — the kinds of a wide character<uchar.h> — characters with the Unicode width nailed down<signal.h> — interruption from outside<fenv.h> — reaching into the floating-point environment<tgmath.h> — it picks the one that fits the type<complex.h> — complex numbers<threads.h> — the standard’s own threads<stdatomic.h> — operations that cannot be split<wchar.h> — everything about wide characters<time.h> — instants and durations<stdlib.h> — the drawer of odds and ends<stdio.h> — stream input and output<math.h> — the mathematical functions
Why — what standard C does not give youWhat standard C alone can still finishWhat the gap really is — not a matter of skill in CFour roads — what else must be learnedOne window on the screen — roads to a GUIWhat to buildWhere to learnGoing deeper into CSeeing computer science widelyGuides and lists
What a dump isWhat is in it, and what is notThe shape of the file — a Linux core is an ELFTurning an address into a nameHow the stack is unwoundThe order to read inMaking sure a dump is left — and the common reasons none isWhat to take from this
The four questions a loader asks a fileA format with no header — the DOS .COMMZ — the first header DOS addeda.out — the first Unix format, and its limitCOFF, and its descendant PEELF — two listsOther formats, and magic numbersWhat to take from this
A word before this appendixWhen the power comes on — a machine running without an OSA list of what the OS was doing for us“An uninitialised global is zero” is not freeThe reset vector — the first place the machine readsWhat to take from thisInterrupts — the device that seizes the flowThree words, told apart firstYou have met this before — how a hardware exception reaches a C functionInterrupts are not countedCan a handler be written in C — the answer differs by chipWhy so little may be done inside a handlerWhat to take from thisTouching registers — memory-mapped I/O and volatileDevices have addressesA name for it — memory-mapped I/OThe other side — distinguishing I/O by instruction (port-mapped I/O)Which is why volatile existsInterrupts are not the only thing that changes my memory — DMAWhat volatile does not doBriefly disabling interrupts — the oldest lockDo not draw a hardware register as a bit-fieldWhat to take from thisC under constraint, and bringing it backOne timer interrupt, all the way throughHow to live without an allocatorWriting the width into the nameHandling fractions as integers — fixed pointHow deep does the stack goTime and power become visible in the codeBringing it back — reading it again on an OS
When the power comes onThe BIOS era — 512 bytes was all there wasThe layout of the disk — from MBR to GPTUEFI — the firmware reads a filesystemWhat a bootloader actually doesWhy several stages — the shadow of 446 bytesThe UEFI path — found by name and by variableWithout a bootloader — the Linux EFI stubEmbedded — U-Boot and MCUbootWhat is handed over at the handoverWhen it will not boot — telling links apart by symptomBy operating system — what happens after thatMS-DOSWindowsLinuxRTOSes and small machinesThe chain is a chain of trustWhat to take from this
Words to know firstFour axes that separate connectionsThe oldest wire — UART and RS-232Frames — cutting characters out of an idle wireRS-232 — the same frame, different voltagesFlow control — how to say “wait”The four errors a UART reportsWhy parallel disappearedMany devices on two wires — I2CFast on four wires — SPIOn one wire, and with anyone speaking — 1-Wire and CANThe motorway inside the machine — PCI and PCI ExpressConfiguration space — the 256 bytes in which a device introduces itselfThe motorway going out — USBInterruption — IRQsMoving it instead — DMAThe three compared — polling, interrupts and DMAWhat to take from this
Words to know firstA disk is a row of numbered cellsWhy divide at allMBR — everything written into the first 512 bytesThe 16-byte partition entryWhen four partitions are not enough — extended partitions and the EBR chainGPT — rebuilt out of an array, a check and a copyThe protective MBR — sector 0 is still an MBRThe 92-byte GPT headerThe 128-byte GPT partition entryA GUID is written one way and stored anotherAlignment — why exactly sector 2048What is inside a partition — a filesystem’s first sectorFAT32 — the easiest example to readHow ext4 and NTFS differThe physics of the medium — why random writes are slowThe spinning platter — the time to reach a placeThe semiconductor — writing and erasing in different unitsWhat was measured on this machinefsync — the cost no cache can hideWhat breaks whereWhat to take from this
Why measure — with concepts alone, what remains is wrong intuitionMeasure the measuring instrument first1. The example reads the machine’s numbers itself2 and 3. Resolution and cost are different things4. The first trap — the optimiser removes what you meant to measure5. The second trap — the first round is slow6. The third trap — one interruption drags the meanThe six disciplines of this appendixMeasuring the memory ladderFirst the principle — why a ladder appearedSo how must it be measuredWhat was measured — the steps appearLines and strides — the unit a cache fetchesFirst the principle — why a line and not a single itemThe method — vary the stride, fix the countWhat was measured — the line appears in the timeThe same data, laid out differently — AoS and SoATranslation has a cost too — the TLB and pagesFirst the principle — the translation table and its cacheThe method — little data, many pagesThree things measuredThe cost of a fork in the road — branch predictionFirst the principle — why it must guessBefore measuring — is there still a branchWhat was measuredHow regular must it be to be predictedThe cost between cores — false sharing and atomicsFirst the principle — a line belongs to one core at a timeFalse sharing — I slow down because of somebody else’s variableWhat was measuredReally shared values — atomics and locksThe cost of numbers — floating pointFirst, this section’s measurement failed onceMeasured 1 — denormals were 71 times slowerMeasured 2 — is float faster than double?Measured 3 — the operations cost differentlyEverything measured, in one placeMeasured again on real ARM hardwareHow to use these numbersWhat to take from this
Three kinds of machineThe shape of translation — what an MMU actually doesThree consequencesOne — an allocation is a promiseTwo — the bill for that promise comes laterThree — the copy that copies nothingUsing the layout, not just obeying itMachines that do not translateWhen there is more than one address spaceCaches, DMA, and scratchpadsPointers are growing flesh