C99 vs C11 vs C17: What Changed and Which Standard to Use

Short answer: C99 modernised the language, C11 added compile-time checks, generic selection, alignment and atomics and removed gets(), and C17 only fixed defects in C11. For new code, compile with -std=c17 (or -std=c11) and add -pedantic-errors; reach for -std=c99 or -std=c89 only when a toolchain or coding standard requires it.

Timeline of C99, C11 and C17 milestones with implementation caveats

C89/C90: the baseline

ANSI C (1989), adopted by ISO in 1990, is the oldest standard still selected with -std=c89 or -std=c90. Declarations must appear at the start of a block, only /* */ comments exist, there is no long long and no boolean type, and __STDC_VERSION__ is not defined.

What C99 added

  • Declarations mixed with statements, including for (int i = 0; ...) loop counters.
  • // line comments, designated initializers and compound literals.
  • long long, _Bool with <stdbool.h>, and fixed-width types in <stdint.h>.
  • inline functions, variable-length arrays and snprintf.
  • The %zu, %td and %lld conversions for size_t, ptrdiff_t and long long.

What C11 added and removed

  • _Static_assert for compile-time checks and _Generic for type-based selection.
  • Anonymous structures and unions, _Alignas/_Alignof and _Noreturn.
  • _Atomic with <stdatomic.h> and <threads.h>; both are optional for implementations.
  • Variable-length arrays became optional (__STDC_NO_VLA__).
  • gets() was removed because it cannot limit how much input it stores; use fgets.

What C17 changed

C17, published as ISO/IEC 9899:2018 and also called C18, is a maintenance revision. It adds no language features; it resolves defect reports against C11 and sets __STDC_VERSION__ to 201710L. GCC accepts both -std=c17 and -std=c18. The next revision, C23, is selected with -std=c23 in recent GCC and Clang releases.

Comparison table

StandardDocumentGCC/Clang flag__STDC_VERSION__Main changes
C89/C90ANSI X3.159-1989 / ISO/IEC 9899:1990-std=c89not definedANSI C / ISO C90: declarations only at the top of a block, /* */ comments only, no long long.
C99ISO/IEC 9899:1999-std=c99199901LMixed declarations, // comments, designated initializers, VLAs, inline, long long, _Bool.
C11ISO/IEC 9899:2011-std=c11201112L_Generic, _Static_assert, anonymous struct/union, _Alignas, _Noreturn, <stdatomic.h>, threads; gets() removed; VLAs optional.
C17ISO/IEC 9899:2018-std=c17201710LDefect-fix revision of C11 (also called C18): no new language features, __STDC_VERSION__ becomes 201710L.

Check which standard your compiler uses

Compile the same file with each flag. With -pedantic-errors, code that needs a newer standard fails to build instead of compiling as an extension.

gcc -std=c89 -pedantic-errors -Wall -Wextra main.c -o main
gcc -std=c99 -pedantic-errors -Wall -Wextra main.c -o main
gcc -std=c11 -pedantic-errors -Wall -Wextra main.c -o main
gcc -std=c17 -pedantic-errors -Wall -Wextra main.c -o main

This program prints the standard the compiler is using. Built with -std=c17 it prints __STDC_VERSION__ = 201710L.

#include <stdio.h>

int main(void) {
#ifdef __STDC_VERSION__
    printf("__STDC_VERSION__ = %ldL\n", __STDC_VERSION__);
#else
    puts("__STDC_VERSION__ is not defined (C89/C90)");
#endif
    return 0;
}

Try it: C standard compatibility matrix

Filter reviewed C language features by standard or category, see which -std= flag accepts each one, then load the runnable fixture — or a probe demonstrating rejection or an implementation extension — into the compiler.

16 features reviewed

FeatureC89/C90-std=c89C99-std=c99C11-std=c11C17-std=c17
Not availableSupportedSupportedSupported
Not availableSupportedSupportedSupported
Not availableSupportedSupportedSupported
Not availableSupportedSupportedSupported
Not availableSupportedOptionalOptional
Not availableSupportedSupportedSupported
Not availableSupportedSupportedSupported
Not availableSupportedSupportedSupported
Not availableNot availableSupportedSupported
Not availableNot availableSupportedSupported
Not availableNot availableSupportedSupported
Not availableNot availableSupportedSupported
Not availableNot availableSupportedSupported
Not availableNot availableOptionalOptional
SupportedDeprecatedRemovedRemoved
Not availableNot availableNot availableSupported
Show the -std=c99 / c11 / c17 lanes diagramMatrix of reviewed C features in -std=c99, -std=c11 and -std=c17 lanes with accepted, optional, removed and rejected badges

_Generic type-generic macros

Added in C11 · minimum flag -std=c11

_Generic picks an expression at compile time from the type of its controlling argument — the basis for type-generic macros like <tgmath.h>.

#include <stdio.h>

#define TYPE_NAME(x) _Generic((x), \
    int: "int", \
    double: "double", \
    char *: "char *", \
    default: "other")

int main(void) {
    char text[] = "hi";
    printf("%s\n", TYPE_NAME(42));
    printf("%s\n", TYPE_NAME(3.14));
    printf("%s\n", TYPE_NAME(text + 0));
    return 0;
}
gcc -std=c11 -pedantic-errors -Wall -Wextra main.c -o main
$ gcc -std=c11 -pedantic-errors -Wall -Wextra main.c -o main && ./main
int
double
char *

Compiled without diagnostics · exit code 0

Representative GCC output, not a live run. Diagnostic wording, line and column numbers vary between compiler versions.

Need the conversions for a particular type? Open the printf and scanf format specifier cheat sheet. The full-page C standard compatibility checker hosts the same matrix with more room, and Turbo C vs modern C covers code written for pre-standard compilers.

Ready to try it? Open the online C compiler, paste a program and run it, or read the C error guides when a build fails.

Frequently asked questions

What is the difference between C11 and C17?

C17 (ISO/IEC 9899:2018, also called C18) adds no new language features. It incorporates corrections to defects reported against C11 and changes __STDC_VERSION__ to 201710L. Code that compiles as C11 normally compiles unchanged as C17.

Should I use C99, C11 or C17 for new code?

Use -std=c17 (or -std=c11) unless a toolchain or coding standard requires older C. It includes everything in C99 plus _Static_assert, _Generic, alignment control and atomics. Choose -std=c99 only for compilers that lack C11 support.

Was gets() removed in C11?

Yes. gets() was deprecated by a 2007 technical corrigendum to C99 and removed in C11 because it cannot limit how much input it stores. Use fgets(buf, sizeof buf, stdin) instead.

Are variable-length arrays allowed in C11?

C99 required variable-length arrays; C11 made them optional, and an implementation without them defines __STDC_NO_VLA__. GCC and Clang support them, but Microsoft Visual C++ does not.