14.12 goto can only jump to a label inside the same function.
14.13 goto should not jump over variable initializations.
D Premature optimization is the root of all evil.
15.1 Do not trade off safety for performance.
15.2 Optimizers are clever enough to eliminate unused initializations.
15.3 The different notations of pointer arguments to functions result in the same binary code.
15.4 Not taking addresses of local variables helps the optimizer because it inhibits aliasing.
15.5 Inlining can open up a lot of optimization opportunities.
15.6 Adding a compatible declaration without the inline keyword ensures the emission of the function symbol in the current TU.
15.7 An inline function definition is visible in all TUs.
15.8 An inline definition goes in a header file.
15.9 An additional declaration without inline goes in exactly one TU.
15.10 Only expose functions as inline if you consider them to be stable.
15.11 All identifiers that are local to an inline function should be protected by a convenient naming convention.
15.12 inline functions can’t access identifiers of static functions.
15.13 inline functions can’t define or access identifiers of modifiable static objects.
15.14 A restrict-qualified pointer has to provide exclusive access.
15.15 A restrict-qualification constrains the caller of a function.
E Don’t speculate about the performance of code; verify it rigorously.
15.16 Complexity assessment of algorithms requires proofs.
15.17 Performance assessment of code requires measurement.
15.18 All measurements introduce bias.
15.19 Instrumentation changes compile-time and runtime properties.
15.20 The relative standard deviation of run times must be in a low percentage range.
15.21 Collecting higher-order moments of measurements to compute variance and skew is simple and cheap.
15.22 Runtime measurements must be hardened with statistics.
16.1 Whenever possible, prefer an inline function to a functional macro.
16.2 A functional macro shall provide a simple interface to a complex task.
16.3 Macro replacement is done in an early translation phase, before any other interpretation is given to the tokens that compose the program.
16.4 (macro retention) If a functional macro is not followed by (), it is not expanded.
16.5 The line number in __LINE__ may not fit into an int.
16.6 Using __LINE__ is inherently dangerous.
16.7 Stringification with the operator # does not expand macros in its argument.
16.8 When passed to a variadic parameter, all arithmetic types are converted as for arithmetic operations, with the exception of float arguments, which are converted to double.
16.9 A variadic function has to receive valid information about the type of each argument in the variadic list.
16.10 Using variadic functions is not portable unless each argument is forced to a specific type.
16.11 Avoid variadic functions for new interfaces.
16.12 The va_arg mechanism doesn’t give access to the length of the va_list.
16.13 A variadic function needs a specific convention for the length of the list.
16.14 The result type of a _Generic expression is the type of the chosen expression.
16.15 Using _Generic with inline functions adds optimization opportunities.
16.16 The type expressions in a _Generic expression should only be unqualified types: no array types, and no function types.
16.17 The type expressions in a _Generic expression must refer to mutually incompatible types.
16.18 The type expressions in a _Generic expression cannot be a pointer to a VLA.
16.19 All choices expression1 ... expressionN in a _Generic must be valid.
17.1 Side effects in functions can lead to indeterminate results.
17.2 The specific operation of any operator is sequenced after the evaluation of all its operands.
17.3 The effect of updating an object with any of the assignment, increment, or decrement operators is sequenced after the evaluation of its operands.
17.4 A function call is sequenced with respect to all evaluations of the caller.
17.5 Initialization-list expressions for array or structure types are indeterminately sequenced.
17.6 Each iteration defines a new instance of a local object.
17.7 goto should only be used for exceptional changes in control flow.
17.8 Each function call defines a new instance of a local object.
17.9 longjmp never returns to the caller.
17.10 When reached through normal control flow, a call to setjmp marks the call location as a jump target and returns 0.
17.11 Leaving the scope of a call to setjmp invalidates the jump target.
17.12 A call to longjmp transfers control directly to the position that was set by setjmp as if that had returned the condition argument.
17.13 A 0 as a condition parameter to longjmp is replaced by 1.
17.14 setjmp may be used only in simple comparisons inside controlling expression of conditionals.
17.15 Optimization interacts badly with calls to setjmp.
17.16 Objects that are modified across longjmp must be volatile.
17.17 volatile objects are reloaded from memory each time they are accessed.
17.18 volatile objects are stored to memory each time they are modified.
17.19 The typedef for jmp_buf hides an array type.
17.20 C’s signal-handling interface is minimal and should only be used for elementary situations.
17.21 Signal handlers can kick in at any point of execution.
17.22 After return from a signal handler, execution resumes exactly where it was interrupted.
17.23 A C statement may correspond to several processor instructions.
17.24 Signal handlers need types with uninterruptible operations.
17.25 Objects of type sig_atomic_t should not be used as counters.
17.26 Unless specified otherwise, C library functions are not asynchronous signal safe.
18.1 If a thread T0 writes a non-atomic object that is simultaneously read or written by another thread T1, the behavior of the execution becomes undefined.
18.2 In view of execution in different threads, standard operations on atomic objects are indivisible and linearizable.
18.3 Use the specifier syntax _Atomic(T) for atomic declarations.
18.4 There are no atomic array types.
18.5 Atomic objects are the privileged tool to force the absence of race conditions.
18.6 A properly initialized FILE* can be used race-free by several threads.
18.7 Concurrent write operations should print entire lines at once.
18.8 Destruction and deallocation of shared dynamic objects needs a lot of care.
18.9 Pass thread-specific data through function arguments.
18.10 Keep thread-specific state in local variables.
18.11 A thread_local variable has one separate instance for each thread.
18.12 Use thread_local if initialization can be determined at compile time.
18.13 Mutex operations provide linearizability.
18.14 Every mutex must be initialized with mtx_init.
18.15 A thread that holds a nonrecursive mutex must not call any of the mutex lock functions for it.
18.16 A recursive mutex is only released after the holding thread issues as many calls to mtx_unlock as it has acquired locks.
18.17 A locked mutex must be released before the termination of the thread.
18.18 A thread must only call mtx_unlock on a mutex that it holds.
18.19 Each successful mutex lock corresponds to exactly one call to mtx_unlock.
18.20 A mutex must be destroyed at the end of its lifetime.
18.21 On return from a cnd_t wait, the expression must be checked again.
18.22 A condition variable can only be used simultaneously with one mutex.
18.23 A cnd_t must be initialized dynamically.
18.24 A cnd_t must be destroyed at the end of its lifetime.
18.25 Returning from main or calling exit terminates all threads.
18.26 While blocking on mtx_t or cnd_t, a thread frees processing resources.
19.1 Every evaluation has an effect.
19.2 If F is sequenced before E, then F → E.
19.3 The set of modifications of an atomic object X are performed in an order that is consistent with the sequenced-before relation of any thread that deals with X.