C reference
Compiler invocation, language-mode macros, the keyword set of every revision, operator precedence, the standard headers, conversion specifiers, the undefined behaviours that actually bite, and the hardening flags that catch them. The language is described in the C article; the C ecosystem map covers Linux, embedded systems, assembly, robotics, drones, assurance, and FFmpeg; this page is the lookup companion to both.
Toolchain and command line¶
GCC and Clang share their option surface almost exactly; MSVC does not. The rows below are the options that change the meaning of a build rather than its speed.
| Option | What it does | Example | Note |
|---|---|---|---|
cc / gcc / clang | Compiler driver: preprocess, compile, assemble, link | cc -std=c23 -O2 -o app main.c util.c | The driver, not the compiler; it calls the others |
-c | Compile to an object file, do not link | cc -c -std=c17 util.c | Produces util.o |
-E | Preprocess only | cc -E main.c | less | The first tool to reach for on a macro bug |
-S | Compile to assembly | cc -S -O2 -masm=intel hot.c | Reading the output settles most performance arguments |
-I, -L, -l | Header search, library search, library to link | cc main.c -Iinclude -L/opt/lib -lz | -l comes after the objects that need it |
-D, -U | Define or undefine a macro on the command line | cc -DNDEBUG -D_FORTIFY_SOURCE=3 main.c | NDEBUG disables assert |
-g | Emit debug information | cc -g -Og main.c | -Og is the optimisation level meant for debugging |
-O0 … -O3, -Os, -Ofast | Optimisation level | cc -O2 main.c | -Ofast relaxes IEEE conformance; treat it as a different language |
-flto | Link-time optimisation across translation units | cc -O2 -flto *.c | The only way to inline across the unit boundary |
-Wall -Wextra | The warning sets worth enabling everywhere | cc -Wall -Wextra -Wpedantic -Werror main.c | Neither includes all warnings, despite the name |
-ffreestanding | Do not assume a hosted library or that main is special | cc -ffreestanding -nostdlib boot.c | Kernel and firmware builds |
-fno-strict-aliasing | Disable type-based alias analysis | cc -O2 -fno-strict-aliasing net.c | How the Linux kernel and much legacy code stay correct |
cl.exe | The MSVC driver | cl /std:c17 /W4 /O2 main.c | /std:clatest for the C23 subset |
Driver options in common use. GCC and Clang accept all of the Unix-form options listed; the MSVC equivalents differ in both spelling and default. Source: GCC and Clang command-line documentation and the MSVC compiler options reference.
# A build that catches what a build can catch, before the sanitizers run.
cc -std=c23 -Wall -Wextra -Wpedantic -Werror \
-O2 -g -D_FORTIFY_SOURCE=3 -fstack-protector-strong \
-fno-common -Wconversion -Wshadow \
-o app main.c util.c
Language modes and feature tests¶
| Revision | Compiler option | __STDC_VERSION__ | Note |
|---|---|---|---|
| C89 / C90 | -std=c89, -std=iso9899:1990 | not defined | -std=gnu89 for the GNU dialect |
| C95 | -std=iso9899:199409 | 199409L | Rarely selected explicitly |
| C99 | -std=c99 | 199901L | -std=gnu99 adds GNU extensions |
| C11 | -std=c11 | 201112L | MSVC: /std:c11 |
| C17 / C18 | -std=c17 | 201710L | The default in Clang and in GCC before 15; MSVC /std:c17 |
| C23 | -std=c23 (-std=c2x in older releases) | 202311L | The default in GCC 15 and later as gnu23; MSVC subset under /std:clatest |
| C2y | -std=c2y where supported | implementation-defined until published | Working draft; contents may change |
Selecting a language mode, and the macro value that identifies it. The gnu* variants of each option enable GNU extensions on top of the same standard. Source: GCC and Clang option documentation; ISO/IEC 9899:2024 §6.10.10.
| Macro | Meaning | Note |
|---|---|---|
__STDC__ | 1 in a conforming implementation | Present since C89 |
__STDC_VERSION__ | The revision, as in the table above | Absent in C89; the standard mode test |
__STDC_HOSTED__ | 1 hosted, 0 freestanding | The portable way to ask whether the full library exists |
__STDC_NO_ATOMICS__ | Defined if <stdatomic.h> is absent | Conditional feature |
__STDC_NO_THREADS__ | Defined if <threads.h> is absent | Defined by several mainstream implementations |
__STDC_NO_VLA__ | Defined if variable-length arrays are absent | VLAs became optional in C11 |
__STDC_NO_COMPLEX__ | Defined if complex arithmetic is absent | Optional since C11 |
__STDC_IEC_60559_BFP__ | IEEE-754 binary floating point conformance | Replaces C99’s __STDC_IEC_559__ |
__has_include(...) | Whether a header can be included | C23; available as an extension in GCC and Clang long before |
__has_c_attribute(...) | Whether a standard attribute is supported | C23 |
Predefined macros used to compile the same source against several revisions and implementations. Source: ISO/IEC 9899:2024 §6.10.10.
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
typedef typeof(0) counter; /* C23 */
#else
#include <stdbool.h> /* C99/C11: bool is a macro */
typedef int counter;
#endif
#if !defined(__STDC_HOSTED__) || !__STDC_HOSTED__
#define LOG(...) ((void)0) /* freestanding: no stdio */
#endif
Keywords by revision¶
Each revision only adds. No keyword has ever been removed, which is why a 1990 source file still parses: the cost is paid in the underscore-capital spellings, chosen precisely because no conforming program could already have used them as identifiers.
| Revision | Count | Keywords added | Note |
|---|---|---|---|
| C89 / C90 | 32 | auto, break, case, char, const, continue, default, do, double, else, enum, extern, float, for, goto, if, int, long, register, return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile, while | The original set; unchanged by C95 |
| C99 | +5 = 37 | inline, restrict, _Bool, _Complex, _Imaginary | The underscore-capital spellings avoid colliding with existing identifiers |
| C11 | +7 = 44 | _Alignas, _Alignof, _Atomic, _Generic, _Noreturn, _Static_assert, _Thread_local | C17 adds none. <stdalign.h>, <stdbool.h> and <stdnoreturn.h> provide the friendly spellings |
| C23 | +15 = 59 | alignas, alignof, bool, constexpr, false, nullptr, static_assert, thread_local, true, typeof, typeof_unqual, _BitInt, _Decimal32, _Decimal64, _Decimal128 | The friendly spellings become keywords in their own right; the underscore forms are kept for compatibility |
The keyword set, cumulative. C23’s 59 entries include the fourteen underscore-prefixed spellings retained from C99 and C11 plus _BitInt. These counts are the source of the chart in the article’s standard lineage. Source: ISO/IEC 9899:2024 §6.4.1 and the corresponding clause of each earlier revision.
Types, limits, and fixed-width integers¶
C guarantees minimum ranges, not sizes. The exact-width types in <stdint.h> exist for
code that needs a layout; the least- and fast- families exist for code that needs a range and should not fail
to compile on a target without that exact width.
| Type | Size | Guaranteed range | Note |
|---|---|---|---|
char | 1 byte by definition | at least 8 bits | Signedness is implementation-defined; a distinct type from both signed char and unsigned char |
short | at least 2 bytes | ±32767 | Promoted to int in expressions |
int | at least 2 bytes; 4 in practice | ±32767 | The type integer constants and promotions land in |
long | at least 4 bytes | ±2147483647 | 8 bytes on LP64 (Unix), 4 on LLP64 (Windows) — the classic portability trap |
long long | at least 8 bytes | ±9223372036854775807 | C99 |
bool | 1 byte typically | true or false | _Bool in C99–C17; converts any nonzero value to 1 |
float, double, long double | 4, 8, and 8–16 bytes | IEEE-754 where __STDC_IEC_60559_BFP__ is defined | long double is 80-bit extended on x86 Unix, 64-bit on MSVC |
_BitInt(N) | exactly N bits | as declared | C23; the first standard type with a programmer-chosen width |
size_t, ptrdiff_t | target-dependent | unsigned / signed | The types of sizeof and of pointer difference |
intN_t, uintN_t | exactly N bits | exact | Optional; absent if the target has no such type |
int_leastN_t, int_fastN_t | at least N bits | at least | Always present; the portable choice |
intmax_t, uintmax_t | widest supported | widest | Also intptr_t and uintptr_t, both optional |
Types and their guarantees. Sizes described as ‘in practice’ are what mainstream 64-bit targets do, not requirements. Source: ISO/IEC 9899:2024 §6.2.5, §7.20 and <limits.h>.
/* Integer promotion and the usual arithmetic conversions, in three lines
that surprise everyone once. */
unsigned char a = 200, b = 200;
int c = a + b; /* 400: both promote to int before adding */
int i = -1;
unsigned u = 1;
if (i < u) { /* not taken */ } /* i converts to unsigned: 4294967295 < 1 */
char x = 'A';
printf("%zu\n", sizeof x); /* 1 */
printf("%zu\n", sizeof +x); /* 4: unary + promotes */
Two rules generate most integer bugs. Integer promotion converts anything narrower than
int to int before arithmetic. The usual arithmetic conversions then
bring both operands to a common type, and when the operands have the same rank but different signedness, the
unsigned type wins. Signed overflow is undefined; unsigned arithmetic wraps by definition. C23’s
<stdckdint.h> is the first standard facility for detecting the former without invoking
it.
Operators and precedence¶
Sixteen levels, highest binding first. Two of them are historical accidents that still cause bugs and are flagged in the notes: shift binds looser than addition, and the bitwise operators bind looser than the equality operators.
| Level | Operators | Associativity | Group and note |
|---|---|---|---|
| 1 | a[i] f(...) . -> a++ a-- (type){...} | Left to right | Postfix |
| 2 | ++a --a +a -a ! ~ *p &a sizeof alignof | Right to left | Unary |
| 3 | (type)x | Right to left | Cast |
| 4 | * / % | Left to right | Multiplicative |
| 5 | + - | Left to right | Additive |
| 6 | << >> | Left to right | Shift — lower than additive, which is why a << b + c shifts by b + c |
| 7 | < <= > >= | Left to right | Relational |
| 8 | == != | Left to right | Equality |
| 9 | & | Left to right | Bitwise AND — lower than equality, so x & 1 == 0 parses as x & (1 == 0) |
| 10 | ^ | Left to right | Bitwise XOR |
| 11 | | | Left to right | Bitwise OR |
| 12 | && | Left to right | Logical AND; sequences and short-circuits |
| 13 | || | Left to right | Logical OR; sequences and short-circuits |
| 14 | ?: | Right to left | Conditional; evaluates exactly one branch |
| 15 | = += -= *= /= %= <<= >>= &= ^= |= | Right to left | Assignment |
| 16 | , | Left to right | Comma; sequences its operands and yields the right one |
Operator precedence and associativity. The levels are a conventional rendering of the grammar in §6.5, which expresses precedence through nested production rules rather than a table. Precedence governs parsing only: except for &&, ||, ?: and ,, it says nothing about evaluation order. Source: ISO/IEC 9899:2024 §6.5.
Reading a declaration¶
C declarations mirror use: the declaration of p is written as the expression that yields the
base type. Read from the identifier, then right, then left, honouring parentheses.
Function declarators are where the rule earns its keep, because a function pointer, a function returning a pointer, and an array of function pointers differ only in where the parentheses sit. What each one is for — dispatch tables, callbacks, and the reason a function is not itself a value — is in the article, under function pointers and indirect calls.
| Declaration | Reads as | Why |
|---|---|---|
int *p[10] | array of 10 pointers to int | [] binds tighter than * |
int (*p)[10] | pointer to an array of 10 int | Parentheses force the pointer first |
int *f(void) | function returning pointer to int | () binds tighter than * |
int (*f)(void) | pointer to function returning int | The form every callback table uses |
int (*fa[5])(void) | array of 5 pointers to function returning int | Read outward from fa |
const char *p | pointer to const char — the characters are read-only | p may be reassigned |
char * const p | const pointer to char — the pointer is read-only | *p may be written |
const char * const p | const pointer to const char | Both fixed |
char *restrict p | pointer promising exclusive access to what it points at | A promise to the optimiser, checked by nobody |
The declarations that are misread most often. typedef exists largely to stop the last three rows from nesting. Source: ISO/IEC 9899:2024 §6.7.6.
/* The same type, twice: once inline, once via typedef. */
void (*signal(int sig, void (*handler)(int)))(int);
typedef void handler_t(int);
handler_t *signal(int sig, handler_t *handler); /* identical, readable */
Preprocessor directives and predefined macros¶
The preprocessor is a separate language operating on token sequences, executed in translation phase 4 before any C construct is parsed. It has no types, no scope, and no knowledge of the program — which is both why it is portable and why every C project eventually contains a macro nobody wants to touch.
| Directive | What it does | Note |
|---|---|---|
#include | Textual inclusion of a header or source file | <…> searches the system paths, "…" the current directory first |
#define, #undef | Object-like and function-like macros | # stringises a parameter, ## pastes tokens |
#if, #elif, #else, #endif | Conditional compilation | Only integer constant expressions and defined are evaluable |
#ifdef, #ifndef | Shorthand for #if defined(X) | C23 adds #elifdef and #elifndef |
#error, #warning | Stop, or complain, at preprocessing time | #warning standardised in C23 after decades as an extension |
#pragma | Implementation-defined directive | _Pragma("…") is the operator form, usable inside a macro |
#embed | Include a binary resource as a list of integer constants | C23; replaces build-time xxd -i generators |
#line | Reset the reported line number and file name | Used by code generators |
Preprocessing directives. Source: ISO/IEC 9899:2024 §6.10.
| Macro | Expands to | Note |
|---|---|---|
__FILE__, __LINE__ | Current file name and line number | The basis of assert |
__DATE__, __TIME__ | Translation date and time | Defeats reproducible builds; avoid |
__func__ | The enclosing function’s name | C99; an identifier, not a macro, so it cannot be pasted |
__VA_ARGS__, __VA_OPT__ | Variadic macro arguments; C23 adds the conditional form | __VA_OPT__(,) solves the trailing-comma problem |
__COUNTER__ | A distinct integer per expansion | GCC/Clang/MSVC extension, not standard |
Predefined macros other than the feature-test macros listed under Language modes. Source: ISO/IEC 9899:2024 §6.10.10.
/* The two idioms worth memorising. */
#define STR_(x) #x
#define STR(x) STR_(x) /* expands x before stringising it */
#define LOG(fmt, ...) \
fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__ __VA_OPT__(,) __VA_ARGS__)
Standard headers by revision¶
| Revision | Total | Headers added | Note |
|---|---|---|---|
| C89 / C90 | 15 | <assert.h>, <ctype.h>, <errno.h>, <float.h>, <limits.h>, <locale.h>, <math.h>, <setjmp.h>, <signal.h>, <stdarg.h>, <stddef.h>, <stdio.h>, <stdlib.h>, <string.h>, <time.h> | The original library |
| C95 | +3 = 18 | <iso646.h>, <wchar.h>, <wctype.h> | Wide characters and digraphs |
| C99 | +6 = 24 | <complex.h>, <fenv.h>, <inttypes.h>, <stdbool.h>, <stdint.h>, <tgmath.h> | <complex.h> and <tgmath.h> became optional in C11 |
| C11 | +5 = 29 | <stdalign.h>, <stdatomic.h>, <stdnoreturn.h>, <threads.h>, <uchar.h> | <threads.h> and <stdatomic.h> are conditional features |
| C23 | +2 = 31 | <stdbit.h>, <stdckdint.h> | <stdalign.h>, <stdbool.h> and <stdnoreturn.h> become obsolescent, since their contents are keywords |
The standard headers, cumulative. These counts are the second series in the chart on the article’s standard lineage. Source: clause 7 of each revision.
A freestanding implementation must provide only the headers that declare no functions,
which in C23 are <float.h>, <iso646.h>, <limits.h>, <stdalign.h>, <stdarg.h>, <stdbit.h>, <stdbool.h>, <stddef.h>, <stdint.h>, <stdnoreturn.h> and
<stddef.h>’s type definitions. Everything else — including
<stdio.h> and <stdlib.h> — is optional, which is what makes kernel
and firmware C conforming rather than a dialect.
Standard library map¶
The C standard library is small by modern standards: no containers, no networking, no filesystem traversal, no regular expressions, no string type. Those live in POSIX, in the platform SDK, or in a third-party library, which is why a portable C project’s dependency list starts where this table stops.
| Header group | Covers | Representative names | Note |
|---|---|---|---|
<stdio.h> | Streams and formatted I/O | fopen, fread, printf, snprintf, fgets | gets was removed in C11; snprintf is the only safe formatter |
<stdlib.h> | Allocation, conversion, process control, sorting | malloc, free, strtol, qsort, abort | atoi cannot report failure; use strtol |
<string.h> | Byte and string handling | memcpy, memmove, strlen, strncpy, memcmp | strncpy does not guarantee termination; memcpy requires non-overlap |
<stdint.h>, <inttypes.h> | Fixed-width integers and their format macros | uint32_t, INT64_MAX, PRIu64 | The portable way to print a fixed-width type |
<math.h>, <fenv.h>, <tgmath.h> | Floating point, rounding modes, type-generic wrappers | fma, nextafter, fesetround | Link with -lm on Unix |
<stdatomic.h>, <threads.h> | Atomics, threads, mutexes, condition variables | atomic_load_explicit, thrd_create, mtx_lock | Both conditional; check the __STDC_NO_* macros |
<stdbit.h> | Bit counting and rounding (C23) | stdc_leading_zeros, stdc_popcount, stdc_bit_ceil | Standardises what was previously a compiler builtin |
<stdckdint.h> | Checked integer arithmetic (C23) | ckd_add, ckd_sub, ckd_mul | Returns whether the operation overflowed rather than invoking undefined behaviour |
<setjmp.h>, <signal.h> | Non-local jumps and signal handling | setjmp, longjmp, signal | What a signal handler may portably do is a very short list |
<time.h>, <locale.h> | Calendar time, clocks, locale selection | clock_gettime (POSIX), timespec_get, setlocale | timespec_get is the C11 portable monotonic-ish clock |
<wchar.h>, <uchar.h> | Wide and Unicode characters | mbrtowc, c16rtomb, char8_t | wchar_t is 16-bit on Windows, 32-bit on Unix — not a portable encoding |
The standard library by area, with the traps that most often appear in review. Source: ISO/IEC 9899:2024 clause 7.
Conversion specifiers¶
Format strings are unchecked at run time: the variadic call passes what the caller wrote, and a mismatch
between the specifier and the argument is undefined behaviour. Compilers do check literal format strings
— -Wformat is enabled by -Wall — which is the single best reason never to
build a format string dynamically.
| Specifier | Argument type | Meaning | Note |
|---|---|---|---|
%d, %i | int | Signed decimal | %i differs from %d only in scanf, where it accepts 0x and 0 prefixes |
%u, %o, %x, %X | unsigned int | Decimal, octal, hexadecimal | %#x prefixes 0x |
%b, %B | unsigned int | Binary (C23) | New in C23 |
%f, %e, %g, %a | double | Fixed, scientific, shorter of the two, hexadecimal float | %a round-trips exactly; %g does not |
%c, %s | int, char * | Character, string | %.*s takes a precision argument and does not require termination |
%p | void * | Pointer, implementation-defined form | Cast the argument; passing any other pointer type is undefined |
%n | int * | Stores the count written so far | A format-string attack primitive; disabled by hardened libcs |
%% | — | A literal percent sign | |
hh, h | char, short | Length modifier | Arguments still arrive promoted to int |
l, ll | long, long long | Length modifier | %ld is wrong for int64_t on Windows |
z, t, j | size_t, ptrdiff_t, intmax_t | Length modifier | %zu is the correct specifier for a sizeof result |
L | long double | Length modifier | |
PRId64, PRIu32, … | Fixed-width types | Macros from <inttypes.h> | The only portable way to print int64_t: printf("%" PRId64, x) |
Conversion specifiers, then length modifiers. Both tables apply to the whole printf and scanf family; scanf additionally requires a pointer argument and a width for %s. Source: ISO/IEC 9899:2024 §7.23.6.
Undefined behaviour: the common cases¶
Annex J.2 of the standard lists undefined behaviour in the low hundreds and is explicitly not exhaustive. The rows below are the ones that appear in real defect reports, each paired with the tool that finds it. The classification and the reasoning behind it are in the article’s treatment of behaviour classes.
| Case | What triggers it | What finds it |
|---|---|---|
| Out-of-bounds access | a[n] where n >= the element count | AddressSanitizer; -fsanitize=bounds; -fbounds-safety where available |
| Use after free, double free | Touching memory after free, or freeing twice | AddressSanitizer; hardened allocators; -fsanitize=address in CI |
| Signed integer overflow | INT_MAX + 1, -INT_MIN, 1 << 31 on int | -fsanitize=signed-integer-overflow; <stdckdint.h>; -fwrapv to define it |
| Uninitialised read | Reading an automatic object before storing to it | MemorySanitizer; -ftrivial-auto-var-init=zero; -Wmaybe-uninitialized |
| Null dereference | Dereferencing a pointer that may be null, including after a checked allocation | -fsanitize=null; nullability annotations in Clang |
| Strict-aliasing violation | Reading an object through an incompatible lvalue type | -fstrict-aliasing -Wstrict-aliasing; use memcpy or a union |
| Misaligned access | Casting a char * to a wider type at an odd offset | -fsanitize=alignment; memcpy into an aligned object |
| Data race | Two threads, one write, no synchronisation | ThreadSanitizer; _Atomic; a mutex |
| Unsequenced modification | i = i++, a[i] = i++ | -Wsequence-point (GCC), -Wunsequenced (Clang) |
| Library preconditions | memcpy with overlapping ranges, strcpy into a short buffer, free of a non-allocated pointer | _FORTIFY_SOURCE=3; memmove; bounded functions |
| Lifetime escape | Returning the address of an automatic object, or of a compound literal in a nested block | -Wreturn-local-addr; AddressSanitizer’s stack-use-after-return mode |
The undefined behaviours worth building a pipeline around. None of these is diagnosed reliably by the compiler alone. Source: ISO/IEC 9899:2024 Annex J.2, and the GCC and LLVM sanitizer documentation.
Sanitizers and hardening flags¶
Worked transcripts of ThreadSanitizer catching a data race and a lock-order inversion — in programs that print the right answer and never hang — are on the synchronisation page.
Two distinct sets, and confusing them wastes both. Sanitizers are test-time instrumentation that finds undefined behaviour when the test happens to reach it. Hardening flags are release-time mitigations that turn an exploitable defect into a crash. A project that is serious about C uses both, and neither is a substitute for the other.
| Flag | When | What it catches or mitigates | Cost and caveat |
|---|---|---|---|
-fsanitize=address | Test builds | Out-of-bounds, use-after-free, leaks | ~2× slower; incompatible with the other sanitizers except UBSan |
-fsanitize=undefined | Test builds | Overflow, misalignment, invalid shifts, null | Cheap enough that some projects ship it with -fsanitize-trap |
-fsanitize=thread | Test builds | Data races | Large memory overhead; run separately |
-fsanitize=memory | Test builds | Uninitialised reads | Clang only; needs an instrumented libc |
-D_FORTIFY_SOURCE=3 | Release builds | Bounded versions of memcpy, sprintf and friends | Requires optimisation to be enabled; level 3 needs GCC 12 or Clang 15 |
-fstack-protector-strong | Release builds | Stack canaries on functions with local buffers | The strong variant is the accepted default; -all costs more |
-ftrivial-auto-var-init=zero | Release builds | Zeroes uninitialised locals | Turns an information leak into a deterministic bug |
-fstack-clash-protection | Release builds | Large stack allocations skipping the guard page | |
-fcf-protection=full, -mbranch-protection=standard | Release builds | Hardware control-flow integrity: Intel CET, Arm PAC and BTI | Needs hardware and OS support to take effect |
-fsanitize=cfi | Release builds | Indirect-call type confusion | Clang, with LTO |
-Wl,-z,relro,-z,now, -pie | Link | Read-only relocations, full RELRO, ASLR | Distribution defaults on most Linux targets |
-fno-delete-null-pointer-checks | Kernel-style builds | Keeps null checks the optimiser would otherwise remove | Used where address zero is mappable and the standard’s assumption does not hold |
Sanitizers above the rule, hardening flags below. Availability and spelling differ between GCC, Clang and MSVC; the forms shown are the GCC and Clang spellings. Source: GCC and LLVM documentation and published distribution hardening guidance.
# Two builds, one source tree. The first finds bugs, the second survives them.
cc -std=c23 -O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer -o app-test *.c
cc -std=c23 -O2 -D_FORTIFY_SOURCE=3 -fstack-protector-strong \
-ftrivial-auto-var-init=zero -fcf-protection=full -pie -Wl,-z,relro,-z,now -o app *.c
Sources¶
- ISO/IEC 9899:2024, Programming languages — C, with public working draft N3220 for clause numbering: §6.4.1 keywords, §6.5 expressions, §6.7.6 declarators, §6.10 preprocessing, §7 the library, §7.23.6 formatted I/O, Annex J undefined and implementation-defined behaviour.
- ISO/IEC 9899:1990, its 1995 amendment, 9899:1999, 9899:2011 and 9899:2018, for the per-revision keyword and header lists.
- GCC documentation — invocation options, C dialect options, warning options, instrumentation options, and the C status page.
- Clang and LLVM documentation — the user manual, the sanitizer manuals (Address, Undefined
Behavior, Thread, Memory),
-fbounds-safety, and the C status page. - Microsoft C/C++ documentation — compiler options and C language conformance.
- Published distribution hardening guidance, for the release-build flag set.
Code examples are original. Where a table condenses a longer normative document, the caption names the clause so the full text can be consulted.