C23 language changes

Breaking changes

void foo() now means void foo(void)

In C17 and earlier, void foo() means “I haven't yet told you how many arguments this function has”. In C23, it's equivalent to C++ and means “this function has no arguments”. This may surface as a function pointer type mismatch, because previously () matched functions taking any arguments, whereas in C23 it only matches functions taking no arguments.

Fix: in cases where your function does have arguments, declare them.

Undeclared identifiers are now errors

In C17 and earlier, calling foo(123) without a declaration for foo() produced a warning. In C23 this is an error instead. One common special case of this is code that's explicitly ignoring such warnings to call functions that are GNU extensions; such code should be fixed to ensure that _GNU_SOURCE is defined before any header is included instead (often by adding -D_GNU_SOURCE to the cflags in the build file).

Fix: add the missing forward declaration or #include (or -D_GNU_SOURCE).

bool/true/false are now keywords

In C17 and earlier, only code that included <stdbool.h> would have standard definitions for these (typically macros for _Bool/1/0). In C23 these are keywords and should no longer be defined in your code.

Fix: delete any definitions of bool/true/false if you only need to build as C23, or switch to #include <stdbool.h> for compatibility back to C99.

false is no longer 0

In C17 and earlier, it was common for true and false to be defined as 1 and 0 (either by <stdbool.h> or by user-provided #define/enum). This meant that false (as 0) could be implicitly converted to NULL. In C23, a function that returns (or takes) a pointer can no longer return false (or be passed false).

Fix: return/pass NULL (or nullptr for C23-only code) instead of false in pointer contexts.

unreachable() is now a predefined function-like macro in <stddef.h>

In C17 and earlier, unreachable() was available for your own macros/functions. In C23 there's a standard definition.

Fix: delete your unreachable() if it was just equivalent to __builtin_unreachable() or rename it if it had different behavior.

K&R prototypes are no longer valid

In C17 and earlier, K&R function prototypes were deprecated but still allowed. In C23 K&R prototypes are no longer allowed.

Fix: rewrite any K&R prototypes as ANSI/ISO prototypes.

Non-breaking changes

Unused function parameters can now be anonymous

In C17 and earlier you'd have to use __attribute__((unused)) on an unused function parameter. In C23 you can just omit the parameter name instead, like void* pthread_callback_fn(void*) { (as in C++).

New standard attributes

C23 adds [[deprecated("reason")]], [[fallthrough]], [[nodiscard]] (the equivalent of the clang attribute warn_unused_result), [[maybe_unused]] (the equivalent of the clang attribute unused), and [[noreturn]] (equivalent to C11 _Noreturn). Most of these have been available before via __attribute__ or other syntax, but are now standard.

#embed

You can now include binary data directly into an array or string: https://en.cppreference.com/w/c/preprocessor/embed

void foo(...) is now allowed

In C17 and earlier, a varargs function needed a non-varargs argument. In C23 this is allowed (as in C++).

enum base types

You can now say enum E : long { ... } to explicitly choose the base type of your enum (as in C++, and already supported by clang as an extension).

nullptr constant

There is now a nullptr constant (as in C++), and a corresponding nullptr_t type for that constant.

constexpr

There is now a limited form of constexpr for defining const variables (similar to, but much more limited than C++ constexpr).

Library changes

Library changes are not covered here because bionic does not make library functionality available based on target C version, since the target API level distinctions are confusing enough already.

See status.md for what functionality went into which API level.