Not Every Compiler Likes Your Code

You would think that the various C compilers deal with C code in the same manner. After all, they adhere to the same C standards, right? This compatibility makes it possible to compile and cleanly build C programs regardless of which compiler you use, right? Well, maybe not.

I recently discovered that I technique I use for my C programs works well in the LLVM clang compiler, but really ticks off gcc in Linux. Here is the code:

2022_01_21-Lesson.c

#include <stdio.h>

int main()
{
    const int size = 5;
    int data[size] = {
        5, 8, 2, 6, 9
    };
    int x = 0;

    while( x<size )
        printf("%d\n",data[x++]);

    return(0);
}

At Line 5, I set variable size as a constant to be used throughout the code. It represents the number of elements in the data[] array. I could just declare the array without a size value, in which case the compiler figures out the number of elements. But I use the element count as the while loop’s condition at Line 11. This approach is more readable, plus it avoids having a “magic number” in the code that other programmers may not readily recognize.

The clang compiler has no issues with my using a constant to declare the data[] array’s size.

On the Macintosh, the gcc compiler (which is probably aliased to clang) has no issues either.

In Linux, however, the gcc compiler is hostile toward this approach. It spews out this error:

error: variable-sized object may not be initialized

I use this “variable-sized object” initialization in a lot of my code. Imagine how annoyed I was when I was using an alien system with only gcc available and this error puked all over my screen. (The error message is followed by a series of warnings, one for each array element.)

My frustration with this “error” is not only do other compilers not think it’s an issue, but that gcc also lets you declare an array with a variable size. The key is not to initialize it. So the following statement is okee-doke:

int data[size];

You can then manually assign all the element values.

Some of the feedback I’ve read on the Internet states that it works, but not on a variable-length array (VLA). But data[] isn’t a VLA. In fact, even if I declare the array const, gcc gets angry with the same intensity.

I’m at a loss.

I suppose for compatibility’s sake, I must stop using a constant to initialize an array. I feel the construction is less readable, especially because the constant (for the array’s size) is used elsewhere in the function, but if I strive to write code compatible with the major compilers, it’s a sacrifice I must make.

4 thoughts on “Not Every Compiler Likes Your Code

  1. Have you got the same version of gcc on Mac and Linux? If not it could be a bug in one version which was fixed in the other. (Or didn’t exist in the earlier version but was sneaked into a later version!)

  2. GCCʼs unwillingness to compile the given program stems from the fact, that despite the given assertions, code like

    const int size = 5;
    int data [size] = {5, 8, 2, 6, 9};

    *does* force the compiler to generate code for a VLA (“variable length array”).

    Thatʼs because, while “const”s (like ‘size’) are often initialized by giving literal values, they can also be initialized from variables (only available at runtime):

    int main (int argc, char *argv [])
    { /* const only really says: “After initialization, this variable is read-only.” */
    int const size = argc;
    int data [size] = {…};

    }

    Because of this, compilers canʼt–in general–figure out “const” values at compile-time (and donʼt even try). Therefore, even in C99 (and later standards) the language grammar states, that array declarators must be of the following form (if VLAs are take out of the picture):

    direct-declarator “[” constant-expression “]”

    And apart from specifying an arrayʼs size in form of a compile-time deductible integral constant (or by relying on #define macros, which is the same thing after the preprocessor has run), thereʼs only one other way to get such a “constant-expression”… by using Enumerations (optionally anonymous ones):

    enum { N = 5 }; /* Exemplifies the so-called “enum hack” */
    int data [N] = {5, 8, 2, 6, 9};

    This will compile with every standards-compliant compiler, because “enumeration constant”s (like ‘N’) must be initialized with “constant-expression”s.

    Now, while all that has been said is true, once C23 is released, thereʼll finally be a standards-compliant way of writing the above code:

    constexpr¹ int size = 5; /* see § 6.7.1.15 on page 99 */
    int data [size] = {5, 8, 2, 6, 9};

    … this will compile (without creating a VLA), because–as the name suggests–“constexpr” identifiers can only be initialized by “constant-expression”s (which allows the compiler to deduce the array size at compile-time).

    On another note: as opposed to C++, functions with empty parentheses are taken to mean by “C” compilers, that one is dealing with variable argument functions, i.e. functions that can be called with any number of arguments (of any type). If a program doesn’t expect to be called with any arguments, specifying “void” isnʼt really optional:

    int main (void¹) { /* … */ } /* see § 5.1.2.2.1 on page 12 */

    Sorry for this somewhat overlong posting, but I thought it could be of value if I expounded a little bit, why the compiler behaves as it does.

    ¹ Latest working draft of C23: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3054.pdf

  3. » Can’t wait for C23.
    I agree, the next standard will come with some nice additions – support for IEEE 754-2008 decimal floating-point numbers, the new bit-twiddling functions, standardized syntax for attribute specifiers, … –; however, considering how long it took for C99 to become mainstream, I’d wager that it’ll take at least another 10 years, before any of this can really be used without second thought (at least in the embedded space).

Leave a Reply