Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't a static initialization expression in C use an element of a constant array?

The following (admittedly contrived) C program fails to compile:

int main() {
const int array[] = {1,2,3};
static int x = array[1];
}

When compiling the above C source file with gcc (or Microsoft's CL.EXE), I get the following error:

error: initializer element is not constant
static int x = array[1];
               ^

Such simple and intuitive syntax is certainly useful, so this seems like it should be legal, but clearly it is not. Surely I am not the only person frustrated with this apparently silly limitation. I don't understand why this is disallowed-- what problem is the C language trying to avoid by making this useful syntax illegal?

It seems like it may have something to do with the way a compiler generates the assembly code for the initialization, because if you remove the "static" keyword (such that the variable "x" is on the stack), then it compiles fine.

However, another strange thing is that it compiles fine in C++ (even with the static keyword), but not in C. So, the C++ compiler seems capable of generating the necessary assembly code to perform such an initialization.

Edit: Credit to Davislor-- in an attempt to appease the SO powers-that-be, I would seek following types of factual information to answer the question:

  1. Is there any legacy code that supporting these semantics would break?

  2. Have these semantics ever been formally proposed to the standards committee?

  3. Has anyone ever given a reason for rejecting the allowance of these semantics?

like image 356
deltamind106 Avatar asked Jun 27 '18 14:06

deltamind106


2 Answers

Objects with static storage duration (read: variables declared at file scope or with the static keyword) must be initialized by compile time constants.

Section 6.7.9 of the C standard regarding Initialization states:

4 All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.

Section 6.6 regarding Constant Expressions states:

7 More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:

  • an arithmetic constant expression,
  • a null pointer constant,
  • an address constant, or
  • an address constant for a complete object type plus or minus an integer constant expression.

8 An arithmetic constant expression shall have arithmetic type and shall only have operands that are integer constants, floating constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and _Alignof expressions. Cast operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic types, except as part of an operand to a sizeof or _Alignof operator.

9 An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type. The array-subscript [] and member-access . and -> operators, the address & and indirection * unary operators, and pointer casts may be used in the creation of an address constant, but the value of an object shall not be accessed by use of these operators.

By the above definition, a const variable does not qualify as a constant expression, so it can't be used to initialize a static object. C++ on the other had does treat const variables as true constants and thus allows them to initialize static objects.

like image 166
dbush Avatar answered Nov 15 '22 06:11

dbush


If the C standard allowed this, then compilers would have to know what is in arrays. That is, the compiler would have to have a compile-time model of the array contents. Without this, the compiler has a small amount of work to do for each array: It needs to know its name and type (including its size), and a few other details such as its linkage and storage duration. But, where the initialization of the array is specified in the code, the compiler can just write the relevant information to the object file it is growing and then forget about it.

If the compiler had to be able to fetch values out of the array at compile time, it would have to remember that data. As arrays can be very large, that imposes a burden on the C compiler that the committee likely did not desire, as C is intended to operate in a wide variety of environments, including those with constrained resources.

The C++ committee made a different decision, and C++ is much more burdensome to translate.

like image 26
Eric Postpischil Avatar answered Nov 15 '22 07:11

Eric Postpischil