diff --git a/content/c-alignas-alignof.md b/content/c-alignas-alignof.md new file mode 100644 index 0000000..0476471 --- /dev/null +++ b/content/c-alignas-alignof.md @@ -0,0 +1,33 @@ +--- +execute: true +show_assembly: true +flags: "-std=c11" +--- + +## What It Does + +`_Alignas(N)` specifies the alignment requirement for a variable or type. `_Alignof(type)` +yields the alignment requirement of a type. The `` header provides the alternative +spellings `alignas` and `alignof`. + +## Why It Matters + +Some hardware operations and data structures require specific alignment for correctness or +performance. SIMD operations may require 16-byte or 32-byte alignment. Memory-mapped hardware +registers may have alignment constraints. These operators provide portable alignment control. + +## Example + +```c +#include +#include + +int main(void) { + alignas(32) char buffer[64]; + alignas(double) char storage[sizeof(double)]; + + printf("Alignment of int: %zu\n", alignof(int)); + printf("Alignment of double: %zu\n", alignof(double)); + printf("Buffer alignment: %zu\n", (size_t)buffer % 32 == 0 ? 32 : 0); +} +``` diff --git a/content/c-anonymous-struct-union.md b/content/c-anonymous-struct-union.md new file mode 100644 index 0000000..09431e6 --- /dev/null +++ b/content/c-anonymous-struct-union.md @@ -0,0 +1,49 @@ +--- +execute: true +show_assembly: true +flags: "-std=c11" +--- + +## What It Does + +Anonymous structures and unions are members without a declared name. Their members are accessed +directly as if they were members of the containing structure or union, without an intermediate +member name. + +## Why It Matters + +Nested structures and unions normally require accessing members through the intermediate member +name. Anonymous members flatten the access path, simplifying code that accesses nested data +and enabling natural representation of variant types. + +## Example + +```c +#include + +struct Vector3 { + union { + struct { float x, y, z; }; // Anonymous struct + float components[3]; // Same memory + }; // Anonymous union +}; + +struct Variant { + int type; + union { + int i; + double d; + char s[16]; + }; +}; + +int main(void) { + struct Vector3 v = {.x = 1.0f, .y = 2.0f, .z = 3.0f}; + + printf("v.x = %f\n", v.x); + printf("v.components[1] = %f\n", v.components[1]); + + struct Variant var = {.type = 0, .i = 42}; + printf("Integer variant: %d\n", var.i); +} +``` diff --git a/content/c-atomic.md b/content/c-atomic.md new file mode 100644 index 0000000..2f8514c --- /dev/null +++ b/content/c-atomic.md @@ -0,0 +1,41 @@ +--- +execute: true +show_assembly: true +flags: "-std=c11" +--- + +## What It Does + +`_Atomic` qualifies a type to enable atomic operations without data races. The `` +header provides atomic types, operations (`atomic_load()`, `atomic_store()`, `atomic_fetch_add()`, etc.), +and memory ordering specifications. Atomic operations are indivisible with respect to concurrent +access. + +## Why It Matters + +Concurrent access to shared variables without synchronization causes data races and undefined +behavior. Atomic types and operations provide lock-free synchronization primitives that are +guaranteed to execute as indivisible units, enabling correct concurrent programming. + +## Example + +```c +#include +#include + +int main(void) { + _Atomic int counter = 0; + + atomic_store(&counter, 10); + int old = atomic_fetch_add(&counter, 5); + + printf("Old value: %d\n", old); + printf("New value: %d\n", atomic_load(&counter)); + + // Compare-and-swap + int expected = 15; + if (atomic_compare_exchange_strong(&counter, &expected, 20)) { + printf("CAS succeeded, counter = %d\n", atomic_load(&counter)); + } +} +``` diff --git a/content/c-attributes.md b/content/c-attributes.md new file mode 100644 index 0000000..c1fc96b --- /dev/null +++ b/content/c-attributes.md @@ -0,0 +1,44 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +C23 introduces standard attributes using the `[[attribute]]` syntax. Standard attributes +include `[[nodiscard]]`, `[[maybe_unused]]`, `[[deprecated]]`, `[[fallthrough]]`, and +`[[noreturn]]`. The `__has_c_attribute` preprocessor operator tests for attribute support. + +## Why It Matters + +Compiler-specific attributes like `__attribute__` (GCC/Clang) and `__declspec` (MSVC) +are not portable. Standard attributes provide a uniform syntax across compilers, enabling +portable code that communicates intent to the compiler for diagnostics and optimization. + +## Example + +```c +#include +#include + +[[nodiscard]] int compute(int x) { + return x * 2; +} + +[[deprecated("use new_function instead")]] +void old_function(void) {} + +[[noreturn]] void fatal_error(const char* msg) { + fprintf(stderr, "Fatal: %s\n", msg); + exit(1); +} + +int main(void) { + [[maybe_unused]] int unused_var = 10; + + int result = compute(5); // OK: result is used + // compute(5); // Warning: nodiscard value ignored + + printf("Result: %d\n", result); +} +``` diff --git a/content/c-auto.md b/content/c-auto.md new file mode 100644 index 0000000..6859ef4 --- /dev/null +++ b/content/c-auto.md @@ -0,0 +1,34 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +`auto` in C23 enables type inference for object definitions with initializers. The compiler +deduces the type from the initializer expression. This applies only to object definitions, +not to function parameters or return types. + +## Why It Matters + +Declaring objects with complex types required writing the full type name, even when the +type was obvious from the initializer. Type inference reduces redundancy and makes code +more concise when the type is clear from context. + +## Example + +```c +#include + +int main(void) { + auto x = 42; // int + auto pi = 3.14159; // double + auto c = 'A'; // int (character constants have type int in C) + + int arr[] = {1, 2, 3}; + auto ptr = arr; // int* + + printf("x = %d, pi = %f, c = %c\n", x, pi, c); + printf("First element: %d\n", *ptr); +} +``` diff --git a/content/c-binary-literals.md b/content/c-binary-literals.md new file mode 100644 index 0000000..a8747cb --- /dev/null +++ b/content/c-binary-literals.md @@ -0,0 +1,40 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +Integer constants can be written in binary using the `0b` or `0B` prefix followed by binary +digits (0 and 1). Binary literals can include digit separators (single quotes) for readability. + +## Why It Matters + +Bit manipulation code with hexadecimal or decimal constants requires mental conversion to +understand bit patterns. Binary literals express bit patterns directly, making hardware +register values, masks, and flag combinations self-documenting. + +## Example + +```c +#include + +int main(void) { + int mask = 0b11110000; // 240 in decimal + int flags = 0b0000'0101; // With digit separator + + int value = 0b10101010; + int result = value & mask; + + printf("mask = %d (0x%X)\n", mask, mask); + printf("flags = %d\n", flags); + printf("value & mask = %d (0b", result); + + // Print binary representation + for (int i = 7; i >= 0; i--) { + printf("%d", (result >> i) & 1); + } + + printf(")\n"); +} +``` diff --git a/content/c-bitint.md b/content/c-bitint.md new file mode 100644 index 0000000..eda78b4 --- /dev/null +++ b/content/c-bitint.md @@ -0,0 +1,34 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +`_BitInt(N)` defines an integer type with exactly N bits of precision. Both signed and +unsigned variants are supported (`unsigned _BitInt(N)`). Bit-precise integer constants +use the `wb` or `uwb` suffix. The maximum supported width is implementation-defined but +at least `BITINT_MAXWIDTH` bits. + +## Why It Matters + +Standard integer types have implementation-defined sizes, requiring careful selection based +on range requirements. Cryptographic algorithms, hardware interfaces, and arbitrary-precision +arithmetic often need specific bit widths. `_BitInt` provides integers with exact precision, +independent of the platform's native word sizes. + +## Example + +```c +#include + +int main(void) { + _BitInt(128) large = 12345678901234567890123456789012345wb; + unsigned _BitInt(7) small = 100uwb; // Fits in 7 bits (0-127) + + _BitInt(256) huge = large * 2; + + printf("small = %d\n", (int)small); + printf("large fits in 128 bits\n"); +} +``` diff --git a/content/c-bool-true-false.md b/content/c-bool-true-false.md new file mode 100644 index 0000000..f8cf281 --- /dev/null +++ b/content/c-bool-true-false.md @@ -0,0 +1,35 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +`true`, `false`, and `bool` are predefined keywords in C23. `bool` is a standard type, +and `true` and `false` are constants of type `bool` with values 1 and 0 respectively. +Including `` is no longer necessary. + +## Why It Matters + +Prior to C23, using boolean types required including ``, which defined `bool` +as a macro for `_Bool` and `true`/`false` as macros for 1 and 0. Making these keywords +simplifies boolean usage and aligns C with other languages. + +## Example + +```c +#include + +bool is_even(int n) { + return n % 2 == 0; +} + +int main(void) { + bool flag = true; + bool result = is_even(42); + + printf("flag: %s\n", flag ? "true" : "false"); + printf("42 is even: %s\n", result ? "true" : "false"); + printf("sizeof(bool): %zu\n", sizeof(bool)); +} +``` diff --git a/content/c-compound-literals.md b/content/c-compound-literals.md new file mode 100644 index 0000000..d340fd5 --- /dev/null +++ b/content/c-compound-literals.md @@ -0,0 +1,53 @@ +--- +execute: true +show_assembly: true +flags: "-std=c99" +--- + +## What It Does + +Compound literals create unnamed objects of a specified type using the syntax +`(type){initializer}`. The object has automatic storage duration when created in block +scope and static storage duration at file scope. They can be used wherever an lvalue +of that type is expected. + +## Why It Matters + +Passing structures or arrays to functions previously required declaring named variables. +Compound literals allow creating temporary aggregates inline, simplifying function calls +and reducing the need for intermediate variables. + +## Example + +```c +#include + +struct Point { int x, y; }; + +void print_point(struct Point p) { + printf("(%d, %d)\n", p.x, p.y); +} + +int sum_array(int* arr, int n) { + int total = 0; + + for (int i = 0; i < n; i++) { + total += arr[i]; + } + + return total; +} + +int main(void) { + // Compound literal as function argument + print_point((struct Point){10, 20}); + + // Compound literal array + int total = sum_array((int[]){1, 2, 3, 4, 5}, 5); + printf("Sum: %d\n", total); + + // Taking address of compound literal + int* ptr = (int[]){100, 200, 300}; + printf("Second element: %d\n", ptr[1]); +} +``` diff --git a/content/c-constexpr.md b/content/c-constexpr.md new file mode 100644 index 0000000..8184643 --- /dev/null +++ b/content/c-constexpr.md @@ -0,0 +1,35 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +`constexpr` specifies that an object's value is a constant expression and must be computable +at compile time. Unlike `const`, which permits runtime initialization, `constexpr` objects +must be initialized with values known during compilation. The initializer must consist of +constant expressions only. + +## Why It Matters + +`const` objects in C can be initialized at runtime, so they cannot be used in contexts +requiring compile-time constants (array sizes in some contexts, case labels). `constexpr` +guarantees compile-time evaluation, making objects usable wherever constant expressions +are required. + +## Example + +```c +#include + +constexpr int array_size = 10; +constexpr double pi = 3.14159265358979; + +int main(void) { + int arr[array_size]; // OK: array_size is a constant expression + + constexpr int doubled = array_size * 2; + printf("Array size: %d, doubled: %d\n", array_size, doubled); + printf("Pi: %f\n", pi); +} +``` diff --git a/content/c-designated-init.md b/content/c-designated-init.md new file mode 100644 index 0000000..5b5cc93 --- /dev/null +++ b/content/c-designated-init.md @@ -0,0 +1,53 @@ +--- +execute: true +show_assembly: true +flags: "-std=c99" +--- + +## What It Does + +Designated initializers specify which member or element to initialize using `.member` for +structures/unions or `[index]` for arrays. Unspecified members are zero-initialized. +Initializers can be given in any order and can be mixed with positional initializers. + +## Why It Matters + +Positional initialization requires remembering member order and initializing all preceding +members. Designated initializers document which field is being set, allow partial initialization +with automatic zeroing, and remain correct when structure layout changes. + +## Example + +```c +#include + +struct Point { + int x, y, z; +}; + +struct Config { + int timeout; + int retries; + char name[32]; +}; + +int main(void) { + struct Point p = {.y = 10, .x = 5}; // z is zero-initialized + + printf("Point: (%d, %d, %d)\n", p.x, p.y, p.z); + + struct Config cfg = { + .name = "default", + .timeout = 30 + // retries is zero-initialized + }; + + printf("Config: %s, timeout=%d, retries=%d\n", + cfg.name, cfg.timeout, cfg.retries); + + int arr[5] = {[2] = 100, [4] = 200}; // Others are zero + + printf("Array: %d %d %d %d %d\n", + arr[0], arr[1], arr[2], arr[3], arr[4]); +} +``` diff --git a/content/c-digit-separators.md b/content/c-digit-separators.md new file mode 100644 index 0000000..5e56c04 --- /dev/null +++ b/content/c-digit-separators.md @@ -0,0 +1,34 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +Single quote characters (`'`) can appear within integer and floating-point constants to +separate digit groups. The separators are ignored during compilation and serve only to +improve readability. + +## Why It Matters + +Large numeric constants are difficult to read without visual separation. Digit separators +allow grouping digits by thousands, bytes, or any logical boundary, making constants like +memory sizes, timestamps, or financial values self-documenting. + +## Example + +```c +#include + +int main(void) { + long population = 8'000'000'000; + int memory_size = 0x0100'0000; // 16 MB in hex + int permissions = 0b111'101'101; // rwxr-xr-x + double avogadro = 6.022'140'76e23; + + printf("Population: %ld\n", population); + printf("Memory: %d bytes\n", memory_size); + printf("Permissions: %o (octal)\n", permissions); + printf("Avogadro: %e\n", avogadro); +} +``` diff --git a/content/c-elifdef.md b/content/c-elifdef.md new file mode 100644 index 0000000..980b326 --- /dev/null +++ b/content/c-elifdef.md @@ -0,0 +1,37 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +`#elifdef` and `#elifndef` are preprocessor directives that combine `#elif` with `#ifdef` +and `#ifndef` respectively. `#elifdef X` is equivalent to `#elif defined(X)`, and +`#elifndef X` is equivalent to `#elif !defined(X)`. + +## Why It Matters + +Conditional compilation chains testing macro definitions required the verbose `#elif defined()` +form. The new directives provide concise syntax matching the existing `#ifdef` and `#ifndef` +directives, improving readability of preprocessor conditionals. + +## Example + +```c +#include + +// #define USE_FEATURE_A +#define USE_FEATURE_B + +int main(void) { +#ifdef USE_FEATURE_A + printf("Feature A enabled\n"); +#elifdef USE_FEATURE_B + printf("Feature B enabled\n"); +#elifndef USE_FEATURE_C + printf("Feature C not defined, using default\n"); +#else + printf("No features\n"); +#endif +} +``` diff --git a/content/c-embed.md b/content/c-embed.md new file mode 100644 index 0000000..703f673 --- /dev/null +++ b/content/c-embed.md @@ -0,0 +1,35 @@ +--- +show_only: true +--- + +## What It Does + +`#embed` is a preprocessor directive that includes the contents of a binary file as a +comma-separated list of integer values. It supports parameters like `limit()` to restrict +the number of bytes, `prefix()` and `suffix()` to add surrounding elements, and `if_empty()` +to provide a fallback when the file is empty. + +## Why It Matters + +Embedding binary data (images, fonts, firmware) into C programs previously required external +tools to convert files into C arrays or the use of compiler-specific extensions. `#embed` +provides a standard, portable mechanism to include binary resources directly, with control +over how the data is incorporated. + +## Example + +```c +#include + +static const unsigned char icon[] = { +#embed "icon.png" limit(64) if_empty(0) +}; + +int main(void) { + printf("First %zu bytes of icon: ", sizeof(icon)); + for (size_t i = 0; i < sizeof(icon) && i < 8; ++i) { + printf("%02x ", icon[i]); + } + printf("\n"); +} +``` diff --git a/content/c-empty-init.md b/content/c-empty-init.md new file mode 100644 index 0000000..082f094 --- /dev/null +++ b/content/c-empty-init.md @@ -0,0 +1,36 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +Empty initializers `{}` zero-initialize an object. For structures and arrays, all members +and elements are zero-initialized. For unions, the first member is zero-initialized. +This applies to both scalar and aggregate types. + +## Why It Matters + +Zero-initializing aggregates previously required explicit `{0}`, which triggers warnings +in some compilers when applied to structures whose first member is itself an aggregate. +Empty braces provide unambiguous zero-initialization for any type without such warnings. + +## Example + +```c +#include + +struct Point { + int x, y; +}; + +int main(void) { + struct Point p = {}; // Both members zero-initialized + int arr[5] = {}; // All elements zero-initialized + int scalar = {}; // Zero-initialized + + printf("Point: (%d, %d)\n", p.x, p.y); + printf("Array[0]: %d\n", arr[0]); + printf("Scalar: %d\n", scalar); +} +``` diff --git a/content/c-fixed-enum.md b/content/c-fixed-enum.md new file mode 100644 index 0000000..2c2c173 --- /dev/null +++ b/content/c-fixed-enum.md @@ -0,0 +1,42 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +Enumerations can specify a fixed underlying type using the syntax `enum Name : Type { ... }`. +The underlying type determines the size and signedness of the enumeration. +Without a fixed type, the underlying type is implementation-defined. + +## Why It Matters + +The size of enumeration types was implementation-defined, complicating binary compatibility +and serialization. +A fixed underlying type guarantees specific storage and range characteristics, +enabling precise control over enumeration layout in structures and data formats. + +## Example + +```c +#include +#include + +enum Color : uint8_t { + RED = 0, + GREEN = 1, + BLUE = 2 +}; + +enum LargeValues : int64_t { + BIG = 1000000000000LL +}; + +int main(void) { + printf("sizeof(enum Color) = %zu\n", sizeof(enum Color)); + printf("sizeof(enum LargeValues) = %zu\n", sizeof(enum LargeValues)); + + enum Color c = GREEN; + printf("Color value: %d\n", c); +} +``` diff --git a/content/c-flexible-array.md b/content/c-flexible-array.md new file mode 100644 index 0000000..bcd11ae --- /dev/null +++ b/content/c-flexible-array.md @@ -0,0 +1,50 @@ +--- +execute: true +show_assembly: true +flags: "-std=c99" +--- + +## What It Does + +A flexible array member is an array declared with no size as the last member of a structure. +The structure is allocated with additional space beyond its declared size, and the flexible +array member uses that space. The syntax is `type member[];` as the final member. + +## Why It Matters + +Variable-size data structures traditionally used a pointer member requiring separate allocation. +Flexible array members allow the variable-size data to be contiguous with the structure header, +improving cache locality and simplifying memory management to a single allocation. + +## Example + +```c +#include +#include +#include + +struct Message { + int type; + size_t length; + char data[]; // Flexible array member +}; + +struct Message* create_message(int type, const char* text) { + size_t len = strlen(text) + 1; + struct Message* msg = malloc(sizeof(struct Message) + len); + msg->type = type; + msg->length = len; + memcpy(msg->data, text, len); + return msg; +} + +int main(void) { + struct Message* msg = create_message(1, "Hello, World!"); + + printf("Type: %d\n", msg->type); + printf("Length: %zu\n", msg->length); + printf("Data: %s\n", msg->data); + + free(msg); +} +``` diff --git a/content/c-func.md b/content/c-func.md new file mode 100644 index 0000000..d12e870 --- /dev/null +++ b/content/c-func.md @@ -0,0 +1,47 @@ +--- +execute: true +show_assembly: true +--- +## What It Does + +`__func__` is a predefined identifier that expands to a string literal containing the name +of the enclosing function. It is implicitly declared as `static const char __func__[]` +at the beginning of each function body. + +## Why It Matters + +Debugging messages and error reports benefit from including the function name. Manually +writing function names in strings requires updating strings when renaming functions. +`__func__` provides the name automatically, keeping error messages synchronized with code. + +## Example + +```c +#include + +void log_entry(void) { + printf("Entering: %s\n", __func__); +} + +int calculate(int x) { + printf("[%s] Input: %d\n", __func__, x); + return x * 2; +} + +#define LOG_ERROR(msg) fprintf(stderr, "%s: %s\n", __func__, msg) + +void process(int value) { + if (value < 0) { + LOG_ERROR("negative value not allowed"); + return; + } + printf("%s: processing %d\n", __func__, value); +} + +int main(void) { + log_entry(); + calculate(21); + process(-1); + process(5); +} +``` diff --git a/content/c-generic.md b/content/c-generic.md new file mode 100644 index 0000000..970e57e --- /dev/null +++ b/content/c-generic.md @@ -0,0 +1,48 @@ +--- +execute: true +show_assembly: true +flags: "-std=c11" +--- + +## What It Does + +`_Generic(expression, type1: result1, type2: result2, ..., default: resultN)` selects one +of several expressions based on the type of the controlling expression. The result type must +match exactly (no implicit conversions). An optional `default` case handles unmatched types. + +## Why It Matters + +C lacks function overloading, so type-generic operations required separate function names +or macro tricks. `_Generic` enables type-safe generic macros that select the appropriate +implementation based on argument type, providing a form of compile-time polymorphism. + +## Example + +```c +#include +#include +#include + +#define abs(x) _Generic((x), \ + int: abs, \ + long: labs, \ + float: fabsf, \ + double: fabs \ +)(x) + +#define print_type(x) _Generic((x), \ + int: "int", \ + double: "double", \ + char*: "char*", \ + default: "unknown" \ +) + +int main(void) { + printf("abs(-5) = %d\n", abs(-5)); + printf("abs(-3.14) = %f\n", abs(-3.14)); + + int i = 42; + double d = 3.14; + printf("i is %s, d is %s\n", print_type(i), print_type(d)); +} +``` diff --git a/content/c-hex-float.md b/content/c-hex-float.md new file mode 100644 index 0000000..1d62df6 --- /dev/null +++ b/content/c-hex-float.md @@ -0,0 +1,40 @@ +--- +execute: true +show_assembly: true +flags: "-std=c99" +--- + +## What It Does + +Hexadecimal floating-point constants use the format `0xH.HHHpE` where `H` represents hexadecimal +digits, `p` introduces the binary exponent, and `E` is a decimal exponent applied to power of 2. +The value is `mantissa × 2^exponent`. + +## Why It Matters + +Decimal floating-point literals cannot represent all binary floating-point values exactly. +Hexadecimal floating-point literals specify the exact bit pattern, enabling precise +representation of specific floating-point values without rounding errors from decimal conversion. + +## Example + +```c +#include +#include + +int main(void) { + double one = 0x1.0p0; // 1.0 × 2^0 = 1.0 + double two = 0x1.0p1; // 1.0 × 2^1 = 2.0 + double half = 0x1.0p-1; // 1.0 × 2^-1 = 0.5 + double pi_approx = 0x1.921fb54442d18p+1; // Close to π + + printf("one = %f\n", one); + printf("two = %f\n", two); + printf("half = %f\n", half); + printf("pi_approx = %.15f\n", pi_approx); + + // Exact representation of specific values + double smallest_normal = 0x1.0p-1022; // DBL_MIN + printf("Smallest normal: %e\n", smallest_normal); +} +``` diff --git a/content/c-inline.md b/content/c-inline.md new file mode 100644 index 0000000..2b08f2b --- /dev/null +++ b/content/c-inline.md @@ -0,0 +1,49 @@ +--- +execute: true +show_assembly: true +flags: "-std=c99" +--- + +## What It Does + +`inline` serves two purposes: it suggests that the compiler substitute the function body at +call sites rather than performing a function call, and it controls the linkage of functions +defined in headers. An inline function definition allows multiple translation units to share +the same function definition without causing "multiple definition" linker errors. +The compiler may ignore the inlining hint. + +## Why It Matters + +Function calls have overhead: saving registers, pushing arguments, jumping, and returning. +For small, frequently called functions, this overhead can dominate. Inlining eliminates this +overhead and enables further optimizations by exposing the function body to the calling +context. +Without `inline`, placing a function definition in a header file causes linker errors when +multiple translation units include that header. `inline` allows functions to be defined in +headers and shared across translation units while maintaining proper linkage. + +## Example + +```c +#include + +static inline int max(int a, int b) { + return a > b ? a : b; +} + +static inline int square(int x) { + return x * x; +} + +int main(void) { + int a = 10; + int b = 20; + + // Compiler may inline these calls + printf("max(%d, %d) = %d\n", a, b, max(a, b)); + printf("square(%d) = %d\n", a, square(a)); + + // Expression using inline functions + printf("max of squares: %d\n", max(square(a), square(b))); +} +``` diff --git a/content/c-line-comments.md b/content/c-line-comments.md new file mode 100644 index 0000000..dbaa754 --- /dev/null +++ b/content/c-line-comments.md @@ -0,0 +1,42 @@ +--- +execute: true +show_assembly: true +flags: "-std=c99" +--- + +## What It Does + +Single-line comments beginning with `//` extend to the end of the line. Everything from `//` +to the newline is treated as a comment and ignored by the compiler. This is in addition to +the traditional `/* */` block comment syntax. + +## Why It Matters + +Block comments (`/* */`) cannot be nested and require matching delimiters. Line comments +are self-terminating at the newline, making them suitable for quick annotations and +end-of-line explanations without the risk of unterminated comments. + +## Example + +```c +#include + +int main(void) { + int x = 10; // Initialize counter + + // Loop from 0 to x-1 + for (int i = 0; i < x; i++) { + printf("%d ", i); // Print each value + } + + printf("\n"); + + // Traditional comments still work + /* This is a + multi-line comment */ + + // int y = 20; // This line is commented out + + return 0; +} +``` diff --git a/content/c-noreturn.md b/content/c-noreturn.md new file mode 100644 index 0000000..5c6d07f --- /dev/null +++ b/content/c-noreturn.md @@ -0,0 +1,43 @@ +--- +execute: true +show_assembly: true +flags: "-std=c11" +--- + +## What It Does + +`_Noreturn` specifies that a function does not return to its caller. In C23, the `[[noreturn]]` +attribute provides an alternative syntax. The `` header provides the `noreturn` +macro. + +## Why It Matters + +Functions that terminate the program (`exit()`, `abort()`) or loop indefinitely never return. +Marking them as noreturn allows the compiler to optimize calling code, eliminating unreachable +code paths and avoiding warnings about missing return statements after such calls. + +## Example + +```c +#include +#include +#include + +noreturn void fatal_error(const char *message) { + fprintf(stderr, "Fatal error: %s\n", message); + exit(1); +} + +int divide(int a, int b) { + if (b == 0) { + fatal_error("Division by zero"); + // No return needed here; compiler knows fatal_error doesn't return + } + return a / b; +} + +int main(void) { + printf("10 / 2 = %d\n", divide(10, 2)); + // divide(1, 0); // Would call fatal_error +} +``` diff --git a/content/c-nullptr.md b/content/c-nullptr.md new file mode 100644 index 0000000..d42bbaa --- /dev/null +++ b/content/c-nullptr.md @@ -0,0 +1,36 @@ +--- +execute: true +show_assembly: true +--- +## What It Does + +`nullptr` is a null pointer constant with type `nullptr_t`. It implicitly converts to any +pointer type but not to integer types. The type `nullptr_t` is defined in ``. + +## Why It Matters + +The macro `NULL` is defined as an integer constant (typically `0` or `(void*)0`), which +can match integer parameters in overloaded or generic contexts. `nullptr` has a distinct +type that converts only to pointers, eliminating ambiguity in type-generic code and +improving type safety. + +## Example + +```c +#include +#include + +void process(int *ptr) { + if (ptr == nullptr) { + printf("Received null pointer\n"); + } else { + printf("Value: %d\n", *ptr); + } +} + +int main(void) { + int x = 42; + process(&x); + process(nullptr); +} +``` diff --git a/content/c-quick-exit.md b/content/c-quick-exit.md new file mode 100644 index 0000000..7cd2a98 --- /dev/null +++ b/content/c-quick-exit.md @@ -0,0 +1,43 @@ +--- +execute: true +show_assembly: true +flags: "-std=c11" +--- + +## What It Does + +`quick_exit()` terminates the program, calling functions registered with `at_quick_exit()` +but not `atexit()` handlers or signal handlers. It provides a faster termination path than +`exit()` when cleanup registered with `atexit()` is unnecessary or harmful. + +## Why It Matters + +`exit()` runs all `atexit()` handlers, which may be undesirable when terminating from a +signal handler or when rapid shutdown is needed. `quick_exit()` provides controlled +termination that skips potentially problematic cleanup while still running handlers +specifically registered for quick termination. + +## Example + +```c +#include +#include + +void normal_cleanup(void) { + printf("Normal cleanup (atexit)\n"); +} + +void quick_cleanup(void) { + printf("Quick cleanup (at_quick_exit)\n"); +} + +int main(void) { + atexit(normal_cleanup); + at_quick_exit(quick_cleanup); + + printf("Program running...\n"); + + // quick_exit(0); // Only calls quick_cleanup + exit(0); // Only calls normal_cleanup +} +``` diff --git a/content/c-restrict.md b/content/c-restrict.md new file mode 100644 index 0000000..7565cdd --- /dev/null +++ b/content/c-restrict.md @@ -0,0 +1,44 @@ +--- +execute: true +show_assembly: true +flags: "-std=c99" +--- + +## What It Does + +`restrict` is a type qualifier for pointers that asserts the pointed-to object is accessed +only through that pointer (or pointers derived from it) within its scope. This promise enables +the compiler to perform optimizations that assume no aliasing. + +## Why It Matters + +When pointers might alias (point to the same memory), the compiler must reload values after +writes through other pointers. The `restrict` qualifier tells the compiler that no aliasing +occurs, enabling optimizations such as keeping values in registers and reordering memory accesses. + +## Example + +```c +#include + +// Without restrict, compiler assumes a, b, c might overlap +void add_arrays_slow(int* a, int* b, int* c, int n) { + for (int i = 0; i < n; i++) + a[i] = b[i] + c[i]; +} + +// With restrict, compiler assumes no overlap exists +void add_arrays_fast(int* restrict a, int* restrict b, int* restrict c, int n) { + for (int i = 0; i < n; i++) + a[i] = b[i] + c[i]; +} + +int main(void) { + int x[] = {1, 2, 3}; + int y[] = {4, 5, 6}; + int z[3]; + + add_arrays_fast(z, x, y, 3); + printf("Result: %d %d %d\n", z[0], z[1], z[2]); +} +``` diff --git a/content/c-static-assert.md b/content/c-static-assert.md new file mode 100644 index 0000000..d7f5bd2 --- /dev/null +++ b/content/c-static-assert.md @@ -0,0 +1,38 @@ +--- +execute: true +show_assembly: true +flags: "-std=c11" +--- + +## What It Does + +`_Static_assert(expression, message)` verifies a condition at compile time. If the expression +evaluates to false, compilation fails with the specified error message. In C23, the message +is optional. The macro `static_assert` from `` provides an alternative spelling. + +## Why It Matters + +Runtime assertions detect errors only when the assertion executes. Compile-time assertions +catch errors during compilation, before the program runs. This is essential for verifying +type sizes, structure layouts, and other compile-time properties that affect correctness. + +## Example + +```c +#include +#include +#include + +struct Packet { + uint32_t header; + uint8_t data[12]; +}; + +static_assert(sizeof(struct Packet) == 16, "Packet must be 16 bytes"); +static_assert(sizeof(int) >= 4, "int must be at least 32 bits"); + +int main(void) { + printf("All static assertions passed\n"); + printf("sizeof(Packet) = %zu\n", sizeof(struct Packet)); +} +``` diff --git a/content/c-thread-local.md b/content/c-thread-local.md new file mode 100644 index 0000000..a317301 --- /dev/null +++ b/content/c-thread-local.md @@ -0,0 +1,46 @@ +--- +execute: true +show_assembly: true +flags: "-std=c11" +--- + +## What It Does + +`_Thread_local` specifies that a variable has thread storage duration. Each thread has its +own instance of the variable, initialized independently. The `` header provides +the alternative spelling `thread_local`. + +## Why It Matters + +Global variables are shared across threads, requiring synchronization for safe access. +Thread-local variables provide per-thread storage without synchronization overhead, useful +for thread-specific state like error codes, random number generators, or caches. + +## Example + +```c +#include +#include + +_Thread_local int thread_id = 0; +_Thread_local char thread_name[32] = ""; + +int worker(void *arg) { + thread_id = *(int*)arg; + snprintf(thread_name, sizeof(thread_name), "Worker-%d", thread_id); + + printf("%s: thread_id = %d\n", thread_name, thread_id); + return 0; +} + +int main(void) { + thrd_t t1, t2; + int id1 = 1, id2 = 2; + + thrd_create(&t1, worker, &id1); + thrd_create(&t2, worker, &id2); + + thrd_join(t1, NULL); + thrd_join(t2, NULL); +} +``` diff --git a/content/c-threads.md b/content/c-threads.md new file mode 100644 index 0000000..d280f49 --- /dev/null +++ b/content/c-threads.md @@ -0,0 +1,57 @@ +--- +execute: true +show_assembly: true +flags: "-std=c11" +--- + +## What It Does + +`` provides thread management (`thrd_create()`, `thrd_join()`), mutexes (`mtx_init()`, +`mtx_lock()`, `mtx_unlock()`), condition variables (`cnd_wait()`, `cnd_signal()`), and thread-specific +storage (`tss_create()`, `tss_get()`, `tss_set()`). This is the standard C threading API. + +## Why It Matters + +Before C11, threading required platform-specific APIs (pthreads on Unix, Win32 threads on +Windows) or third-party libraries. `` provides a portable threading interface, +enabling concurrent programs without platform dependencies. + +## Example + +```c +#include +#include + +mtx_t mutex; +int shared_counter = 0; + +int increment(void* arg) { + const int count = *(int*)arg; + + for (int i = 0; i < count; i++) { + mtx_lock(&mutex); + shared_counter++; + mtx_unlock(&mutex); + } + + return 0; +} + +int main(void) { + thrd_t t1; + thrd_t t2; + int count = 10000; + + mtx_init(&mutex, mtx_plain); + + thrd_create(&t1, increment, &count); + thrd_create(&t2, increment, &count); + + thrd_join(t1, NULL); + thrd_join(t2, NULL); + + printf("Counter: %d\n", shared_counter); + + mtx_destroy(&mutex); +} +``` diff --git a/content/c-typeof.md b/content/c-typeof.md new file mode 100644 index 0000000..1c51a8e --- /dev/null +++ b/content/c-typeof.md @@ -0,0 +1,35 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +`typeof` and `typeof_unqual` are operators that yield the type of an expression or type name. +`typeof` preserves all qualifiers (const, volatile, restrict), while `typeof_unqual` removes +the top-level cv-qualifiers. Both can be used wherever a type name is expected. + +## Why It Matters + +Declaring variables with the same type as another expression previously required manual type +specification or compiler-specific extensions like `__typeof__`. The standard `typeof` operator +provides a portable way to derive types from expressions, and `typeof_unqual` addresses the +common need to obtain the unqualified version of a type. + +## Example + +```c +#include + +int main(void) { + const int x = 42; + + typeof(x) y = 10; // y has type const int + typeof_unqual(x) z = 20; // z has type int (no const) + + // z = 30; // OK: z is not const + // y = 30; // Error: y is const + + printf("y = %d, z = %d\n", y, z); +} +``` diff --git a/content/c-unreachable.md b/content/c-unreachable.md new file mode 100644 index 0000000..e896a55 --- /dev/null +++ b/content/c-unreachable.md @@ -0,0 +1,40 @@ +--- +execute: true +show_assembly: true +--- +## What It Does + +`[[unsequenced]]` indicates that a function has no side effects and its return value depends +only on its arguments, not on any state. `[[reproducible]]` indicates that consecutive calls +with the same arguments in the same program state return the same value. These attributes +enable compiler optimizations. + +## Why It Matters + +Compilers cannot always determine whether a function has side effects or depends on external +state. These attributes communicate function properties to the compiler, enabling optimizations +like eliminating redundant calls or reordering operations that would otherwise require +conservative assumptions. + +## Example + +```c +#include + +[[unsequenced]] int square(int x) { + return x * x; +} + +[[reproducible]] int get_cached_value(int key) { + static int cache[10] = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}; + return cache[key % 10]; +} + +int main(void) { + int a = square(5); + int b = square(5); // Compiler may optimize to reuse 'a' + + printf("a = %d, b = %d\n", a, b); + printf("cached: %d\n", get_cached_value(3)); +} +``` diff --git a/content/c-va-opt.md b/content/c-va-opt.md new file mode 100644 index 0000000..2129441 --- /dev/null +++ b/content/c-va-opt.md @@ -0,0 +1,29 @@ +--- +execute: true +show_assembly: true +--- + +## What It Does + +`__VA_OPT__(content)` is a function-like macro that expands to its argument if `__VA_ARGS__` +is non-empty, and expands to nothing if `__VA_ARGS__` is empty. It appears only within the +replacement list of variadic macros. + +## Why It Matters + +Variadic macros that need to handle both zero and non-zero arguments required compiler-specific +extensions or awkward workarounds to handle the trailing comma problem. `__VA_OPT__` provides +a standard way to conditionally include content based on whether variadic arguments are present. + +## Example + +```c +#include + +#define LOG(fmt, ...) printf("[LOG] " fmt __VA_OPT__(,) __VA_ARGS__) + +int main(void) { + LOG("Starting program\n"); // No extra args + LOG("Value: %d\n", 42); // With args +} +``` diff --git a/content/c-variadic-macros.md b/content/c-variadic-macros.md new file mode 100644 index 0000000..9a6514a --- /dev/null +++ b/content/c-variadic-macros.md @@ -0,0 +1,42 @@ +--- +execute: true +show_assembly: true +flags: "-std=c99" +--- + +## What It Does + +Variadic macros accept a variable number of arguments using `...` in the parameter list. +The arguments are accessed through `__VA_ARGS__` in the replacement text. This enables +macros that wrap functions like `printf()` with additional behavior. + +## Why It Matters + +Fixed-argument macros cannot wrap variadic functions like `printf()` or forward arbitrary +argument lists. Variadic macros enable creating logging wrappers, assertion macros with +formatted messages, and other utilities that accept any number of arguments. + +## Example + +```c +#include + +#define LOG(fmt, ...) printf("[LOG] " fmt "\n", __VA_ARGS__) + +#define DEBUG(fmt, ...) fprintf(stderr, "[DEBUG %s:%d] " fmt "\n", \ + __FILE__, __LINE__, __VA_ARGS__) + +#define PRINT(...) printf(__VA_ARGS__) + +#define ERROR(code, fmt, ...) do { \ + fprintf(stderr, "Error %d: " fmt "\n", code, __VA_ARGS__); \ +} while (0) + +int main(void) { + int x = 42; + LOG("Value is %d", x); + DEBUG("Processing item %d of %d", 5, 10); + PRINT("Hello, %s!\n", "World"); + ERROR(404, "Resource '%s' not found", "/data"); +} +``` diff --git a/content/c-vla.md b/content/c-vla.md new file mode 100644 index 0000000..9640cf2 --- /dev/null +++ b/content/c-vla.md @@ -0,0 +1,48 @@ +--- +execute: true +show_assembly: true +flags: "-std=c99" +--- + +## What It Does + +Variable-length arrays (VLAs) have a size determined at runtime rather than compile time. +The array is allocated on the stack with automatic storage duration. VLAs can be declared +in block scope with sizes computed from runtime expressions. + +## Why It Matters + +Fixed-size arrays require compile-time constants, often leading to oversized allocations +or dynamic allocation with `malloc()`. VLAs provide stack-allocated arrays with sizes known +only at runtime, useful for algorithms where the size depends on function parameters. + +## Example + +```c +#include + +void print_matrix(int rows, int cols, int matrix[rows][cols]) { + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + printf("%3d ", matrix[i][j]); + } + printf("\n"); + } +} + +int main(void) { + int n = 3; + int arr[n]; // VLA with runtime size + + for (int i = 0; i < n; i++) + arr[i] = i * 10; + + printf("VLA contents: "); + for (int i = 0; i < n; i++) + printf("%d ", arr[i]); + printf("\n"); + + int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}}; + print_matrix(2, 3, matrix); +} +``` diff --git a/features_c11.yaml b/features_c11.yaml index 67f62f2..aa2186f 100644 --- a/features_c11.yaml +++ b/features_c11.yaml @@ -2,6 +2,7 @@ features: - desc: "Static assertions (`_Static_assert`)" paper: N1330 + content: c-static-assert.md support: - GCC 4.7.1 - Clang 3.1 @@ -10,6 +11,7 @@ features: - TinyCC - desc: "Generic selection (`_Generic`)" paper: N1441 + content: c-generic.md support: - GCC 4.9 - Clang 3.1 @@ -18,6 +20,7 @@ features: - TinyCC - desc: "Anonymous structures and unions" paper: N1444 + content: c-anonymous-struct-union.md support: - GCC 4.7.1 - Clang 3.1 @@ -25,6 +28,7 @@ features: - Xcode 16.1 - desc: "Alignment support (`_Alignas`, `_Alignof`, ``)" paper: N1353 + content: c-alignas-alignof.md support: - GCC 4.7.1 - Clang 3.3 @@ -32,6 +36,7 @@ features: - Xcode 16.1 - desc: "Atomic operations (`_Atomic`, ``)" paper: N1485 + content: c-atomic.md lib: true support: - GCC 4.9 @@ -43,6 +48,7 @@ features: msg: "Requires enabling the `/experimental:c11atomics` compiler flag." - desc: "Thread-local storage (`_Thread_local`)" paper: N1287 + content: c-thread-local.md support: - GCC 4.9 - Clang 3.3 @@ -50,6 +56,7 @@ features: - Xcode 16.1 - desc: "Multi-threading support (``)" paper: N1437 + content: c-threads.md lib: true support: - GCC 4.7.1 @@ -57,6 +64,7 @@ features: - Clang 3.3 - desc: "Noreturn function specifier (`_Noreturn`, ``)" paper: N1478 + content: c-noreturn.md support: - GCC 4.7.1 - Clang 3.3 @@ -81,6 +89,7 @@ features: - Xcode 16.1 - desc: "Quick exit (`quick_exit()`, `at_quick_exit()`)" paper: N1327 + content: c-quick-exit.md lib: true support: - GCC 4.7.1 diff --git a/features_c23.yaml b/features_c23.yaml index 06d31f7..1dc2761 100644 --- a/features_c23.yaml +++ b/features_c23.yaml @@ -29,6 +29,7 @@ features: paper: - N2335 - N2554 + content: c-attributes.md support: - GCC 10 - Clang 9 @@ -77,6 +78,7 @@ features: - Xcode 16.1 - desc: "[Binary integer constants](https://en.cppreference.com/w/c/language/integer_constant.html)" paper: N2549 + content: c-binary-literals.md support: - GCC 4.3 (hint) - GCC 11 @@ -121,6 +123,7 @@ features: msg: "Only `_Float16` is supported." - desc: "Digit separators" paper: N2626 + content: c-digit-separators.md support: - GCC 12 - Clang 13 @@ -131,6 +134,7 @@ features: msg: "Supported as an extension." - desc: "[`#elifdef` and `#elifndef`](https://en.cppreference.com/w/c/preprocessor/conditional.html)" paper: N2645 + content: c-elifdef.md support: - GCC 12 - Clang 13 @@ -156,6 +160,7 @@ features: - TinyCC - desc: "Bit-precise integer types (`_BitInt`)" paper: N2763 + content: c-bitint.md support: - GCC 14 (partial) - Clang 15 @@ -197,6 +202,7 @@ features: - Xcode 16.1 - desc: "[Empty initializers](https://en.cppreference.com/w/c/language/initialization.html#Empty_initialization)" paper: N2900 + content: c-empty-init.md support: - GCC 13 (partial) - Clang (partial) @@ -212,6 +218,7 @@ features: paper: - N2927 - N2930 + content: c-typeof.md support: - GCC 13 (hint) - Clang 16 (hint) @@ -232,12 +239,14 @@ features: - Xcode 16.1 - desc: "Predefined `true` and `false`" paper: N2935 + content: c-bool-true-false.md support: - GCC 13 - Clang 15 - Xcode 16.1 - desc: "`[[unsequenced]]` and `[[reproducible]]`" paper: N2956 + content: c-unreachable.md support: - GCC 15 - desc: "Relax requirements for [variadic parameter list](https://en.cppreference.com/w/c/language/variadic.html)" @@ -246,20 +255,23 @@ features: - GCC 13 - Clang 16 - Xcode 16.1 - - desc: "Type inference in object definitions" + - desc: "Type inference in object definitions (`auto`)" paper: N3007 + content: c-auto.md support: - GCC 13 - Clang 18 - Xcode 16.4 - desc: "`#embed`" paper: N3017 + content: c-embed.md support: - GCC 15 - Clang 19 - Xcode 26 - desc: "`constexpr` objects" paper: N3018 + content: c-constexpr.md support: - GCC 13 - Clang 19 @@ -275,6 +287,7 @@ features: msg: "Supported as an extension with incomplete diagnostics in earlier versions." - desc: "Enumerations with fixed underlying types" paper: N3030 + content: c-fixed-enum.md support: - GCC 13 - Clang 20 (hint) @@ -284,6 +297,7 @@ features: msg: "Supported as an extension with incomplete diagnostics in earlier versions." - desc: "`__VA_OPT__`" paper: N3033 + content: c-va-opt.md support: - GCC 8 - GCC 13 @@ -296,6 +310,7 @@ features: - GCC 13 - desc: "`nullptr`" paper: N3042 + content: c-nullptr.md support: - GCC 13 - Clang 16 (partial) diff --git a/features_c99.yaml b/features_c99.yaml index 633f8bf..eb3c0fd 100644 --- a/features_c99.yaml +++ b/features_c99.yaml @@ -16,6 +16,7 @@ features: - TinyCC - desc: "`//` comments" paper: N644 + content: c-line-comments.md support: - GCC - Clang @@ -25,6 +26,7 @@ features: - TinyCC - desc: "`restrict` pointers" paper: N448 + content: c-restrict.md support: - GCC - Clang @@ -49,6 +51,7 @@ features: - Clang (partial) - Xcode (partial) - desc: "Flexible array members" + content: c-flexible-array.md support: - GCC 3 - Clang @@ -57,6 +60,7 @@ features: - TinyCC - desc: "[Variable-length array](https://en.cppreference.com/w/c/language/array.html#Variable-length_arrays) (VLA) types" paper: N683 + content: c-vla.md support: - GCC - Clang @@ -69,6 +73,7 @@ features: - Xcode - desc: "Designated initializers" paper: N494 + content: c-designated-init.md support: - GCC 3 - Clang @@ -93,6 +98,7 @@ features: - TinyCC - desc: "Hexadecimal [floating constants](https://en.cppreference.com/w/c/language/floating_constant.html)" paper: N308 + content: c-hex-float.md support: - GCC 2.8 - Clang @@ -102,6 +108,7 @@ features: - TinyCC - desc: "[Compound literals](https://en.cppreference.com/w/c/language/compound_literal.html)" paper: N716 + content: c-compound-literals.md support: - GCC 3.1 - Clang @@ -145,6 +152,7 @@ features: - TinyCC - desc: "`inline` functions" paper: N741 + content: c-inline.md support: - GCC 4.3 - Clang @@ -154,6 +162,7 @@ features: - TinyCC - desc: "Predefined variable `__func__`" paper: N611 + content: c-func.md support: - GCC 3 - Clang @@ -168,6 +177,7 @@ features: - Xcode - desc: "[Variadic macros](https://en.cppreference.com/w/c/preprocessor/replace.html)" paper: N707 + content: c-variadic-macros.md support: - GCC 3 - Clang