C23 Standard Features
Explore the newest C standard! Learn about typeof, constexpr, nullptr, _BitInt, bool/true/false as keywords, [[attributes]], and other C23 improvements.
Track Your Progress
Sign in to save your learning progress
What You Will Learn
- ✓Understand what C23 introduced
- ✓Use typeof for type inference
- ✓Use constexpr for compile-time constants
- ✓Use nullptr instead of NULL
- ✓Apply C23 attributes like [[nodiscard]]
?Why Learn C23?
C23 (ISO/IEC 9899:2024) is the latest C standard, bringing modern language features that make C safer and more expressive. GCC 13+ and Clang 16+ support many C23 features!
typeof
Type inference
constexpr
Compile-time consts
nullptr
Null pointer
[[attributes]]
Standardized attrs
Compile with: gcc -std=c23 or clang -std=c23
01What's New in C23?
🆕 C23 vs Previous Standards
| Feature | C99 | C11 | C23 |
|---|---|---|---|
| Type inference | ✗ | ✗ | typeof |
| Null pointer | NULL | NULL | nullptr |
| Boolean type | _Bool | _Bool | bool |
| Compile-time const | ✗ | ✗ | constexpr |
| Attributes | ✗ | ✗ | [[...]] |
C23 Feature Categories
Type System
typeof, typeof_unqual
nullptr, bool keyword
Constants
constexpr objects
true/false keywords
Attributes
[[nodiscard]]
[[deprecated]], [[fallthrough]]
Compiler Support
C23 is new! Check compiler support: GCC 13+ and Clang 16+ support most features. Use -std=c23 or -std=c2x for older versions.
02typeof: Type Inference
The Problem typeof Solves
In C, when writing generic macros, you often need to create temporary variables but don't know the type. Previously, you had to use GCC extensions. Now typeof is standard!
Use cases: Generic macros, type-safe containers, swap functions that work with any type, and avoiding repetition when the type is complex (like function pointers).
typeof returns the type of an expression. Use it to declare variables matching another variable's type without knowing it explicitly!
1#include <stdio.h>23int main() {4 int x = 42;5 float y = 3.14f;6 7 // typeof copies the type8 typeof(x) a = 100; // a is int9 typeof(y) b = 2.71f; // b is float10 11 // Works with expressions12 typeof(x + y) result = x + y; // result is float13 14 // typeof_unqual removes const/volatile15 const int c = 10;16 typeof_unqual(c) d = 20; // d is int (not const int)17 d = 30; // OK! d is mutable18 19 printf("a = %d (int)\n", a);20 printf("b = %f (float)\n", b);21 printf("result = %f (float)\n", result);22 printf("d = %d (mutable int)\n", d);23 24 return 0;25}Macro Power
typeof is invaluable in macros — you can create type-safe generic macros:
#define max(a, b) ({typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b;})02constexpr: Compile-Time Constants
constexpr defines values that are computed at compile time. Unlike const, the compiler guarantees the value is a true constant!
1#include <stdio.h>23// Compile-time constant4constexpr int ARRAY_SIZE = 100;5constexpr double PI = 3.14159265358979;67// Can use in array declarations!8int data[ARRAY_SIZE];910// Compile-time computed values11constexpr int DOUBLED = ARRAY_SIZE * 2;12constexpr int KB = 1024;13constexpr int MB = KB * KB;1415int main() {16 printf("Array size: %d\n", ARRAY_SIZE);17 printf("Doubled: %d\n", DOUBLED);18 printf("MB: %d\n", MB);19 20 // constexpr can be used in switch cases21 int choice = ARRAY_SIZE;22 switch (choice) {23 case ARRAY_SIZE:24 printf("Matched ARRAY_SIZE\n");25 break;26 }27 28 return 0;29}const
const int SIZE = 100;
Can't use in array size (before C99 VLA). Runtime value.
✓constexpr
constexpr int SIZE = 100;
Guaranteed compile-time. Works in array size and switch.
03nullptr: The New Null Pointer
nullptr is a proper null pointer constant with its own type (nullptr_t). It's safer than NULL which is just 0 or (void*)0.
1#include <stdio.h>2#include <stddef.h> // For nullptr_t34void process_int(int x) {5 printf("Got int: %d\n", x);6}78void process_ptr(int* p) {9 if (p == nullptr) {10 printf("Got null pointer\n");11 } else {12 printf("Got pointer to: %d\n", *p);13 }14}1516int main() {17 int* ptr = nullptr; // Use nullptr instead of NULL18 19 // Check with nullptr20 if (ptr == nullptr) {21 printf("ptr is null\n");22 }23 24 // nullptr has its own type25 nullptr_t null_val = nullptr;26 27 // Works with any pointer type28 char* str = nullptr;29 void* generic = nullptr;30 int (*func_ptr)(void) = nullptr;31 32 // Safer function calls33 process_ptr(nullptr); // Clear intent: passing null pointer34 35 return 0;36}Why Not NULL?
NULL is often defined as 0, which is an integer. This can cause ambiguity in function overloading (C++) or type-generic code. nullptr is always a pointer.
04bool, true, false as Keywords
In C23, bool, true, and false are built-in keywords. No need to include <stdbool.h> anymore!
1#include <stdio.h>2// No #include <stdbool.h> needed in C23!34bool is_even(int n) {5 return n % 2 == 0;6}78int main() {9 bool flag = true; // Built-in keyword10 bool done = false; // Built-in keyword11 12 printf("flag = %d\n", flag); // 113 printf("done = %d\n", done); // 014 15 for (int i = 0; i < 5; i++) {16 if (is_even(i)) {17 printf("%d is even\n", i);18 }19 }20 21 // Boolean expressions22 bool result = (5 > 3) && (10 != 0);23 printf("result = %d\n", result); // 124 25 return 0;26}05_BitInt: Arbitrary-Width Integers
_BitInt(N) creates integers with exactly N bits. Need a 128-bit integer? A 3-bit counter? C23 has you covered!
1#include <stdio.h>23int main() {4 // Exact-width integers5 _BitInt(8) small = 127; // 8-bit signed (-128 to 127)6 unsigned _BitInt(8) byte = 255; // 8-bit unsigned (0 to 255)7 8 // Large integers beyond 64 bits!9 _BitInt(128) huge = 12345678901234567890wb; // 128-bit!10 unsigned _BitInt(256) massive; // 256-bit unsigned!11 12 // Small integers for bit flags13 _BitInt(4) nibble = 15; // 4-bit value (0-15)14 unsigned _BitInt(1) bit = 1; // Single bit!15 16 printf("small = %d\n", (int)small);17 printf("byte = %u\n", (unsigned)byte);18 printf("nibble = %d\n", (int)nibble);19 printf("bit = %u\n", (unsigned)bit);20 21 // Arithmetic works normally22 _BitInt(128) a = 1000000000000wb;23 _BitInt(128) b = 2000000000000wb;24 _BitInt(128) sum = a + b;25 26 return 0;27}Literal Suffix
Use wb suffix for signed and uwbfor unsigned _BitInt literals: 123wb, 456uwb
06[[Attributes]]: Standard Metadata
C23 adds standard attributes using the [[attr]] syntax. These provide hints to the compiler for optimization and warnings.
1#include <stdio.h>2#include <stdlib.h>34// [[nodiscard]] - Warn if return value is ignored5[[nodiscard]] int allocate_resource(void) {6 return 42; // Caller must use this value7}89// [[deprecated]] - Mark as deprecated10[[deprecated("Use new_function() instead")]]11void old_function(void) {12 printf("Don't use me!\n");13}1415// [[maybe_unused]] - Suppress unused warnings16void debug_only([[maybe_unused]] int verbose) {17 #ifdef DEBUG18 if (verbose) printf("Debug mode\n");19 #endif20}2122// [[noreturn]] - Function doesn't return23[[noreturn]] void fatal(const char* msg) {24 fprintf(stderr, "%s\n", msg);25 exit(1);26}2728// [[fallthrough]] - Intentional switch fallthrough29void handle(int code) {30 switch (code) {31 case 1:32 printf("Case 1\n");33 [[fallthrough]]; // Intentional!34 case 2:35 printf("Case 2\n");36 break;37 }38}3940int main() {41 // Ignoring [[nodiscard]] would trigger warning:42 int result = allocate_resource();43 printf("Got: %d\n", result);44 45 // Using deprecated function triggers warning:46 // old_function(); // Warning: deprecated47 48 handle(1);49 50 return 0;51}| Attribute | Purpose |
|---|---|
| [[nodiscard]] | Warn if return value ignored |
| [[deprecated]] | Mark function/type as deprecated |
| [[maybe_unused]] | Suppress unused warnings |
| [[noreturn]] | Function never returns |
| [[fallthrough]] | Intentional switch fallthrough |
07Other C23 Improvements
# Binary Literals
int mask = 0b11110000;Write binary numbers directly!
Digit Separators
int million = 1'000'000;Use ' for readability!
Label at End
{ code; label: }Labels can appear at block end
#embed
#embed "data.bin"Include binary files in source
!Code Pitfalls: Common Mistakes & What to Watch For
Code may use C23 features like nullptr, constexpr, or typeof that most compilers don't yet support.
If you search online to "write modern C code," it might use nullptr instead of NULL, or constexpr for compile-time constants. While these are valid C23 features, as of 2024, most compilers have incomplete C23 support. Your code won't compile on standard production systems.
The Trap: Online sources are trained on bleeding-edge documentation and proposals, not production reality. They might also confuse C23 features with similar C++ features (like C++ constexpr which is more powerful than C's version).
The Reality: C23 is still being rolled out. Stick to C17/C11 for production code unless you've verified your specific compiler version supports C23 features. Use -std=c2x or -std=c23 and test thoroughly. Don't use C23 features in code meant to be portable across different systems.
09Frequently Asked Questions
Q:When will C23 be widely supported?
A: C23 was finalized in 2024. GCC 14+ and Clang 18+ have substantial support. Use -std=c23 or-std=c2x to enable. Full support is expected within 1-2 years of release.
Q:What's the difference between nullptr and NULL?
A: NULL is typically (void*)0 or just 0 — an integer that can cause type confusion. nullptr is a true null pointer constant with its own type (nullptr_t), preventing accidental integer usage.
Q:Why would I use _BitInt instead of regular integers?
A: _BitInt(N) lets you specify exact bit widths beyond standard types. Need a 256-bit integer for cryptography? _BitInt(256). Need exactly 48 bits?_BitInt(48). No more manual multi-word arithmetic!
Q:Is constexpr in C23 the same as in C++?
A: Similar but more limited. C23constexpr applies to objects (variables), not functions. It creates true compile-time constants that can be used in array sizes and switch cases, unlike C++ which allows constexpr functions.
09Summary
Key C23 Features:
typeof— Type inferenceconstexpr— Compile-time constantsnullptr— Proper null pointerbool/true/false— Keywords
_BitInt(N)— Arbitrary width[[attributes]]— Standardized0b101— Binary literals- Use
-std=c23to compile
08Adopting C23
Check Compiler Support
GCC 13+ and Clang 16+ have partial C23 support. Check your compiler's documentation for which features are implemented. Not all C23 features are available yet in all compilers.
Gradual Migration
You don't need to use all C23 features at once. Start with the most beneficial ones likenullptr and binary literals. Keep existing code working while adopting new features where they add value.
Test Your Knowledge
Related Tutorials
C11 Standard Features
Master the C11 standard! Learn about _Generic, _Noreturn, static_assert, anonymous structs/unions, aligned_alloc, threads, and atomics introduced in C11.
C Preprocessor Directives
Code that runs before compilation! Learn #include, #define macros, and conditional compilation with #ifdef.
Multithreading in C
Learn parallel programming in C! Master pthreads (POSIX threads), C11 threads, mutex locks, condition variables, and thread synchronization.
Have Feedback?
Found something missing or have ideas to improve this tutorial? Let us know on GitHub!