What Is New in C23? C Language Changes and Features

The C23 standard (ISO/IEC 9899:2024) marks the most significant update to the C programming language in over a decade, introducing modern syntax improvements, standardizing common idioms, and pruning outdated legacy constructs. This overview explores the finalized features of C23, covering fundamental language enhancements such as native booleans, type inference, standard attributes, and binary resource embedding, alongside critical library upgrades and deprecations.

Core Language Modernizations

  • Native Booleans (bool, true, false): Previously defined as macros in <stdbool.h>, bool, true, and false are now first-class keywords in the core language. The header <stdbool.h> remains available for backward compatibility.
  • Distinct Null Pointer (nullptr): C23 introduces nullptr with the underlying type nullptr_t. Unlike the traditional NULL macro (which often expands to (void*)0 or integer 0), nullptr is explicitly a pointer type and cannot be inadvertently assigned to integer types.
  • Type Inference (auto): The auto keyword has been repurposed to deduce the type of a variable from its initializer, aligning with modern C++ usage. The variable must have an initializer to determine its type.
  • Type Queries (typeof and typeof_unqual): Long supported as a compiler extension (such as in GCC and Clang), typeof is now standard. It retrieves the type of an expression or type name. typeof_unqual functions similarly but strips const, volatile, and atomic qualifiers.
  • Empty Parameter Lists Mean "No Arguments": In prior C standards, a function declared as int foo() meant the function took an unspecified number of arguments. In C23, int foo() is strictly equivalent to int foo(void), taking zero arguments.

Syntax and Preprocessor Enhancements

  • Binary Literals and Digit Separators: Programmers can now write binary constants directly using prefixes like 0b or 0B (e.g., 0b101010). Single quotes can be used as digit separators for improved readability in integer and floating-point literals (e.g., 1'000'000 or 0b1111'0000).
  • Standard Attribute Syntax ([[...]]): C23 adopts the double-bracket attribute syntax introduced in C++11. Standardized attributes include:
    • [[nodiscard]]: Warns if the return value of a function or type is ignored.
    • [[maybe_unused]]: Suppresses compiler warnings for unused variables or parameters.
    • [[deprecated]]: Warns when a designated entity is used, optionally accepting a reason string.
    • [[fallthrough]]: Explicitly indicates an intentional fall-through in a switch statement.
    • [[unsequenced]] and [[reproducible]]: Convey functional side-effect guarantees for compiler optimizations.
  • Binary Resource Inclusion (#embed): The #embed preprocessor directive allows source files to directly incorporate raw binary data from external files into arrays as a sequence of integer values, eliminating the need for manual file-to-hex conversion scripts.
  • Conditional Directives (#elifdef and #elifndef): These additions simplify preprocessor logic by replacing verbose combinations of #elif defined(...) and #elif !defined(...).
  • Flexible Label Placement: Labels can now be placed at the end of compound statements or directly before declarations without requiring an empty statement ({ label: int x = 0; }).

Data Types and Representations

  • Mandatory Two's Complement: C23 drops historical support for one's complement and sign-magnitude representations. All signed integers are now guaranteed to use two's complement representation.
  • Bit-Precise Integers (_BitInt(N)): Introduces exact-width integer types where \(N\) defines the explicit number of bits (e.g., _BitInt(24) or unsigned _BitInt(48)), useful for hardware modeling and FPGA target support.
  • Zero-Size Structures: Empty structure declarations (struct S {};) are now conditionally permitted with implementation-defined sizes.

Standard Library Additions and Hardening

  • Checked Integer Arithmetic (<stdckdint.h>): Provides functions to perform safe additions, subtractions, and multiplications (ckd_add, ckd_sub, ckd_mul) that return a boolean indicating whether an overflow occurred.
  • Explicit Memory Zeroing (memset_explicit): Introduced in <string.h> to securely overwrite sensitive data (like passwords or cryptographic keys). Unlike standard memset, compilers are prohibited from optimizing away this call.
  • POSIX Extensions in Standard C: Several widely used POSIX utilities are now standardized in the C library, including memccpy, strdup, and strndup.
  • Time Improvements: Additions to <time.h> include timespec_getres for querying time resolution and standardized handling of leap seconds.

Removed and Deprecated Features

  • Removal of K&R Function Declarations: The ancient K&R declaration style (where parameters are listed in the signature and typed on subsequent lines) has been entirely removed from the language.
  • Removal of Trigraghs: Three-character escape sequences like ??= (for #) or ??/ (for \) have been completely removed.
  • Removal of __alignof_is_defined and __alignas_is_defined: Replaced by core keyword alignments.
  • Deprecation of Static Asserts via Macro: The static_assert keyword is now directly part of the grammar and no longer requires <assert.h>, also permitting single-argument assertions without a diagnostic message.