CC Standards (C89/C99/C11/C17/C23)

C Standards (C89/C99/C11/C17/C23)

When people say a compiler "supports C11" or a codebase "targets C99," they are referring to one of the official language standards — formal documents, maintained by ISO, that pin down exactly what every part of the language means. Understanding these standards matters because C code that relies on a feature from a newer standard simply won't compile on a toolchain that only supports an older one.

What a language standard actually is

A standard is a precise specification: it defines the syntax, the meaning of every keyword and operator, the contents of the standard library, and — just as importantly — which behaviors are undefined or implementation-defined. Compiler vendors (GCC, Clang, MSVC, and others) implement these standards, sometimes with extensions of their own. Writing "standard C" and avoiding vendor-specific extensions is what makes your code portable across compilers and operating systems.

The standards, one by one

Standard

Year

Headline new features

C89 / ANSI C (also called C90)

1989 / 1990

The first official standard. Standardized function prototypes, the void keyword, and the standard library as we mostly know it today.

C99

1999

Added // single-line comments, variable-length arrays, stdint.h fixed-width integer types (int32_t, uint8_t, etc.), inline functions, long long, and mixing declarations with statements.

C11

2011

Added _Generic for type-generic expressions/macros, <threads.h> for native multithreading, anonymous structs and unions, and _Static_assert for compile-time checks.

C17 (also called C18)

2018

A "bug fix" release: it clarified and corrected defects in C11 without adding meaningful new language features.

C23

2024

The most recent standard. Adds nullptr, makes bool/true/false real keywords instead of macros, adds #embed for including binary data, and many smaller improvements.

Targeting a specific standard

Most C compilers let you explicitly choose which standard to compile against using a command-line flag. This controls which language features are accepted and can also enable extra warnings for constructs that are non-portable under that standard.

Bash
# Compile targeting the C11 standard, with warnings enabled
gcc -std=c11 -Wall -Wextra -o program program.c

# Compile targeting C99 instead
gcc -std=c99 -Wall -Wextra -o program program.c

# Ask GCC/Clang to reject non-standard (GNU) extensions entirely
gcc -std=c11 -pedantic -Wall -Wextra -o program program.c

Without a -std= flag, most compilers default to their own preferred dialect (often a GNU extension of a recent standard rather than pure ISO C), which can quietly let non-portable code compile without warning. Being explicit about the standard you target is good practice for any code you intend to share or reuse.

Which standard should you actually use?
  • Learning C today? Aim to understand C99 and C11 — the vast majority of tutorials, textbooks, and existing codebases assume at least C99.

  • Working on an embedded or legacy codebase? You may be limited to C89/C90 if the toolchain is old — check your compiler's documentation.

  • Starting a brand-new project with a modern toolchain? C11 or C17 are safe, widely supported choices; C23 support is still spreading across compilers.

C99/C11 are the practical sweet spot
Most real-world C code written today still targets C99 or C11, because these standards are supported by virtually every modern compiler and offer the best balance between useful modern features (fixed-width integers, `//` comments, `_Static_assert`) and maximum portability across platforms, including many embedded toolchains.