Which C Version?

One thing I take for granted is which C standard I’m using. The differences between the versions are subtle, and the compiler chooses its standard by default. But this choice can be altered for compatibility or historical reasons.

Every so often, the Committee That Does Such Things releases a new C standard, improving the language and offering new features while trying to maintain compatibility with the universe of older (and still operating) C programs. Here are the current standards:

K&R. The original C written by Brian Kernighan and Dennis Ritchie and documented when they published The C Programming Language in 1978.

ANSI C / C89. This version of C is backed by the ANSI standard body. It standardized C for use across multiple compilers.

C99. This version added the long long data types, C++ style // comments, and Unicode support.

C11. New keywords were introduced, primarily the underscore keywords.

C17. Various minor improvements.

C2x. A major update that I’ll cover as soon as many of its features are implemented in popular compilers.

Even within these standards, compilers and libraries exist to extend the language.

To help sort out which version is which, and avoid compatibility issues in C code, two defined constants are available: __STDC__ and __STDC_VERSION__.

The __STDC__ constant is defined in ANSI C / C89 forward. It’s set to 1 or TRUE.

The __STDC_VERSION__ constant was introduced with C99. It’s a long unsigned value equal to the C version year and month represented as a six digit value.

The following code tests the standard constants and outputs the results.

2022_12_10-Lesson.c

#include <stdio.h>

int main()
{
    if( __STDC__ )
    {
        printf("The C version is at least C89\n");
#ifdef __STDC_VERSION__
    printf("C standard version: %lu\n",__STDC_VERSION__);
#endif
    }
    else
    {
        printf("Somehow you got ahold of K&R C. Neat!\n");
    }

    return(0);
}

The if test at Line 5 checks for the __STDC__ constant, which should exist in all modern compilers. If not, the program won’t compile, so my else statement (Line 14) is unnecessary. Presence of the __STDC__ constant indicates at least C89, so a printf() statement confirms at Line 7.

The #ifdef preprocessor test at Line 8 checks for the __STDC_VERSION__ constant, which isn’t present in C89. If found, its value is output.

Here is a sample run with the program compiled by clang in macOS, no other options:

The C version is at least C89
C standard version: 201710

Your compiler may output 201112 as the version; it all depend on the defaults. Even so, you can use the -std switch at the command line to set a specific C version for compiling.

Here is a run of the code (a.out) compiled with various switches to set the C language version:

$ clang -Wall -std=c89 1210.c 
$ ./a.out
The C version is at least C89
$ clang -Wall -std=c99 1210.c 
$ ./a.out
The C version is at least C89
C standard version: 199901
$ clang -Wall -std=c11 1210.c 
$ ./a.out
The C version is at least C89
C standard version: 201112
$ clang -Wall -std=c17 1210.c 
$ ./a.out
The C version is at least C89
C standard version: 201710
$ clang -Wall -std=c2x 1210.c 
$ ./a.out
The C version is at least C89
C standard version: 202000

The final switch I use is -std=c2x for the next version of C. Some compilers have a tiny subset of this version ready, so using the switch may not generate an error. Still, the value output is 202000 – not 2023nn, which would be present for compilers that implement the full version of C23.

If compatibility is an issue for any code, using these constants is one way to ensure that the program compiles. Otherwise, it’s a curious tidbit to know which C standard your compiler uses.

4 thoughts on “Which C Version?

  1. While the above is sufficient, if one has the luxury of only needing to target *nix OS (just as Iʼm currently writing this under Ubuntu), I feel that one cannot negate that 3 out of 4 PCs are still running under Windows.

    And on Windows the sad truth is (or rather was), that until the release of “Visual Studio 2019” v16.8 (on 10ᵗʰ November, 2020), Visual C++ʼs compiler defined neither __STDC__ nor __STDC_VERSION__.

    Now, while in v16.8 (and newer versions) both macros are finally defined… unless one of the compiler options /std:c11 or /std:c17 are given, __STDC_VERSION__ will just report a default value of 199409L (i.e. “C95”).

    Thatʼs not to say, of course, that Visual C++ʼs language standard support hasnʼt improved over the years… but instead of a nice __STDC_VERSION__ macro, one has–had–to check the compilerʼs version by either testing against specific _MSC_VER (major/minor version) or _MSC_FULL_VER (major, minor, as well as build number) values:

    #if _MSC_VER >= 1928 /* Visual Studio 2019 v16.8 (or higher)? */
    /* Supports C11 (as well as its bug-fix release C17/C18 */
    #elif _MSC_VER >= 1900 /* Visual Studio 2015 v14 (or higher)? */
    /* Supports C99, partial support for _Complex floating-point values */
    #elif _MSC_VER >= 1800 /* Visual Studio 2013 v12 (or higher) */
    /* With some exceptions–e.g. , snprintf–good support for C99 */
    #elif _MSC_VER >= 1100 /* Visual Studio 97 (or higher)? */
    /* Already shipped with a iso646.h, seems to have supported “C95” */
    #elif _MSC_VER >= 600 /* MS C (Microsoft C) 6.0, 1989 */
    /* Good support for ANSI C, i.e. “C89”:
    https://www.drdobbs.com/windows/optimizing-with-microsoft-c-60/184408398 */
    #endif

    If someone is interested, the following header file demonstrates how all this could be combined to yield a solution that works under both Linux as well as Windows:

    https://github.com/Cordic77/C-programming/blob/master/detect_stdc.h

  2. As someone who has worked with Microsoft compilers since the early 1980s, I fully understand and dislike why it’s taken them so long to adapt. Oh, I could tell you stories…

    I just learned yesterday that Microsoft runs all its internal stuff on Linux. Perhaps the coders in Redmond are starting to be heard?

  3. » Oh, I could tell you stories…
    Truth be told (having learned “Classic C” under Borland’s Turbo C) I didn’t really come into contact with Microsoft’s “C” compiler until Visual Studio 1998… but I can relate, have no trouble believing that!

    With regards to Microsoft’s newly found love for “C”, we programmers, I think, are just lucky that – since C++’s revival after the C++11 standard came out in 2011 – they now like to stress Visual C++’s standards conformance.

    … and if they want to be able to call their compiler “C++20 compliant”, then they *just have to* have enough language support to at least be able to compile “C”s standard library (as described in ISO/IEC 9899:2018) – lucky us!

  4. Yes, it took me years to filter out all the bad things Turbo C taught me. But at the time, the typical compiler sold for $500. Turbo C was $49. Thank you, Phillipe!

Leave a Reply