9

Let me exemplify this,

int a = 100;
int b = a;

int main(int argc, char **argv, char ** env)
{
  printf("The value of b=%d\r\n",b);

  return 0;
}

Now, I get the compilation error as expected.

[joshis1@localhost global_var]$ gcc global_var.c -o global_var.out
global_var.c:4:1: error: initializer element is not constant
 int b = a;
 ^

What I want to learn here is why do I get the error? why compiler restricts this operation. I understand that initialized global variables are stored in Data segments. The compiler could have first resolved the value of a,and then could have assigned the same value to b. Why it lacks this feature? Is it complex for compiler to do? Is there any rationale behind this functionality or just a pitfall of C?

jruizaranguren
  • 12,679
  • 7
  • 55
  • 73
dexterous
  • 6,422
  • 12
  • 51
  • 99
  • 5
    Error message is your answer. – Grijesh Chauhan Oct 02 '13 at 05:11
  • 2
    Well, how the compiler is supposed to run executable code (evaluate an expression) outside a function? C++ compilers can do this, but that usually works by creating some helper functions that run before `main()`, and that is ugly, hackish, implicit and hidden, so your program doesn't really do what you think it does, and C programers don't like that. (C++ programmers do.) –  Oct 02 '13 at 05:11
  • @Grijesh: I was looking for the reason behind it, the compiler understanding. – dexterous Oct 02 '13 at 05:11
  • 2
    The compiler is a compiler not an interpreter – Jim Rhodes Oct 02 '13 at 05:11
  • 5
    @GrijeshChauhan Does not apply. OP knows why he gets the error, he's looking for the design decision behind it. –  Oct 02 '13 at 05:12
  • Same reason why you couldn't use even const-specified variable as array size specifier (well, technically in some variations you could, but it would be variable-length array anyway) – keltar Oct 02 '13 at 05:14
  • @keltar VLAs have been in the C standard for 14 years. And that has a reason too: if you have a VLA, you can reserve stack space for it by simply adjusting the stack pointer by an amount specified at run-time (think `sub esp, eax` or whatever). That is inside a function, so it's deterministic when it runs, thus it is easy to decide where to out that code. Goval variables with non-const initializers are much harder to implement. –  Oct 02 '13 at 05:18
  • @H2CO3 you've missed my point (and maybe OP's one too). From what i see, OP never wanted non-const initializers, just non-const expression to be resolved as const during compilation phase. Compiler don't do this in this case, just like it never does. VLA is just another example where it isn't happening. And yes, i know what VLA is, how it works, and how long it was present in standard, yet currently it's removed as mandatory feature and left as extension. – keltar Oct 02 '13 at 05:22
  • @keltar more precisely, as an optional, but standard, feature. No, I am not missing the point. Read my comment directed to OP, you'll see why what you wrote is a non sequitur. VLAs have a completely different implementation problem from that of non-const initializers, and that's exactly why duscussing them is not relevant to this question. "OP never wanted non-const initializers, just non-const expression to be resolved as const during compilation phase." - nope. What do you call `int a = b;`? Is it initialization? Yes it is. Is the initializer expression a constexpr? No, it isn't. –  Oct 02 '13 at 05:24
  • @H2CO3 i've red all comments quite carefully, and i still do believe you and OP talking about completely different things. You talk runtime, OP - compile time. And maybe you could notice i haven't brought VLA to discussion (because it completely runtime-related, while question is not), initially i was talking about standard const-size arrays that cannot be used if size specifier is not purely compile time constant. – keltar Oct 02 '13 at 05:31
  • Yes, I am not discussing the Variable length array here, I just wanted to know how to get this int a = b; working at compilation time. I liked the assembly answer sub esp, eax .. but all these will be executed at run-time, how can I force compiler to make this work? Off course, I don't want to write compiler. I know that I am expecting something without modifying the compiler. I wonder why C compiler ignored this. – dexterous Oct 02 '13 at 05:33
  • @keltar **No, I am not talking about runtime.** I am discussing a possible implementation of **compiling** OP's code. Compiling, at compilation-time, of course. –  Oct 02 '13 at 05:38
  • @SHREYASJOSHI well, compiler never does that kind of things. Maybe someone willing to dig up standard could find exact quote, but from what i see - it just forbidden (probably by standard) to interpret potentially non-const expressions as constants. And, please take a note that compiler's authors can't handle every possible case (not related to this question - just for the future). – keltar Oct 02 '13 at 05:39
  • @SHREYASJOSHI I know you are not interested in VLAs, that's why I suggested we don't talk about them anymore. And there's no way to make a C compiler compile your code, because it's not valid C. –  Oct 02 '13 at 05:39
  • @alk: This is not duplicate, I know the issue but wanted to understand the compiler internals behind it, and also some ways to fix it without changing my code. I understand not possible, also the reason behind the restriction. I liked the second answer here that then C has to support even pow, addition at compile time. That makes sense, but not happy, atleast they could have supported such a simple assignment expression. – dexterous Oct 02 '13 at 05:45
  • 1
    @H2CO3 evaluate initialiser as it's value known, and store it in data as b's initialiser - i believe that's what Joshi wanted. But something prevents this (and i rejoice that it is). Runtime evaluation is out-of-question. I'm done with these annoying and loudly misunderstandings. – keltar Oct 02 '13 at 05:45
  • http://stackoverflow.com/a/3025106/1162141 points out that `const int` is not a "constant" in C, only things like 1, 1.0, 'a', "string", ... – technosaurus Oct 02 '13 at 05:51
  • @technosaurus It is a constant (well, that depends on how you define "constant" -- precisely, it is a `const`-qualified object), but it is neither a literal, nor a constexpr, –  Oct 02 '13 at 06:13

