Search code examples
c++arraysgccstructicc

compile-time-known array size: passed compilation using g++ but not for icpc


The toy code is quite simple:

#include <stdio.h>
#include <math.h>

#define A 10
#define SIZE (int)(ceil(A / 2)) // needs computation but known at compile-time

struct node {
    int keys[SIZE];
};

int main() {
    node a_node;
    for (int i = 0; i < SIZE; ++i) {
        a_node.keys[i] = i;
        printf("%d\n", a_node.keys[i]);
    }
    return 0;
}

When I compile it using g++ -std=c++11 -O3 test.cpp -o test, it passes and runs as expected; but using the intel compiler with icpc -std=c++11 -O3 test.cpp -o test, an error occurs:

test.cpp(8): error: function call must have a constant value in a constant expression int keys[SIZE];

Why is it regarded as a function call? But if I used simple arithmetic in the macro (e.g. #define SIZE (A / 2)), the error disappears.

gcc version: 4.8.5; icc version: 16.0.3.

Any clue how to solve this issue? Thanks.

------------------ ADDED ------------------

If int keys[SIZE] is declared in the main function instead of the struct, i.e. like this:

#include <stdio.h>
#include <math.h>

#define A 10
#define SIZE (int)(ceil(A / 2))

/*struct node {
    int keys[SIZE];
};*/

int main() {
    //node a_node;
    int keys[SIZE];
    for (int i = 0; i < SIZE; ++i) {
        keys[i] = i;
        printf("%d\n", keys[i]);
    }
    return 0;
}

it will pass both gnu and intel compilers. Quite interesting and wierd. I am not sure if I made any mistake that I am not aware of, any possbile clue?


Solution

  • #define A 10
    #define SIZE (int)(ceil(A / 2))
    

    Your SIZE is not a compile-time constant, because ceil is a floating point function from <math.h>. So the compiler is understanding the variable int keys[SIZE]; as a variable length array (VLA) (whose size is probably computed at runtime). Standard C++11 or C++14 don't have VLAs. But GCC accepts them as an extension to the language. Notice that if you declare your struct node then keys is not a variable but a field or member.

    BTW, if you replace the define of SIZE with

     #define SIZE ((A+1)/2)
    

    it becomes a compile-time constant (e.g. a constexpr in C++ parlance) and has the same value as before.

    BTW, your code is not genuine C++11, but looks like C11 code. I would suggest to install a newer version of GCC (in march 2017, use GCC 6, not some old 4.8). Then use constexpr and std::array

    VLAs cannot occur as fields in struct-s. C99 and C11 have flexible array members as the possible last member of a struct. C++11 don't have them.

    Probably, your GCC compiler (and the <math.h> header) knows that abs can be expanded as __builtin_abs which is handled specially by the GCC compiler (but not by others) (so SIZE is expanded as (int)(ceil(10 / 2)) which, with GCC specifically, becomes a compile-time constant thanks to __builtin_abs)

    You should understand that C and C++ are different languages. If you choose to code in C++ (at least C++11, anything older is obsolete in 2017) you should use its standard containers (and you probably want std::vector since the size is runtime computed). If you choose to code in C (at least C99), consider using flexible array members and allocate your flexible structure in the heap (appropriately using malloc or friends). Notice that C++ does not have flexible array members.

    Contrarily to your title, your SIZE is not (according to the standard specifications) a "compile-time-known array size" (but becomes one when optimized by gcc or g++)

    An even better reason to use heap allocation (that is either std::vector in C++, or a pointer to some struct ending with a flexible array member, or a pointer to an array, in C) is that in practice you'll want to have a much bigger A (e.g. #define A 100000 ...) and it is not reasonable to allocate large data structures on the call stack (often limited to a megabyte or a few of them on current machines and operating systems).