SimplifyC++ Article
Inside #include <stddef.h> in C17 and C23
Inside #include <stddef.h> in C17 and C23
What it really contains, why it exists, and why systems programmers treat it as “bedrock”
<stddef.h> is not a “utility header”. It’s one of the core vocabulary headers of C: it standardizes the small set of types and macros that make low-level, portable C code possible—sizes, pointer differences, alignment, null pointers, and member offsets.
If you write allocators, containers, parsers, ABIs/FFI layers, kernels, runtimes, or compiler-adjacent code, you’re using <stddef.h>—directly or indirectly.
1) What <stddef.h> contains in C17 (baseline)
1.1 size_t — “size and length” type
An unsigned integer type used to represent sizes and counts in memory.
It’s the type of
sizeofresults and is used pervasively in APIs that deal with memory and buffers.
Why it matters
On 64-bit systems,
size_tis typically 64-bit; usingunsigned intfor lengths can break for large objects or large indexing.
Typical usage
void* my_memcpy(void* dst, const void* src, size_t n);Common pitfall
size_tis unsigned: subtracting can underflow.
size_t a = 2, b = 10;size_t diff = a - b; // huge value, not -81.2 ptrdiff_t — “distance between pointers” type
A signed integer type for pointer subtraction results (difference between two pointers to elements in the same array object).
Why it matters
When you compute
end - begin, you want a signed type that matches the platform’s address model.
ptrdiff_t span(const char* begin, const char* end) { return end - begin;}1.3 NULL — the traditional null pointer macro
NULLis an implementation-defined null pointer constant.Historically it can be
0,0L, or(void*)0depending on the platform and headers.
Why systems programmers care
In C,
NULLbeing “an integer 0” is legal, but it can cause subtle issues in:varargs (
...) calls_GenericdispatchAPIs where you want “this is definitely a pointer-null” rather than “integer zero that converts” (And this is exactly part of what C23 improves.)
1.4 offsetof(type, member) — member offset in bytes
Produces the byte offset of a struct member within its enclosing type.
Extremely useful for intrusive data structures, serialization boundaries (when used carefully), and low-level layout work.
struct Node { int x; double y; };
size_t off_y(void) { return offsetof(struct Node, y);}Important caution
offsetofis only well-defined for valid member designators in standard-layout struct types; using it on bit-fields or doing “clever” UB tricks is a classic pitfall.
1.5 wchar_t — wide character type (C header behavior)
In practice, C’s <stddef.h> is one of the places where wchar_t is declared on many implementations; and we have a strong standards hint for this: C++’s <cstddef> explicitly says it is the same as C’s <stddef.h> except that it does *not* declare wchar_t, which implies C’s <stddef.h> does.
(So, from a portability standpoint: treat wchar_t as a “common definition” type that may appear via <stddef.h> in C ecosystems, but always include the correct headers for your usage style.)
1.6 max_align_t — “alignment strong enough for any scalar” (present in C17)
Added in C11, so it is part of C17.
A type whose alignment is at least as strict as any scalar type on the platform.
Why it matters
If you build an allocator or arena, you often want to guarantee “this buffer is aligned enough for anything”.
2) What changes in C23 inside <stddef.h>
C23 modernizes null pointers by adding:
2.1 nullptr and nullptr_t
C23 introduces a predefined null pointer constant
nullptrand its typenullptr_tvia<stddef.h>.
This addresses long-standing problems where NULL might be an integer constant and not a dedicated null pointer entity.
C23 usage
void f(void* p);
int main(void) { f(nullptr); // explicitly a null pointer constant nullptr_t x = nullptr; // typed null}2.2 NULL may now be defined in terms of nullptr
Cppreference notes that since C23, NULL can be defined as the predefined constant nullptr.
That means C23 gives implementations a cleaner way to make NULL “actually pointer-null” instead of “integer zero”.
3) Why <stddef.h> matters for ABI and low-level correctness
Even though <stddef.h> looks small, it defines the vocabulary used in binary contracts:
size_tleaks into public APIs (buffer sizes, lengths). Its width depends on the platform data model (32 vs 64).ptrdiff_treflects pointer arithmetic semantics and address space realities.max_align_tshapes allocator guarantees and correct alignment in low-level storage.C23’s
nullptr/nullptr_timproves correctness and clarity at boundaries (especially with varargs and generic selection).
If you’re designing a stable ABI, you don’t “casually” use these—you document what they mean on your target ABIs.
4) A practical “systems-grade” mental model
Think of <stddef.h> as the header that standardizes:
How big things can be (
size_t)How far pointers are apart (
ptrdiff_t)How to describe null (C17:
NULL, C23:nullptr+nullptr_t)How to reason about layout (
offsetof)How to guarantee alignment (
max_align_t)How wide characters are represented (
wchar_tin C header practice)
This is why you find it at the heart of allocators, container libraries, kernels, runtimes, and FFI design.