Appendix Q — List of tables and figures
Every table and figure in the book, in order. The numbers, titles and pages are not written by hand but taken from the text itself, so this list changes whenever the text does. In the PDF, the number is a link to the table or figure.
Tables#
- Table 0.1 — The beginner’s pace
- Table 0.2 — Where the returning reader should look first
- Table 2.1 — The representation efficiency of whole-number bases
- Table 2.2 — For small values, base 2 sometimes wins
- Table 2.3 — Voltage levels crowd together as the radix grows
- Table 2.4 — Which radix is ‘most efficient’ depends on what you count
- Table 4.1 — Two layers for reading null — the notation in source and the representation in memory
- Table 5.1 — The four regions of memory and what each is like
- Table 5.2 — The axes that separate static, stack and heap
- Table 5.3 — This chapter’s words and the standard’s words
- Table 5.4 — Machines where this sketch does not hold
- Table 5.5 — Where the four regions come back
- Table 7.1 — How the bits of
floatanddoubleare divided - Table 7.2 — The special values reserved at the two ends of the exponent range
- Table 7.3 — The same numbers as 64-bit representations
- Table 8.1 — The numbers ASCII and EBCDIC gave the same characters
- Table 8.2 — Where EBCDIC’s alphabet breaks, and what sits in the gaps
- Table 8.3 — Classifying a character by hand versus asking the library
- Table 8.4 — UTF-8′s bit layout by byte count
- Table 8.5 — The names the standard gives the character sets
- Table 8.6 — The translation phases where character sets change
- Table 8.7 — The encoding names C23 tidied up
- Table 8.8 — String literal prefixes and what each guarantees
- Table 10.1 — What the operating system gives a process
- Table 10.2 — The states a process can be in
- Table 10.3 — The machine’s two modes — user mode and kernel mode
- Table 10.4 — Program and process — what to remember
- Table 11.1 — Candidate answers to “when do we cut off?”
- Table 11.2 — Ways of taking turns
- Table 11.3 — What has to be carried across a switch
- Table 11.4 — What the timer interrupt does
- Table 11.5 — Two ways to send one byte
- Table 11.6 — Two ways to notice that something happened outside
- Table 11.7 — The order of events in an interrupt
- Table 11.8 — What happens when several things interrupt
- Table 11.9 — The windows a device keeps open
- Table 11.10 — Five ways code copes when interrupts overlap
- Table 11.11 — Interrupts and multitasking
- Table 11.12 — What interrupts did in the DOS years
- Table 11.13 — Where the ideas of this chapter lead
- Table 12.1 — The memory ladder — size and latency by tier
- Table 13.1 — What this book calls a grey area, and what it does not
- Table 13.2 — Grey-area practices and where each is treated
- Table 13.3 — Questions that strengthen the case for avoiding a grey area
- Table 13.4 — The ladder for handling a grey area
- Table 13.5 — What the C standard leaves open, and which other standard promises it instead
- Table 13.6 — What to write down when a grey area is used
- Table 16.1 — Terminal and shell — the words sorted out
- Table 16.2 — Terminals (windows) and shells (workers) on each platform
- Table 16.3 — How to tell which shell you are in
- Table 16.4 — The shell commands used on day one
- Table 16.5 — Running the executable you made — what to type in each situation
- Table 16.6 — Kinds of command-line argument
- Table 16.7 — The first build command, per compiler
- Table 16.8 — The three compilers compared — where, option lineage, C23 support, running
- Table 17.1 — The five kinds of token the C standard defines, and the preprocessing stage’s header name
- Table 18.1 — Tools that need no installation
- Table 18.2 — The compiler options that settle character sets
- Table 18.3 — The bytes of “가” under each option — measured
- Table 18.4 — Common gdb commands
- Table 19.1 — The abbreviations in this chapter
- Table 19.2 — Vendor performance compilers and their status
- Table 19.3 — What gets said about Pelles C
- Table 19.4 — Win32′s strengths and what they cost
- Table 19.5 — What the Pelles C toolchain contains
- Table 19.6 — The stories behind the compiler names
- Table 19.7 — Official documentation, per compiler
- Table 19.8 — This chapter’s compilers in one line each
- Table 21.1 — The bases an integer constant can be written in
- Table 21.2 — The suffixes of an integer constant
- Table 21.3 — Where the C23 digit separator works and where it does not
- Table 21.4 — Character constant prefixes and their types
- Table 21.5 — The kinds of escape sequence
- Table 21.6 — The suffixes of a floating constant
- Table 21.7 — String literal prefixes and their encodings
- Table 21.8 — What the things that look like constants really are
- Table 21.9 — Where a
const intis refused - Table 21.10 — Four kinds of ‘constant’ and what each lacks
- Table 21.11 — Where
constexprmay be used - Table 21.12 — What the two compilers do when the value does not fit exactly
- Table 23.1 — The slots of a conversion specification and their names
- Table 23.2 — The conversions used on day one
- Table 23.3 — Four output functions and where each fits
- Table 24.1 — What one variable has
- Table 25.1 — Link results by how an
inlinefunction is set up —-O0and-O2 - Table 25.2 — The two forms of an
inlinefunction — where the definition and theexterndeclaration go - Table 25.3 — What is forbidden in a non-
staticinline definition, and why - Table 26.1 — What
fgetspromises - Table 26.2 — Reading the result from how the box ends
- Table 27.1 — What a type settles
- Table 27.2 — Object types and function types
- Table 27.3 — What an incomplete type cannot do
- Table 27.4 — The basic types, as the standard has them
- Table 27.5 — The three character types
- Table 27.6 — The type-specifier items of §6.7.3.1 — spellings in one item are one type
- Table 27.7 — The collective names the standard gives types
- Table 27.8 — Derived types — what comes from what
- Table 27.9 — What each type qualifier promises
- Table 27.10 — Type qualifiers and storage-class specifiers, side by side
- Table 27.11 — What
constprotects, depending on where it sits - Table 27.12 — The vocabulary of types and where to find it
- Table 27.13 — The
<stdint.h>width types — three families, plus pointer-holding and widest - Table 27.14 — Choosing the integer type that fits the place
- Table 27.15 — The standard’s words and the chapter that faces each
- Table 28.1 — The minimum ranges the integer types guarantee, and their limit macros
- Table 28.2 — The floating-point limit macros
- Table 28.3 — How the type of a suffixless constant is settled
- Table 28.4 — Measured types when the same value is written differently
- Table 28.5 — Integer types by width and purpose
- Table 29.1 — What each arithmetic operator demands of its operands
- Table 29.2 — Shift, case by case
- Table 33.1 — The three loop forms — where the condition is seen, and the minimum number of turns
- Table 33.2 — The three pieces of checking an invariant
- Table 33.3 — Four ways invariants are used in practice
- Table 33.4 — What an invariant catches
- Table 33.5 — Lifetime of a loop variable, by where it is declared
- Table 33.6 — What may go in
for’s first slot — by standard and compiler - Table 33.7 — When the condition slot touches the loop variable
- Table 33.8 — What compilers do with a loop that may not end
- Table 33.9 — The mistakes that appear once loops are nested
- Table 34.1 — The groups of operators at a glance
- Table 35.1 — Ordering among the three evaluations of an assignment — the relation the standard specifies
- Table 35.2 — How many times the left side is evaluated in a compound assignment
- Table 35.3 — Conversions hidden in an assignment, case by case
- Table 35.4 — Assignment and side effects — what to remember
- Table 36.1 — What an object, an address and a pointer each are — this section in brief
- Table 36.2 — What is an object and what is not
- Table 36.3 — What comes with every object
- Table 36.4 — Three uses of the word “pointer”
- Table 36.5 — What
size_tis - Table 36.6 — The same number means different things in different places
- Table 36.7 — The contract of
%p— the standard’s two lines - Table 36.8 — Passing an
int *straight to%p— by warning level - Table 36.9 — What
%pandPRIxPTRare each for - Table 36.10 — The specifiers the Linux kernel’s
printkuses to print a pointer - Table 37.1 — Which spellings count as a null pointer constant
- Table 37.2 — The layers that handle null, and what each promises
- Table 37.3 — Where C and C++ differ over null
- Table 38.1 — The privileges of
void *and the character pointers - Table 38.2 — Idioms that separate once the provenance model lands
- Table 39.1 — What an array gives
- Table 39.2 — The words of the definition and what they mean
- Table 39.3 — Forms of array initialisation and their rules
- Table 39.4 — What does not work on an array as a whole
- Table 39.5 — What follows from elements being flush against each other
- Table 39.6 — The four shapes of pointer arithmetic
- Table 39.7 — Which arithmetic a pointer may take part in, and which not
- Table 39.8 — What GCC says to the forbidden shapes
- Table 39.9 — Three integer types that go with pointers
- Table 39.10 — How far pointer arithmetic is allowed
- Table 39.11 — Five kinds of array accidents
- Table 39.12 — Reading an uninitialised local array — who notices
- Table 39.13 — Layers that stop array accidents
- Table 40.1 — How a multidimensional subscript unfolds
- Table 40.2 — The four patterns for handling multidimensional data
- Table 40.3 — Multidimensional arrays — what to remember
- Table 41.1 — The five axes that separate an array from a pointer
- Table 41.2 — The number of steps an indexed access takes
- Table 41.3 — What happens when the declarations disagree
- Table 41.4 — The three rules by which an array becomes a pointer
- Table 41.5 — The places decay does not happen
- Table 41.6 — In a parameter only the outer layer changes
- Table 41.7 — The same three words, two layouts
- Table 41.8 — One more level for each thing you mean to change
- Table 41.9 — The ways around passing a 2-D array of varying width
- Table 41.10 — Accidents caused by decay and their cures
- Table 41.11 — Choosing between array and pointer for the job
- Table 41.12 — Arrays and pointers — what to remember
- Table 42.1 — Same sum, different order
- Table 42.2 — Ways to leave several layers at once, by language
- Table 42.3 — Three ways out of nested loops
- Table 42.4 — The discipline for using
goto - Table 42.5 — Three shapes for a macro body
- Table 42.6 — Loop idioms and their traps
- Table 43.1 — What the definition of a string says
- Table 43.2 — The kinds of string literal and their sizes
- Table 44.1 — How each input function reports success, and where it trips
- Table 45.1 — The standard’s four axes — storage duration, scope, linkage, storage-class specifier
- Table 45.2 — The four storage durations and their lifetimes
- Table 45.3 — The kinds of scope
- Table 45.4 — The three kinds of linkage
- Table 45.5 — The seven storage-class specifiers
- Table 45.6 — The three places two storage classes may be written together (C23)
- Table 45.7 — What lives in each region, and for how long
- Table 46.1 — What sizes and lifetimes written in the source cannot handle
- Table 46.2 — What
callocpromises and what it does not - Table 46.3 — Three ways to spell a
malloccall - Table 46.4 — What the compiler says when a function is called without a declaration
- Table 46.5 — Allocation mistakes and their diagnostics
- Table 46.6 — What a C++ compiler says to the same code
- Table 47.1 — What a structure gives
- Table 47.2 — Array, structure and union, split by the words of the definition
- Table 47.3 — What default initialization puts in, by member type
- Table 47.4 — Sizing a struct with a flexible array member — whether
sizeoforoffsetofapplies depends on what is being asked - Table 47.5 — Three ways to move a struct with a flexible array member
- Table 48.1 — The two demands that create tail padding
- Table 48.2 — Three ways to pass a struct, and what each costs
- Table 48.3 — What goes wrong when a struct is written out whole
- Table 48.4 —
alignas— its two forms and their meaning - Table 48.5 — Where
alignasis forbidden - Table 48.6 — Where wrapping an array in a struct earns its keep
- Table 48.7 — Using structs — what to remember
- Table 49.1 — Ways to type-pun, and how C rules on each
- Table 49.2 — Non-value representations — where they come from, what touching them does
- Table 49.3 — The size and alignment rules of a union
- Table 49.4 — What the implementation decides about bit fields
- Table 49.5 — The bit layout of Johab Hangul
- Table 50.1 — What the standard does and does not promise about a struct in memory
- Table 50.2 — Who receives the bytes, and how they may differ from us
- Table 50.3 — Ways of avoiding serialisation, and what they cost
- Table 50.4 — Three ways to change byte order
- Table 50.5 — Principles for designing a binary format
- Table 50.6 — Kinds of accident in deserialisation
- Table 50.7 — Structures that guard deserialisation
- Table 50.8 — Serialisation — one line each
- Table 51.1 — Three ways to lay out the same data
- Table 51.2 — Choosing a layout by what the code mostly does
- Table 51.3 — Kinds of cache-friendly data structure
- Table 51.4 — Data layout — one line each
- Table 52.1 — Expressions in mathematics and in C
- Table 52.2 — The four things an expression does
- Table 52.3 — Four kinds of side effect
- Table 52.4 — What an expression carries
- Table 52.5 — Associativity, by operator group
- Table 52.6 — Where precedence groups it differently than intended
- Table 52.7 — Prefix and postfix increment, compared
- Table 52.8 — The standard’s sentences that order value computation and store in increment
- Table 52.9 — Code generated for prefix and postfix increment, by optimisation level — x86-64, gcc 14 and clang 19
- Table 52.10 — How C and C++ rule on the same code
- Table 52.11 — The contract of the postfix operators
- Table 52.12 — The contract of the unary operators
- Table 52.13 — The contract of the multiplicative and additive operators
- Table 52.14 — The contract of the shift operators
- Table 52.15 — The contract of the relational and equality operators
- Table 52.16 — The contract of the bitwise and logical operators
- Table 52.17 — The contract of the remaining operators
- Table 52.18 — The three relations that can hold between two evaluations
- Table 52.19 — The complete list of sequence points (C23 Annex C)
- Table 52.20 — Verdicts on commonly seen expressions
- Table 52.21 — The order the two compilers actually chose (x86-64,
-O2) - Table 52.22 — Where an operator’s behaviour is undefined
- Table 52.23 — The places left unspecified
- Table 52.24 — The places the implementation decides
- Table 52.25 — Things that look like operators but are not
- Table 52.26 — Expressions and operators — what to remember
- Table 53.1 — The letters used in the five-moves table
- Table 53.2 — The five moves on a single bit
- Table 53.3 — Three masks
- Table 53.4 — The idioms one meets most often
- Table 53.5 — What
<stdbit.h>named (there areuc/us/ui/ul/ullvariants too) - Table 53.6 — Rules for working with bits
- Table 53.7 — Accidents around bit work
- Table 53.8 — What the compiler already knows — checked on this machine
- Table 53.9 — Where bit work actually appears
- Table 54.1 — The floor of
floatanddouble(the tick in the subnormal range equals the smallest subnormal) - Table 54.2 — Three ways subnormals become an attack surface
- Table 56.1 — Implementation-defined, unspecified and undefined behaviour: the standard’s attitude
- Table 56.2 — What the standard requires of a source file’s end, and what breaking it means
- Table 56.3 — Curious UB code and where the standard puts it
- Table 56.4 — Two ways to write “control must not come here”
- Table 57.1 — The four promises an object carries, and the errors that break them
- Table 57.2 — The kinds of buffer overflow
- Table 57.3 — The layers that stop buffer overflows — from top to bottom
- Table 57.4 — Memory-side rules and their place in C23
- Table 57.5 — A field guide to memory errors — broken promise, catching tool, preventing design
- Table 58.1 — Four answers to “who returns memory, and when”
- Table 58.2 — What automatic memory management takes
- Table 59.1 — The forms of
mainand their standing - Table 59.2 — The ways to end a program
- Table 59.3 — Exit status values and their meanings
- Table 59.4 — The three faces of
main— what to remember - Table 61.1 — The three stages of linking
- Table 61.2 — Output size and content, by build
- Table 61.3 — What an ABI settles
- Table 61.4 — Two ways to return a struct (x86-64 System V)
- Table 61.5 — The same 64-bit machine, a different promise — type sizes in bytes, by data model
- Table 61.6 — Changes that are source compatible but not binary compatible
- Table 61.7 — The tables linking uses, and the shape of indirection
- Table 62.1 — The four scopes the standard defines
- Table 62.2 — Working discipline for shadowing
- Table 62.3 — Two layers make names, by two different rules
- Table 62.4 — Three questions about a name and its object
- Table 63.1 — The four name spaces and what lives in each
- Table 63.2 — The three axes that settle a name
- Table 63.3 — Naming policies and their grounds
- Table 63.4 — Options that catch shadowing, measured
- Table 63.5 — The range of reserved names
- Table 64.1 — When a name collision shows up
- Table 64.2 — Prefix conventions in real projects
- Table 64.3 — Handling symbol visibility, per platform
- Table 64.4 — Exporting one struct of function pointers — what it gains and costs
- Table 64.5 — Symbol kinds by declaration, as
nmshows them - Table 64.6 — Conventions for
using namespace, and their grounds - Table 64.7 — The name in source and the name the linker sees
- Table 64.8 — Habits worth forming about names
- Table 65.1 — The rules for using a macro
- Table 65.2 — The four macro-replacement rules — the standard’s names
- Table 65.3 — Expanding
STR_RAW(WIDTH)— one layer, step by step - Table 65.4 — Expanding
STR(WIDTH)— two layers, step by step - Table 65.5 — Expanding
glue(HIGH, LOW), step by step - Table 65.6 — Expanding
xglue(HIGH, LOW), step by step - Table 65.7 — The eight translation phases at a glance
- Table 66.1 — One name in three places
- Table 66.2 — The operands of a call expression
- Table 66.3 — The rules around prototypes, before and after
- Table 66.4 — Adjustments applied to arguments and results
- Table 66.5 — One source, two orders — measured on this machine
- Table 66.6 — Sequencing rules around a call
- Table 66.7 — The five steps of one call
- Table 66.8 — Responsibility for preserving registers
- Table 66.9 — Four desktop calling conventions — as emitted by this machine’s compilers
- Table 66.10 — Three conventions on 32-bit Windows —
int f(int,int)built with this machine’s mingw - Table 66.11 — Where the arguments of
mix(1, 2, 3)ride on six machines - Table 66.12 — Passing and returning one structure — as the compilers emitted it
- Table 66.13 — Three ways of declaring
pick, a function returning a function pointer - Table 66.14 — Accidents around a call
- Table 67.1 — How values are promoted through a variadic call
- Table 67.2 — How a variadic function learns the count and types, and how each way breaks
- Table 68.1 — Return-type designs for a symbol-lookup function, and their relation to the standard
- Table 68.2 — Three spellings of a generic function pointer type — round trip, a forgotten cast, warnings
- Table 68.3 — Ways to print a function pointer, and their portability
- Table 68.4 — What
dladdrfound for each kind of function - Table 68.5 — Function pointers — what to remember
- Table 69.1 — Van der Linden’s precedence rule
- Table 69.2 — Reading a declaration with
constin it - Table 69.3 — Reading
char *const *(*next)();by erasing, step by step - Table 69.4 — Reading the declaration of
signalthrough, step by step - Table 69.5 — Reading declarations — what to remember
- Table 70.1 — Four words used to explain object orientation
- Table 70.2 — What an object-oriented language does, and what C does by hand
- Table 70.3 — The three parts that make up an object
- Table 70.4 — The six chapters of Part XI
- Table 70.5 — Imitating object orientation — one line each
- Table 71.1 — Overloading and overriding are different things
- Table 71.2 — Ways of handling a struct that changes between versions
- Table 71.3 — Lifetime conventions in an object system
- Table 71.4 — Building the table — one line each
- Table 72.1 — Where dynamic tables are needed
- Table 72.2 — Two places for the table pointer
- Table 72.3 — Dynamic virtual tables — one line each
- Table 73.1 — The eleven rows of the measurement output
- Table 73.2 — Five functions calling
shape_area, and whether they were devirtualised — both compilers agreed - Table 73.3 — Whether to use a dynamic table — a judgement table
- Table 73.4 — The cost of an indirect call — one line each
- Table 74.1 — The structure of GObject
- Table 74.2 — A multi-method — choosing a function by the kinds of two arguments
- Table 74.3 — The ideas of this part, and where they first appeared
- Table 74.4 — Objects built in C — one line each
- Table 75.1 — Macros that help an object system
- Table 75.2 — The five SOLID principles and the shape they take in C
- Table 75.3 — The paths modern languages have taken
- Table 75.4 — What to use — a decision table
- Table 75.5 — Limits, and when not to — one line each
- Table 76.1 — The families of the standard library and their headers
- Table 76.2 — Output conversions, by data type
- Table 76.3 — Input conversions, by data type
- Table 77.1 — The headers every freestanding implementation must provide
- Table 77.2 — Headers added by edition
- Table 77.3 — The standard library at a glance — what to remember
- Table 78.1 — Success and failure in
<stdio.h>functions - Table 78.2 — What
fflushdoes to an input stream - Table 78.3 — The rules for handling a stream
- Table 79.1 — The reading functions and how they rate
- Table 79.2 — The remaining file functions and what to watch
- Table 79.3 — Choosing the I/O function that fits the job
- Table 80.1 — The string-copy functions — status and truncation
- Table 80.2 — The remaining string and memory functions and what to watch
- Table 80.3 — Choosing the string function that fits the job
- Table 81.1 — The contracts and traps of the
<stdlib.h>allocation functions - Table 81.2 — What cleanup each way of ending performs
- Table 81.3 — The
<stdlib.h>function that fits the job - Table 82.1 — When each
<ctype.h>function answers true - Table 82.2 — The rules for using character classification
- Table 83.1 — What a locale changes — Korea and Germany
- Table 83.2 — Locale categories and the functions each governs
- Table 83.3 — What
setlocale’s second argument means - Table 83.4 — Locale names — their parts and what settles them
- Table 83.5 — The standards behind locale notation
- Table 83.6 — The precedence of the locale environment variables
- Table 83.7 — Locale data on glibc — files, command and paths
- Table 83.8 — Locales 1 — what to remember
- Table 84.1 — The members of
struct lconv - Table 84.2 — The grouping array’s values and meanings
- Table 84.3 — What the sign-position values mean
- Table 84.4 —
1.25printed with$and+(part of the standard’s table) - Table 84.5 —
strftimeconversions — families and properties - Table 84.6 — Per-thread locales — the means outside the standard
- Table 84.7 — The locale tool that fits the job
- Table 84.8 — Locales 2 — what to remember
- Table 85.1 — What the standard settles about
wchar_t, and what it leaves - Table 85.2 — The macros around wide characters
- Table 85.3 — The five notations for character constants and string literals — types and encodings
- Table 85.4 —
MB_CUR_MAXandMB_LEN_MAX— header and meaning - Table 85.5 — The layers of multibyte conversion functions
- Table 85.6 — What
mbrtowc’s return values mean - Table 85.7 — Wide characters 1 — what to remember
- Table 86.1 — The de facto encoding, by platform
- Table 86.2 — The ranges of the surrogate area
- Table 86.3 — Windows API suffixes and their string types
- Table 86.4 — Why Linux code uses UTF-8 rather than
wchar_t - Table 86.5 — String types, by toolkit
- Table 86.6 — Stream orientation — states and meanings
- Table 86.7 — Wide characters 2 — what to remember
- Table 87.1 — What to use, place by place
- Table 87.2 — The properties of UTF-8 and what they buy
- Table 87.3 — Byte sequences that must be rejected
- Table 87.4 — The layers of counting text and their units
- Table 87.5 — The four Unicode normalisation forms
- Table 87.6 — Case mappings that depend on the language
- Table 87.7 — Legacy encodings, by region
- Table 87.8 — Trail-byte ranges of the legacy encodings
- Table 87.9 — The means for handling Unicode
- Table 87.10 — Unicode in practice — what to remember
- Table 88.1 — The traps of
<math.h> - Table 88.2 — Choosing the maths function for the situation
- Table 89.1 — The types and functions of
<time.h> - Table 89.2 — The
strftimespecifiers - Table 89.3 — What C23 added to
<time.h> - Table 89.4 — The time function that fits the job
- Table 90.1 — Functions that turn an error number into a message
- Table 90.2 — The diagnostic tools and their rules
- Table 91.1 — The declarations of
<signal.h> - Table 91.2 —
signal’s arguments and return values - Table 91.3 — The standard signals and their default actions
- Table 91.4 — What is allowed inside a handler
- Table 91.5 — What each half of
volatile sig_atomic_tprevents - Table 91.6 — When a handler calls
malloc - Table 91.7 — What a signal and
longjmpsave and restore - Table 91.8 — The gaps in standard
signal, andsigaction - Table 91.9 — The members of
struct sigaction - Table 91.10 — What the
sigactionflags mean - Table 91.11 — The most used members of
siginfo_t - Table 91.12 — What each signal is used for
- Table 91.13 — Why it had to be a signal
- Table 91.14 — Signals — what to remember
- Table 92.1 — The declarations of
<setjmp.h> - Table 92.2 — What
setjmpreturns - Table 92.3 — The contexts where
setjmpis allowed - Table 92.4 —
longjmp’s conditions and what breaking them means - Table 92.5 — What the jump buffer stores
- Table 92.6 — The parts of a jump buffer and their sizes
- Table 92.7 — Variable values after a
longjmp - Table 92.8 — Which variables need
volatile - Table 92.9 — The three places
volatileis used, and what it prevents - Table 92.10 — What is built on
setjmp/longjmp - Table 92.11 — Who frees the memory taken before the jump
- Table 92.12 — The recommended approach, by intent
- Table 92.13 — Non-local jumps — what to remember
- Table 93.1 — What C99 added, and where it stands today
- Table 93.2 — What C11 added, and where it stands today
- Table 93.3 — What each edition added, and whether it is used
- Table 94.1 — The functions of
<threads.h> - Table 94.2 — What
shared += 1really does - Table 94.3 — Kinds of mutex and where each fits
- Table 94.4 — The mutex functions
- Table 94.5 — The condition-variable functions
- Table 94.6 — Two ways to keep storage per strand
- Table 94.7 — The thread-specific storage functions
- Table 94.8 — Why C11 threads are used less
- Table 94.9 — C11, POSIX and Win32 thread APIs compared
- Table 94.10 — Thread recommendations, by situation
- Table 95.1 — Three guarantees compared:
volatile,_Atomicand a mutex - Table 95.2 — The atomic operations and where each fits
- Table 95.3 — CAS is realised differently from machine to machine
- Table 95.4 — The same code pays a different price per machine — raising the flag
- Table 95.5 — The memory orders and what each guarantees
- Table 95.6 — The concurrency tool recommended, by situation
- Table 95.7 — Atomics — what to remember
- Table 96.1 — Checking overflow — the old idiom and today’s
- Table 96.2 — Asking about overflow — what to remember
- Table 97.1 — The names that went from macro to keyword, and the new keywords
- Table 97.2 —
NULLandnullptrcompared - Table 97.3 — The seven standard attributes
- Table 97.4 — From macro to keyword — what to remember
- Table 98.1 — Regions of the executable and the address space, and their permissions
- Table 98.2 — Address-space layout in Linux and Windows
- Table 98.3 — Memory layout on embedded machines
- Table 98.4 — A program’s memory layout — what to remember
- Table 99.1 — The layers of glibc’s allocator
- Table 99.2 — Ways to ease fragmentation
- Table 99.3 — Alternative allocators and their character
- Table 99.4 — Alternative standard library implementations
- Table 99.5 — Inside the allocator — what to remember
- Table 100.1 — The five bugs and one more — what C gives and what is needed
- Table 101.1 — What proven does not do
- Table 101.2 — proven’s name shapes and what they mean
- Table 101.3 — Getting started with proven — the build, three rules, first output
- Table 102.1 — The error codes and what they mean
- Table 102.2 — The result bundle types
- Table 102.3 — Error handling, case by case
- Table 103.1 — Ownership and writability of the three memory types
- Table 103.2 — What the four slicing functions return — reading/writing × checked/unchecked
- Table 103.3 — The names of the alignment operations and constants
- Table 103.4 — This chapter’s vocabulary and its contracts
- Table 104.1 — The life cycle of an arena — four steps
- Table 104.2 — Where each arena allocation function fits
- Table 104.3 — Heap, arena and pool compared
- Table 104.4 — Choosing the allocator for the situation
- Table 105.1 — Allocation and destruction in the string functions
- Table 105.2 — The three kinds of string operation and their failure atomicity
- Table 105.3 — The contracts of the string-editing functions
- Table 105.4 — Failure and ownership in the text functions
- Table 106.1 — What may go in a formatting slot
- Table 106.2 — The formatting functions and their allocators
- Table 106.3 — What the parsing functions read and return
- Table 106.4 — The formatting and scanner API
- Table 107.1 — Containers — making, allocation, traversal, giving back
- Table 107.2 — The list traversal macros
- Table 107.3 — Keys for a hash map, and what to watch
- Table 107.4 — The container and algorithm tools and where each fits
- Table 108.1 — The steps of handling a file
- Table 108.2 — What the file flags mean
- Table 108.3 — The whole-file read and write functions and what to watch
- Table 108.4 — The APIs of the outside world and what to watch
- Table 109.1 — proven — what this book has verified and what it has not
- Table 109.2 — The answers to the five bugs and the other problems, and their chapters
- Table 110.1 — The tiny JSON — what is in and what is out
- Table 110.2 — What the code says and what it leaves unsaid
- Table 110.3 — The same place in plain C and in proven
- Table 110.4 — What each tool did, and what would happen without it
- Table 110.5 — Writing it three times — what to remember
- Table 111.1 — The tools of practice
- Table 111.2 — What each profiler measures
- Table 111.3 — Notable C projects, and why C
- Table 111.4 — The road MISRA C has travelled
- Table 111.5 — What the MISRA C rule categories mean
- Table 111.6 — Safety standards and coding rules, and what each governs
- Table 111.7 — What the rulebooks often forbid, and where this book treats it
- Table 111.8 — What each of C and C++ has that the other lacks
- Table 112.1 — The jobs a build takes on
- Table 112.2 — What belongs in a header and what does not
- Table 112.3 — Common folder kinds and their reasons
- Table 112.4 — Kinds of test and what each catches
- Table 112.5 — What to hand the machine and what people look at
- Table 112.6 — Which document answers which question
- Table 112.7 — Build and test — what to remember
- Table 113.1 — The words of this chapter
- Table 113.2 — What was a fixture in the golden demonstration
- Table 113.3 — What must not be made a fixture
- Table 113.4 — What a negative check reveals
- Table 113.5 — With a ratchet and without
- Table 113.6 — An example of dividing checks by depth (tiers)
- Table 113.7 — The steps one piece of work passes
- Table 113.8 — The faces of silent failure
- Table 113.9 — How the work turns — what to remember
- Table 114.1 — Embedded compilers and the chips they target
- Table 114.2 — Embedded tools, by job
- Table 115.1 — What C23 brought, and where we met it
- Table 115.1 — Operator groups in precedence order (strongest first), with their associativity
- Table 115.2 — Expressions that precedence groups differently than intended
- Table 115.3 — Where to look for what
- Table 115.1 — The skeleton of output and input conversion specifications
- Table 115.2 — The full list of output conversions
- Table 115.3 — The places of flags, width and precision
- Table 115.4 — The length modifiers
- Table 115.5 — The full list of input conversions
- Table 115.6 — The return values of the formatted and line I/O functions
- Table 115.7 — Common formatting mistakes and their fixes
- Table 115.1 — The risk of each conversion
- Table 115.1 — What the columns of this reference mean
- Table 115.2 —
<string.h>reference — memory blocks (mem*) - Table 115.3 —
<string.h>reference — copying and joining - Table 115.4 —
<string.h>reference — comparing and length - Table 115.5 —
<string.h>reference — searching and splitting - Table 115.6 —
<string.h>reference — error strings - Table 115.7 —
<string.h>macros and types - Table 115.8 — What holds for every
<ctype.h>function - Table 115.9 —
<ctype.h>reference - Table 115.10 —
<stdckdint.h>reference - Table 115.11 — The common contract of the
ckd_macros - Table 115.12 —
<assert.h>reference - Table 115.13 —
<errno.h>entry by entry - Table 115.14 —
<setjmp.h>entry by entry - Table 115.15 —
<stdarg.h>entry by entry - Table 115.16 —
<stddef.h>entry by entry - Table 115.17 — How they came to be empty
- Table 115.18 — The eleven names
<iso646.h>gives - Table 115.19 — How to read the
<limits.h>names - Table 115.20 —
<limits.h>entry by entry - Table 115.21 — The three that belong to no type
- Table 115.22 — What each suffix means — all three prefixes take them
- Table 115.23 — The names that belong to no type
- Table 115.24 — Three families — what each promises
- Table 115.25 —
<stdint.h>entry by entry - Table 115.26 — Limits belonging to types from other headers
- Table 115.27 —
<inttypes.h>entry by entry - Table 115.28 — How to read the format macros
- Table 115.29 —
<stdbit.h>entry by entry — written with the type-generic names - Table 115.30 —
<locale.h>entry by entry - Table 115.31 —
<wctype.h>entry by entry - Table 115.32 —
<uchar.h>entry by entry - Table 115.33 —
<signal.h>entry by entry - Table 115.34 — Exception flags and rounding directions
- Table 115.35 —
<fenv.h>entry by entry - Table 115.36 — What it picks
- Table 115.37 — The six that take a complex value apart or work on it
- Table 115.38 — Those that pair with
<math.h> - Table 115.39 — Making values and choosing the arithmetic
- Table 115.40 — Threads themselves —
thrd_ - Table 115.41 — Synchronisation —
mtx_,cnd_,call_once - Table 115.42 — Thread-specific storage, and the types
- Table 115.43 — The operations of
<stdatomic.h> - Table 115.44 — The type names —
atomic_in front - Table 115.45 — Strings and memory — the
<string.h>counterparts - Table 115.46 — Conversion — between bytes and wide, and to numbers
- Table 115.47 — Input and output — the
<stdio.h>counterparts - Table 115.48 — Types, macros and Annex K
- Table 115.49 —
<time.h>reference - Table 115.50 — Types and macros
- Table 115.51 —
<stdlib.h>reference — memory allocation - Table 115.52 —
<stdlib.h>reference — ending the program - Table 115.53 —
<stdlib.h>reference — between strings and numbers - Table 115.54 —
<stdlib.h>reference — arithmetic, sorting, randomness, multibyte - Table 115.55 — Annex K’s constraint handlers, and names from other headers
- Table 115.56 —
<stdio.h>reference — opening, closing, buffering - Table 115.57 —
<stdio.h>reference — reading and writing - Table 115.58 —
<stdio.h>reference — position and state - Table 115.59 — Macros, types and Annex K
- Table 115.60 — How to read the
<math.h>names - Table 115.61 — Asking what a value is — these are macros
- Table 115.62 — The elementary functions
- Table 115.63 — Rounding, remainders, moving the exponent
- Table 115.64 — Keeping precision — computed wide, rounded once
- Table 115.65 — Decimal floating point only
- Table 115.66 — The remaining macros and types of
<math.h> - Table 115.1 — EBNF notation and its meaning
- Table 115.2 — The C standard’s conventions for writing its grammar
- Table 115.3 — What the grammar settles, and what lies outside
- Table 115.4 — The grammar — what to remember
- Table 115.1 — What C23 changed in the language
- Table 115.2 — What C23 added to the preprocessor
- Table 115.3 — What C23 changed in the library
- Table 115.1 — What a dump holds and what it does not
- Table 115.2 — Program headers of a core file
- Table 115.3 — Three layers that turn an address into a name
- Table 115.4 — Common reasons no dump is left
- Table 115.1 — What the operating system did, and who does it without one
- Table 115.2 — Interrupt, exception, trap — the axes that separate them
- Table 115.3 — When an interrupt arrives, who saves what
- Table 115.4 — Touching the same address — as memory, and as a device
- Table 115.5 — Two designs for reaching a device
- Table 115.6 — Classic port addresses met verbatim in old code
- Table 115.7 — What
volatilegives and what it does not - Table 115.8 — Three reasons embedded work decides against
malloc - Table 115.9 — Earlier chapters, read again after the visit
- Table 115.1 — The words used in this appendix
- Table 115.2 — Four axes that separate connections
- Table 115.3 — The connections covered in this appendix
- Table 115.4 — The ten bits of an 8N1 frame
- Table 115.5 — How to read a notation like
8N1 - Table 115.6 — TTL-level UART and RS-232
- Table 115.7 — The signals of RS-232 (on a 9-pin connector)
- Table 115.8 — Two kinds of flow control
- Table 115.9 — Errors met in a UART’s status register
- Table 115.10 — The signals of a parallel (Centronics) port
- Table 115.11 — Why parallel loses as things get faster
- Table 115.12 — The signals and markers of I2C
- Table 115.13 — I2C speed modes
- Table 115.14 — The four wires of SPI
- Table 115.15 — SPI’s four modes
- Table 115.16 — I2C against SPI
- Table 115.17 — Two unusual connection schemes
- Table 115.18 — PCI and PCI Express
- Table 115.19 — PCIe generations and effective speed (per lane)
- Table 115.20 — The main fields of PCI configuration space (header type 0)
- Table 115.21 — The low bits of a BAR say what kind it is
- Table 115.22 — USB speed grades
- Table 115.23 — USB’s four transfer types
- Table 115.24 — Edge triggering and level triggering
- Table 115.25 — How interrupt delivery changed
- Table 115.26 — How DMA uses the bus
- Table 115.27 — Three things that catch you with DMA
- Table 115.28 — Polling, interrupts and DMA at a glance
- Table 115.1 — The words used in this appendix
- Table 115.2 — Three kinds of sector size
- Table 115.3 — The whole layout of the MBR’s 512 bytes
- Table 115.4 — The 16 bytes of an MBR partition entry
- Table 115.5 — Bit layout of the three CHS bytes
- Table 115.6 — Partition type bytes commonly seen
- Table 115.7 — The two EBR entries — their bases differ
- Table 115.8 — MBR and GPT
- Table 115.9 — Every field of the GPT header (LBA 1, 92 bytes)
- Table 115.10 — Every field of a GPT partition entry (128 bytes)
- Table 115.11 — GPT attribute bits worth knowing
- Table 115.12 — The five groups of a GUID and their storage order
- Table 115.13 — Type GUIDs commonly seen
- Table 115.14 — What 1 MiB alignment lines up with
- Table 115.15 — Where a filesystem’s self-description lives
- Table 115.16 — The main fields of the FAT32 boot sector (BPB)
- Table 115.17 — FAT32 entry values (only the low 28 bits are used)
- Table 115.18 — The main fields of the ext4 superblock (start of partition + 1024 bytes)
- Table 115.19 — The main fields of the NTFS boot sector
- Table 115.20 — Filesystems commonly met
- Table 115.21 — The time for one read on a hard disk
- Table 115.22 — Three units inside an SSD
- Table 115.23 — What “it cannot be overwritten” produces
- Table 115.24 — The lower rungs of the memory ladder (measured on this machine)
- Table 115.25 — Design guidance that comes from this layer
- Table 115.26 — Symptom → which layer was damaged
- Table 115.1 — Four things an executable format must answer
- Table 115.2 — Three things to know about PE
- Table 115.3 — Formats told apart by their first few bytes
- Table 115.1 — What the firmware does
- Table 115.2 — How the MBR’s 512 bytes divide up
- Table 115.3 — The BIOS way and the UEFI way
- Table 115.4 — Bootloaders commonly met
- Table 115.5 — GRUB’s stages (the old BIOS way)
- Table 115.6 — Where
core.imglives - Table 115.7 — The order in which UEFI firmware looks for the next thing
- Table 115.8 — How Linux starts with secure boot enabled
- Table 115.9 — The stages of the U-Boot family
- Table 115.10 — How MCUboot makes an update safe
- Table 115.11 — What passes across at each change of stage
- Table 115.12 — Symptom → which link of the chain
- Table 115.13 — The Windows boot chain
- Table 115.1 — The clocks met in C and POSIX
- Table 115.2 — The numbers of the machine this was written on, and of an ARM phone (as the example read them)
- Table 115.3 — What to measure how — the method depends on the size
- Table 115.4 — Which representative value to use
- Table 115.5 — Six disciplines for measuring
- Table 115.6 — What each rung does
- Table 115.7 — What pointer chasing achieves
- Table 115.8 — Design guidance from this measurement
- Table 115.9 — Four consequences of moving in lines
- Table 115.10 — The same computation, laid out differently
- Table 115.11 — How to choose a layout
- Table 115.12 — The three cases of address translation
- Table 115.13 — Cost per access against page count (measured on this machine and an ARM phone)
- Table 115.14 — Where the first-touch cost actually bites
- Table 115.15 — The same source, different code — time per element (measured here)
- Table 115.16 — Cost by pattern (in the version that kept a real branch)
- Table 115.17 — Guidance from this measurement
- Table 115.18 — How cores treat a cache line (roughly)
- Table 115.19 — Two threads each incrementing their own slot (measured here and on an ARM phone)
- Table 115.20 — How to avoid false sharing
- Table 115.21 — The cost of sharing (measured here and on an ARM phone)
- Table 115.22 — Changing only the magnitude of the values (measured here)
- Table 115.23 —
floatanddouble— with and without vector instructions - Table 115.24 — Cost by operation (data inside L1, measured here)
- Table 115.25 — Techniques that follow from this measurement
- Table 115.26 — The measurements of this appendix (this machine and an ARM phone, medians)
- Table 115.27 — The latency curve of an ARM phone (random access, median of five runs, each cluster against its own 4 KiB)
- Table 115.28 — Symptom → where to suspect first
- Table 115.1 — Three ways to govern memory
- Table 115.2 — Page-table levels and page sizes — the two RISC-V schemes
- Table 115.3 — What changes on a machine without translation
- Table 115.4 — Machines with more than one address space
- Table 115.5 — When DMA and the cache disagree
- Table 115.1 — What standard C does not have
- Table 115.2 — What can be finished with standard C alone
- Table 115.3 — What the gap seems to be, and what it is
- Table 115.4 — The roads that lead on from C
- Table 115.5 — Roads to opening a window in C
- Table 115.6 — Things worth building to the end
- Table 115.7 — Places to see more C
- Table 115.8 — Courses and curricula
- Table 115.9 — Places to survey what exists
- Table 115.1 — Online references and where to get the standard documents
- Table 115.2 — Sites that run code straight in a browser
- Table 115.3 — Visualisation tools, and the chapters they connect to
- Table 115.4 — The problem sites’ character and what they give a learner
- Table 115.5 — Reference material free to read
- Table 115.6 — The marks of outdated code
- Table 115.7 — The standard editions and their free drafts
- Table 115.1 — The order in which sources are written
Figures#
- Figure 3.1 — A cell of memory — eight bits each, numbered up by one
- Figure 3.2 — Big-endian — the most significant byte goes first
- Figure 3.3 — Little-endian — the least significant byte goes first
- Figure 4.1 — Four aligned bytes — starting at a multiple of four
- Figure 5.1 — The memory layout of one program. Nearly every later chapter builds on this picture.
- Figure 6.1 — Outside, the value read as an
unsigned char; inside, the same bits read as asigned char. - Figure 6.2 — The same eight bits: what fills the front depends on the signedness of the original type.
- Figure 7.1 — The bit layout of
floatanddouble. The width of each field is proportional to its bit count. - Figure 12.1 — Virtual pages are paired with physical ones through the page table. Pages contiguous in virtual addresses are scattered in physical memory — that one fact is what this figure carries.
- Figure 18.1 — Layout of a screen showing how one source line becomes several lines of machine code.
- Figure 18.2 — Values as boxes, pointers as arrows — the picture a visualiser draws.
- Figure 27.1 — How the standard divides types. A dashed box is a name that gathers several branches.
- Figure 30.1 — The usual arithmetic conversions between integers: follow the four steps in order. Most accidents happen at step 3.
- Figure 36.1 — The object
n— four lockers from address 200, holding 55 (hex 37) in little-endian order - Figure 36.2 — The object
p— eight lockers from address 208. The value held is 200 (hex C8):n’s address - Figure 36.3 — The three things that travel with one pointer value. The same address does not make the same pointer.
- Figure 39.1 — The array
aand its decayed&a[0]have the same address but different types — so how many elements there are disappears. - Figure 39.2 — The distance
+1moves a pointer is the size of the type it points at. - Figure 40.1 — The two jumps that reach
a[2][1]— one row-sized, one element-sized. - Figure 41.1 — An array of pointers — the array itself is contiguous, what it points at is scattered.
- Figure 41.2 — To change the caller’s pointer you must pass the place where that pointer lives.
- Figure 47.1 — The hatched cells are padding. Each member sits at a multiple of its alignment, and padding is added at the end of the struct too.
- Figure 47.2 — Header and data in one block — two allocations become one.
- Figure 47.3 — Same leading members; the last member’s type moves where the array begins.
- Figure 48.1 — An array of structs — since no gap is allowed between elements, the size is rounded up to a multiple of the alignment.
- Figure 48.2 — Above, the struct written whole: twelve bytes, padding included; below, the fields written one by one: seven bytes in the order we chose.
- Figure 51.1 — When only positions are updated, only 37.5% of an AoS cache line is used; every byte of an SoA
xline is. - Figure 54.1 — With normal numbers alone there is a hole before zero. Subnormals fill it with ticks at the same spacing.
- Figure 54.2 — The bits on both sides of the normal/subnormal border. The exponent field drops from 1 to 0, but the actual exponent stays.
- Figure 70.1 — State per object, one table per kind, one copy of the behaviour.
- Figure 72.1 — The five steps of building a table. After sealing, it is only read.
- Figure 72.2 — The table pointer inside the object (top) and beside the pointer (bottom).
- Figure 72.3 — The way one message finds its function. Most end in the class’s cache.
- Figure 73.1 — When the same kind follows the same kind, the guess is wrong only where the kind changes. Mixed, it is wrong almost every time.
- Figure 74.1 — Xt’s class records. A derived record starts with its parent record’s exact shape.
- Figure 95.1 — If the flag is visible, is the data written before it visible too?
- Figure 115.1 — How a hardware exception reaches my C function, and why it cannot go back.
- Figure 115.2 — How much the hardware saves differs by chip — and that difference settles the compiler’s work.
- Figure 115.3 — A design with one address space and a design with two.
- Figure 115.1 — Sending the same byte serially and in parallel.
- Figure 115.2 — The path where the CPU carries the data, and the path where DMA does.
- Figure 115.1 — Three layers on one disk — the row of sectors, the partition table, the filesystem.
- Figure 115.2 — The EBR chain — the two entries of one EBR count from different bases.
- Figure 115.3 — An SSD’s pages and blocks — where write amplification comes from.
- Figure 115.1 — The formats differ; the loader’s questions do not.
- Figure 115.1 — The chain of booting — the machines differ; the work of each link does not.
- Figure 115.1 — Three traps in measuring, and the guard against each.
- Figure 115.2 — The latency curve, actually measured. The vertical dashed lines are this machine’s cache boundaries.
- Figure 115.3 — Cost per access against stride. The vertical dashed line is this machine’s cache line size.
- Figure 115.4 — What happens in the pipeline when the prediction is right and when it is wrong.
- Figure 115.5 — Two threads’ variables sharing a line, and kept apart.