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
33 changes: 33 additions & 0 deletions content/c-alignas-alignof.md
Original file line number Diff line number Diff line change
@@ -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 `<stdalign.h>` 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 <stdio.h>
#include <stdalign.h>

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);
}
```
49 changes: 49 additions & 0 deletions content/c-anonymous-struct-union.md
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>

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);
}
```
41 changes: 41 additions & 0 deletions content/c-atomic.md
Original file line number Diff line number Diff line change
@@ -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 `<stdatomic.h>`
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 <stdio.h>
#include <stdatomic.h>

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));
}
}
```
44 changes: 44 additions & 0 deletions content/c-attributes.md
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>
#include <stdlib.h>

[[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);
}
```
34 changes: 34 additions & 0 deletions content/c-auto.md
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>

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);
}
```
40 changes: 40 additions & 0 deletions content/c-binary-literals.md
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>

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");
}
```
34 changes: 34 additions & 0 deletions content/c-bitint.md
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>

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");
}
```
35 changes: 35 additions & 0 deletions content/c-bool-true-false.md
Original file line number Diff line number Diff line change
@@ -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 `<stdbool.h>` is no longer necessary.

## Why It Matters

Prior to C23, using boolean types required including `<stdbool.h>`, 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 <stdio.h>

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));
}
```
53 changes: 53 additions & 0 deletions content/c-compound-literals.md
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>

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]);
}
```
Loading