Skip to content

C Standards in Practice

A project standard is a compatibility contract. It controls syntax and library expectations, but compiler support, platform libraries, extensions, and build flags still determine what works.

C89/C90 established the portable base

The first standardized C remains visible in long-lived and embedded code. Declarations traditionally appear at block beginnings, comments use /* ... */, and function declarations may use forms that modern projects should replace with prototypes.

Supporting C90 can be a legitimate platform constraint, but it excludes useful language and library improvements and increases reliance on local conventions.

C99 was a major modernization

C99 added or standardized widely used features including:

  • declarations mixed with statements;
  • // comments;
  • long long and <stdint.h>;
  • inline, restrict, and variable length arrays;
  • designated initializers and compound literals;
  • flexible array members;
  • improved floating and formatting facilities.

VLA support later became optional, and MSVC support differs, so portable projects often avoid depending on it even when using C17.

C11 added concurrency and stronger language facilities

C11 introduced _Static_assert, _Generic, _Atomic, a memory model, <threads.h>, alignment facilities, anonymous struct/union support, and other library updates. Some facilities are optional or unevenly implemented. Atomics can be usable even where the standardized thread API is not the platform's normal thread layer.

C17 primarily corrected C11

C17 made defect corrections rather than adding a large feature set. That stability and broad implementation make it a practical conservative course baseline. Selecting -std=c17 does not disable all implementation-specific behavior unless extensions and warning policy are also controlled.

C23 is a substantial revision

C23 adds language and library improvements such as nullptr, typeof forms, attributes, binary literals, digit separators, improved enumerations, checked integer arithmetic through <stdckdint.h>, modernized declarations and initialization, and removal or revision of older facilities. Compiler and library support arrives feature by feature.

Do not infer complete C23 support from acceptance of -std=c23. Check the particular front end and library facility. Some compilers historically used -std=c2x during development.

Dialects and extensions should be explicit

GCC and Clang distinguish c17 from gnu17. GNU modes enable extensions and may define additional macros. Platform headers can also expose APIs based on feature-test macros.

Use a strict ISO mode for portable core code and enable required extensions deliberately at platform boundaries. -pedantic/-Wpedantic helps reveal some extension use but is not a complete conformance proof.

MSVC's C conformance and option availability differ by release. Treat the Microsoft documentation and CI build as evidence rather than translating GCC flags mechanically.

Migration is a behavior-preserving project

Before changing a standard flag:

  1. reproduce clean builds and tests;
  2. enable strong diagnostics;
  3. inventory extensions and compatibility macros;
  4. change the mode in one authoritative build configuration;
  5. compile across supported compilers;
  6. adopt new features separately from the flag change.

The newest possible standard is not automatically the best baseline. Choose the oldest version that supports the design cleanly across required targets, then revisit deliberately.

Optional prompts

Explain: Why might a C17 project avoid VLAs even though C99 introduced them?

Answer: VLA support became optional and remains uneven across toolchains; stack size is also runtime-dependent. A portable project may choose explicit dynamic or fixed storage.

Explain: Does -std=c23 prove the standard library is complete?

Answer: No. The flag selects a language mode supported by that driver; individual front-end and library features can still be missing.

Further reference