4 Answers4

17

The official documentation, taken from line 1644, 6.7.8 Initialization, says:

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

Why the rule exists is a more difficult question - perhaps as you suggest it is difficult for the compiler to do. In C++ such an expression is valid, but global initialiser may invoke constructors, etc, whereas for C, to keep things compact, globals are evaluated at the compile phase. int b = a; is evaluable at compile time, but what about int b = a + c;? int b = pow(a, 2);? Where would you stop? C decides that not allowing you to start is the best solution.

Ken Y-N
  • 14,644
  • 21
  • 71
  • 114
  • 5
    It is more than just "hard" for a compiler to do. It touches upon the halting problem. If static initializes were not constants, then to evaluate them at compile time would require interpreting the parsed code, which might not terminate. On the other hand, if static initializes were not constants, but they were determined at run time, then you would need a very complicated mechanism to detect and order all the static initialization. C++ approaches that, but it is far from straightforward. Basically order between initialization blocks isn't guaranteed. – Edwin Buck Oct 02 '13 at 06:02
  • 1
    @EdwinBuck: Of course that's an imaginary problem that keeps getting repeated like if it was a law of Nature. Nobody expects a compiler to terminate given bad code. That's never a problem. Try giving a C++ compiler some useful metaprograms and see what happens. On the other hand, if it could interpret imperative C++ instead of interpreting a hard-to-use functional metalanguage, it would be done in a millisecond, having used negligible amount of heap. Sometimes being practical completely and utterly trumps some theoretical purity. – Kuba hasn't forgotten Monica Oct 02 '13 at 13:11
  • @KubaOber The halting problem is far from imaginary. Compilers are expected to terminate, but one could easily fail it with: `#define a b+1`, `#define b a+1`, if true interpretation was desired. The only part that requires one's imagination is in imagining how to abuse it in interesting ways. – Edwin Buck Oct 02 '13 at 20:07
5

From your comment:

...how can I force compiler to make this work?

Well you can't make the compiler accept what you have but you can accomplish your goal by defining the value you want to assign to both variables.

#define INITIAL_VALUE_FOR_A 100

int a = INITIAL_VALUE_FOR_A;
int b = INITIAL_VALUE_FOR_A;

Now if you need to change the initial value, you only need to change it in one place;

Jim Rhodes
  • 5,021
  • 4
  • 25
  • 38
  • Sorry, this is what I am not looking for. – dexterous Oct 02 '13 at 05:55
  • 4
    @SHREYASJOSHI if you are not satisfied with "you cannot; the language forbids this" then you are using the wrong language. Think about which part of the equation is under your control. – tripleee Oct 02 '13 at 06:26
1

C is portable to very simple, small machines. Evaluating expressions that aren't constant requires runtime code, in a function. In embedded programming you might not want any functions (or code) that you did not explicitly program.

Your compiler probably will evaluate the initializer as a language extension, if configured with different options. If that fails, you could try C++ (even just the C-like subset) or another language that does more things you like :v) .

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
0

Others have stated why initializers in general can't be allowed to be arbitrary expressions.

The way to "force the compiler to accept the code" is to change the code. The canonical way to perform arbitrary initialization of file scope, external linkage (vulgo: "global") variables is to call an initialization function at the start of main() that does all the initialization in the sequence you need:

#include <stdio.h>
int a;
int b;

void init(void)
{
     a = 100;
     b = a;
}

int main(int argc, char **argv)
{
     init();
     printf("The value of b=%d\n", b);

     return 0;
}
Jens
  • 69,818
  • 15
  • 125
  • 179