C Tutorial

C23 Features Explained: New Syntax, Types & Attributes (2026)

C23 features explained: nullptr, constexpr, auto, typeof, _BitInt, #embed, and the new [[attributes]]. A clear 2026 reference to the ISO/IEC 9899:2024 standard.

Long Nguyen Avatar

Long Nguyen

Fullstack Developer · AI Engineer · Researcher

6 min read

C23 (formally ISO/IEC 9899:2024) is the current revision of the C standard and the biggest update to the language in over a decade. It bumps __STDC_VERSION__ to 202311L and borrows several ideas from C++ to make the two languages easier to use together. This is a practical reference to the most important C23 features: the new keywords and types, the standardized attributes, the literal and preprocessor additions, and the library and safety changes, plus how to enable it in your compiler.

What is C23?

C23 is the successor to C17 and the fourth major C standard after C99, C11, and C17. Its focus is modernization: removing long-standing ambiguities, catching more errors at compile time, and aligning low-level syntax with patterns C++ developers already know. You enable it by compiling with -std=c23 or -std=gnu23 (older toolchains used the pre-standard name -std=c2x). GCC 13 and later and Clang 18 and later offer solid support, while MSVC support is still partial, so check cppreference's per-feature table before relying on any single feature in production.

New keywords, types, and type inference

Most of the day-to-day improvements in C23 are here.

  • nullptr: a real null pointer constant of type nullptr_t, instead of the ambiguous NULL macro. It converts to any pointer type but is not an integer, which removes a class of bugs in type-generic and variadic code.
  • constexpr: true compile-time constant objects. Unlike C++, it applies to variables only, not functions, and only for values computable at compile time from constant expressions.
  • auto: type inference from the initializer. It is more limited than C++ auto, for example it cannot be used as a function return type.
  • typeof and typeof_unqual: standardized operators to get the type of an expression, the latter stripping qualifiers like const.
  • _BitInt(N): bit-precise integers of an exact width, useful for hardware protocols, FPGA work, and packed network fields where fixed 8, 16, 32, or 64-bit types waste space.
  • Real keywords at last: bool, true, false, static_assert, thread_local, alignas, and alignof are now keywords, so you no longer need <stdbool.h> or the underscore-prefixed spellings.
  • Enumerations with a fixed underlying type: you can now write enum E : unsigned char { ... } for portable, predictable sizes.
  • Empty initializer: = {} zero-initializes any object, replacing the older = {0} idiom.
  • Decimal floating-point: _Decimal32, _Decimal64, and _Decimal128 for exact base-10 arithmetic, though support is implementation-defined rather than mandatory.
constexpr int max_buffer = 1024;
auto total = max_buffer * 4;           // int, inferred
int *p = nullptr;                      // typed null
unsigned _BitInt(7) flag = 120;        // exact 7-bit width
enum Status : unsigned char { OK, ERR };

C23 attributes

C23 finally standardizes the [[attribute]] syntax, so you no longer need vendor-specific spellings like GCC's __attribute__((...)). These are portable diagnostic and optimization hints the compiler understands directly.

Attribute Purpose
[[nodiscard]] Warns if the return value is ignored; accepts an optional reason string.
[[maybe_unused]] Suppresses unused warnings for a variable, function, or parameter.
[[deprecated]] Flags an entity as deprecated, warning on use, with an optional message.
[[fallthrough]] Marks an intentional fall-through between switch cases.
[[noreturn]] Marks a function that never returns; replaces _Noreturn.
[[unsequenced]] Declares a function stateless and side-effect free (pure), enabling optimizations.
[[reproducible]] Declares a function effectless, so repeated calls with the same inputs are interchangeable.

C23 also relaxes static_assert: the message argument is now optional, so static_assert(sizeof(int) >= 4); is valid when the condition is self-explanatory.

Literals and preprocessor additions

  • Binary literals: write values directly as 0b1010, and print them with the new %b and %B conversions in printf.
  • Digit separators: use ' to group digits, as in 1'000'000, for readability.
  • #embed: a preprocessor directive that embeds the bytes of a binary file directly into your source, replacing brittle build-time conversion scripts.
  • #elifdef and #elifndef: concise conditional branches, plus a standardized #warning directive.
  • Feature testing: __has_include and __has_c_attribute let you probe for headers and attributes at compile time.
  • UTF-8: u8 character literals and a char8_t type for unambiguous UTF-8 text.

Standard library and safety changes

  • unreachable(): a macro in <stddef.h> marking code paths that can never be reached, which both documents intent and enables optimization.
  • Checked integer arithmetic: <stdckdint.h> adds ckd_add, ckd_sub, and ckd_mul, which perform arithmetic and report overflow instead of silently wrapping.
  • Two's complement guaranteed: signed integers are now defined as two's complement, removing a long-standing source of portability uncertainty.
  • New library functions: memset_explicit for erasing sensitive data that will not be optimized away, plus standardized strdup, strndup, and memccpy.

What C23 removes or changes

C23 also cleans out legacy behavior. Old K&R style function declarations and definitions are gone, and an empty parameter list now means the function takes no arguments, exactly like (void), rather than the historic meaning of accepting any arguments. This effectively makes prototypes mandatory, so migrating older code means adding proper prototypes and enabling strict warnings before switching the standard.

Compiler support and enabling C23

Enable C23 with -std=c23 (strict) or -std=gnu23 (with GNU extensions) on GCC 13+ and Clang 18+; MSVC support is partial and evolving. You can confirm the mode at compile time by checking that __STDC_VERSION__ equals 202311L. Because feature coverage still varies between compilers and versions, verify anything critical against cppreference's C23 compiler-support table before shipping.

gcc -std=c23 -Wall -Wextra main.c -o main
clang -std=c23 -Wall -Wextra main.c -o main

FAQ

Frequently asked questions

What is C23?

C23, formally ISO/IEC 9899:2024, is the current revision of the C standard, following C17. It bumps __STDC_VERSION__ to 202311L and adds features like nullptr, constexpr, auto type inference, typeof, _BitInt, #embed, and standardized [[attributes]], many borrowed from C++ to make the languages work together more easily.

What are the main new features in C23?

The highlights are nullptr and nullptr_t, constexpr for variables, auto type inference, typeof, _BitInt(N) bit-precise integers, standardized [[attributes]], binary literals and digit separators, #embed, unreachable(), checked integer arithmetic via <stdckdint.h>, and bool, true, false, and static_assert becoming real keywords.

What are C23 attributes?

C23 standardizes the [[attribute]] syntax so you no longer need vendor-specific forms like GCC's __attribute__. The standard attributes are [[nodiscard]], [[maybe_unused]], [[deprecated]], [[fallthrough]], [[noreturn]], [[unsequenced]], and [[reproducible]], which give the compiler portable diagnostic and optimization hints.

How do I enable C23 in GCC or Clang?

Compile with -std=c23 for strict mode or -std=gnu23 to keep GNU extensions. GCC 13 and later and Clang 18 and later have good C23 support, while MSVC is partial. You can verify the mode by checking that __STDC_VERSION__ equals 202311L.

Is C23 constexpr the same as C++ constexpr?

No. C23's constexpr is more limited: it applies only to variables, not functions, and only to values computable at compile time from constant expressions. C++ additionally allows constexpr functions and much more, so the keyword looks familiar but does less in C.

Stay visible to AI

AEO, GEO, and agent-readiness tips, sent straight to your inbox.