Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions content/c-alignof-incomplete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## What It Does

The `alignof` operator can now be applied to incomplete array types.
The alignment of an incomplete array type is the same as the alignment of its element type.

## Why It Matters

Previously, `alignof` required a complete type, which prevented querying the alignment
of array types with unknown bounds at the point of use.
Since array alignment depends only on the element type, this restriction was unnecessary.

## Example

```c
#include <stdio.h>
#include <stdalign.h>

extern int incomplete_array[];

int main(void) {
// Alignment of incomplete array equals element alignment
printf("alignof(int[]) = %zu\n", alignof(int[]));
printf("alignof(int) = %zu\n", alignof(int));

printf("alignof(double[]) = %zu\n", alignof(double[]));
printf("alignof(double) = %zu\n", alignof(double));

// Works with extern declarations
printf("alignof(incomplete_array) = %zu\n", alignof(incomplete_array));
}
```
41 changes: 41 additions & 0 deletions content/c-case-range.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## What It Does

Case range expressions allow a `case` label to match a contiguous range of integer values
using the syntax `case low ... high:`.
Both endpoints are inclusive, and the range covers all integer values from `low` to `high`.

## Why It Matters

Matching multiple consecutive values in a `switch` previously required listing each value
as a separate `case` label.
Case ranges express the intent directly and reduce repetition when handling contiguous value sets.

## Example

```c
#include <stdio.h>

const char *classify_char(char c) {
switch (c) {
case 'a' ... 'z':
return "lowercase letter";
case 'A' ... 'Z':
return "uppercase letter";
case '0' ... '9':
return "digit";
case ' ':
case '\t':
case '\n':
return "whitespace";
default:
return "other";
}
}

int main(void) {
printf("'g' is a %s\n", classify_char('g'));
printf("'M' is a %s\n", classify_char('M'));
printf("'7' is a %s\n", classify_char('7'));
printf("'@' is a %s\n", classify_char('@'));
}
```
35 changes: 35 additions & 0 deletions content/c-complex-inc-dec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## What It Does

The `++` and `--` operators can now be applied to complex number values.
Incrementing a complex number adds 1 to its real part, and decrementing subtracts 1 from its real part.
The imaginary part remains unchanged.

## Why It Matters

Increment and decrement operators were not defined for complex types in previous C standards.
This addition provides consistency with arithmetic operations on complex numbers, where adding
an integer affects only the real component.

## Example

```c
#include <stdio.h>
#include <complex.h>

int main(void) {
double complex z = 3.0 + 4.0i;

printf("Initial: %.1f + %.1fi\n", creal(z), cimag(z));

++z; // Adds 1 to real part
printf("After ++z: %.1f + %.1fi\n", creal(z), cimag(z));

z--; // Subtracts 1 from real part
printf("After z--: %.1f + %.1fi\n", creal(z), cimag(z));

// Works in expressions
double complex w = 1.0 + 2.0i;
double complex result = ++w + z;
printf("Result: %.1f + %.1fi\n", creal(result), cimag(result));
}
```
35 changes: 35 additions & 0 deletions content/c-complex-literals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## What It Does

Complex literals allow imaginary numbers to be written directly using the
`i`, `I`, `j`, or `J` suffix on floating-point constants.
The suffix indicates an imaginary value with a real part of zero.
Combined with real constants via addition, this forms complete complex numbers.

## Why It Matters

Constructing complex numbers previously required the `CMPLX` macro or arithmetic operations.
Complex literals enable direct notation that matches mathematical convention and can appear in
contexts requiring constant expressions, such as static initializers.

## Example

```c
#include <stdio.h>
#include <complex.h>

int main(void) {
// Imaginary literals
double complex z1 = 3.14i; // 0 + 3.14i
float complex z2 = 2.0fi; // 0 + 2.0i (float)

// Full complex numbers via addition
double complex z3 = 2.0 + 3.0i; // 2 + 3i
float complex z4 = 1.5f + 0.5fi; // 1.5 + 0.5i (float)

printf("z1 = %.2f + %.2fi\n", creal(z1), cimag(z1));
printf("z3 = %.2f + %.2fi\n", creal(z3), cimag(z3));

// Constant expressions allowed
static double complex table[] = {1.0 + 2.0i, 3.0 + 4.0i};
}
```
35 changes: 35 additions & 0 deletions content/c-const-constexpr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## What It Does

Const integer declarations with constant expression initializers are implicitly treated
as `constexpr`. A `const`-qualified integer variable initialized with a constant expression
in the same declaration becomes a compile-time constant, usable in contexts requiring
constant expressions such as array bounds and `_Static_assert`.

## Why It Matters

In C23, `const int n = 10; int arr[n];` created a variable-length array because `n` was
not a constant expression despite its constant initializer.
This conflicted with decades of existing practice where compilers treated such declarations
as compile-time constants. The change aligns the standard with existing implementations.

## Example

```c
#include <stdio.h>

int main(void) {
const int size = 10; // Implicitly constexpr
int array[size]; // Fixed-size array, not VLA

const int doubled = size * 2; // Also constexpr
_Static_assert(doubled == 20, "should be 20");

// Chained constants work
const int a = 1;
const int b = a + 1;
const int c = b + 1;
_Static_assert(c == 3, "should be 3");

printf("Array has %d elements\n", size);
}
```
34 changes: 34 additions & 0 deletions content/c-counter-macro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## What It Does

