C23 Double Bracket Attribute Syntax Guide

The C23 standard officially adopted the double bracket attribute syntax, [[attribute]], directly aligning ISO C with modern C++ standards. This article covers the origins of this syntax, the problems it solves regarding compiler portability, the standard attributes introduced in C23, and how developers can utilize them to write cleaner, safer code.

Historically, C compilers relied on vendor-specific extensions to provide metadata about functions, variables, and types. GCC and Clang used __attribute__((...)), while Microsoft Visual C++ used __declspec(...). This fragmented ecosystem led to convoluted macro definitions across codebases to maintain cross-platform compatibility. To resolve this fragmentation, the C Standards Committee integrated the standardized double-bracket attribute syntax first introduced in C++11.

In C23, attributes are enclosed within double square brackets, such as [[nodiscard]] or [[deprecated]]. They can be applied to various code entities, including functions, variables, types, and statement blocks. Syntactically, they can appear with or without arguments and can also support vendor-specific namespaces (for example, [[gnu::always_inline]]) without breaking conformance on other standard-compliant compilers, which are required to ignore unrecognized attributes.

C23 specifies several standard attributes adopted directly from C++:

  • [[nodiscard]]: Signals a warning if the return value of a function call is discarded. An optional string literal can be provided to explain why the result must not be ignored (e.g., [[nodiscard("Resource leak")]]).
  • [[maybe_unused]]: Suppresses compiler warnings when a variable, function, or parameter is intentionally left unused.
  • [[deprecated]]: Marks an entity as obsolete, optionally providing a message guiding the developer toward modern alternatives.
  • [[fallthrough]]: Explicitly indicates that an intentional fallthrough occurs in a switch statement, silencing warnings from static analysis tools and compilers.

In addition to these common attributes, C23 introduced memory- and effect-tracking attributes tailored for C:

  • [[reproducible]]: Indicates that a function does not modify any observable state and returns the same output given the same arguments within a given execution sub-sequence.
  • [[unsequenced]]: Signifies a pure, stateless function with no side effects and no dependencies on mutable global state, enabling aggressive compiler optimizations.

By adopting the [[...]] syntax, C23 establishes a unified, cross-language mechanism for supplying semantic metadata, reducing reliance on preprocessor hacks and improving interoperability between modern C and C++.