The `__COUNTER__` predefined macro expands to an integer literal that starts at 0
and increments by 1 each time it is expanded within a translation unit.
Each expansion produces a unique value, enabling generation of distinct identifiers or values at compile time.

## Why It Matters

Generating unique identifiers in macros previously required manual numbering or reliance on
`__LINE__`, which fails when multiple expansions occur on the same line.
`__COUNTER__` provides a reliable mechanism for creating unique names in macro-generated code.

## Example

```c
#include <stdio.h>

#define CONCAT_IMPL(a, b) a ## b
#define CONCAT(a, b) CONCAT_IMPL(a, b)
#define UNIQUE_NAME(prefix) CONCAT(prefix, __COUNTER__)

#define MAKE_TEMP(value) \
int UNIQUE_NAME(temp_) = (value)

int main(void) {
MAKE_TEMP(10); // Creates temp_0
MAKE_TEMP(20); // Creates temp_1
MAKE_TEMP(30); // Creates temp_2

printf("temp_0 = %d\n", temp_0);
printf("temp_1 = %d\n", temp_1);
printf("temp_2 = %d\n", temp_2);
}
```
33 changes: 33 additions & 0 deletions content/c-countof.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
## What It Does

The `_Countof` operator returns the number of elements in an array.
It takes either an array expression or an array type as its operand and evaluates to a constant
of type `size_t` representing the array's element count.

## Why It Matters

Computing array length previously required the idiom `sizeof(arr) / sizeof(arr[0])`, which
produces incorrect results when applied to a pointer instead of an array.
`_Countof` only accepts array types, providing a compile-time error when misused with pointers,
and directly expresses the intent of obtaining element count.

## Example

```c
#include <stdio.h>

int main(void) {
int numbers[10];
char message[] = "Hello";

printf("numbers has %zu elements\n", _Countof(numbers)); // 10
printf("message has %zu elements\n", _Countof(message)); // 6 (includes '\0')

// Works with typedefs
typedef double vec3[3];
printf("vec3 has %zu elements\n", _Countof(vec3)); // 3

// Compile-time constant
_Static_assert(_Countof(numbers) == 10, "expected 10 elements");
}
```
39 changes: 39 additions & 0 deletions content/c-generic-type-operand.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## What It Does

Generic selection with a type operand extends `_Generic` to accept a type name directly,
in addition to the existing expression form.
The syntax `_Generic(type-name, ...)` selects an association based on the provided type
without requiring a value of that type.

## Why It Matters

The original `_Generic` required an expression operand, forcing macros to manufacture a
value (often via a cast or compound literal) when only type-based dispatch was needed.
Type operands enable `_Generic` selection based purely on type information, supporting
metaprogramming patterns that operate on types rather than values.

## Example

```c
#include <stdio.h>

#define type_name(T) _Generic(T, \
int: "int", \
unsigned int: "unsigned int", \
float: "float", \
double: "double", \
default: "unknown")

#define alignment_of(T) _Generic(T, \
char: 1, \
short: _Alignof(short), \
int: _Alignof(int), \
double: _Alignof(double), \
default: 0)

int main(void) {
printf("Type name: %s\n", type_name(int));
printf("Type name: %s\n", type_name(double));
printf("Alignment of int: %d\n", alignment_of(int));
}
```
38 changes: 38 additions & 0 deletions content/c-if-declarations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## What It Does

If declarations allow a variable declaration with initialization in the condition of an `if` statement.
The declared variable's scope extends through the entire `if`/`else` block,
and its initialized value serves as the controlling expression.

## Why It Matters

Declaring a variable and testing it previously required either a separate declaration before the
`if` or an additional scope block.
If declarations reduce variable scope to where it is actually needed and eliminate the pattern of
declaring, initializing, and testing in three separate steps.

## Example

```c
#include <stdio.h>
#include <stdlib.h>

int get_value(void) {
return 42;
}

int main(void) {
// Variable 'x' exists only within the if/else block
if (int x = get_value()) {
printf("Got non-zero value: %d\n", x);
} else {
printf("Got zero\n");
}
// 'x' is not accessible here

// Works with pointers too
if (char *env = getenv("HOME")) {
printf("HOME = %s\n", env);
}
}
```
41 changes: 41 additions & 0 deletions content/c-named-loops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## What It Does

Named loops allow `break` and `continue` statements to target a specific enclosing loop or `switch` by name.
A label placed before an iteration statement or `switch` becomes a target that can be referenced by
`break label;` or `continue label;` to exit or continue that specific statement, even from deeply nested code.

## Why It Matters

Breaking out of multiple nested loops previously required a `goto` statement or additional flag variables.
Named loops provide a structured way to exit or continue outer loops directly, matching functionality
available in Java, JavaScript, and Rust.

## Example

```c
#include <stdio.h>
#include <stdbool.h>

int main(void) {
int matrix[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int target = 5;
bool found = false;
int row = -1, col = -1;

search:
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (matrix[i][j] == target) {
row = i;
col = j;
found = true;
break search; // exits both loops
}
}
}

if (found) {
printf("Found %d at [%d][%d]\n", target, row, col);
}
}
```
Loading