Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Arithmetic operations between constants

Consider this code;

#define A 5
#define B 3

int difference = A - B;

does value of "difference" is hardcoded as "2" in compile time, or does it get calculated on runtime?

like image 622
yasar Avatar asked Mar 09 '12 01:03

yasar


2 Answers

The A and B macros are a bit of a distraction. This:

#define A 5
#define B 3

int difference = A - B;

is exactly equivalent to this:

int difference = 5 - 3;

so let's discuss the latter.

5 - 3 is a constant expression, which is an expression that "can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be". It's also an *integer constant expression". For example, a case label must be an integer constant expression, so you could write either this:

switch (foo) {
    case 2: /* this is a constant */
    ...
}

or this:

switch (foo) {
    case 5 - 3: /* this is a constant expression */
    ...
}

But note that the definition says that it can be evaluated during translation, not that it must be. There are some contexts that require constant expressions, and in those contexts the expression must be evaluated at compile time.

But assuming that difference is declared inside some function, the initializer is not one of those contexts.

Any compiler worth what you pay for it (even if it's free) will reduce 5 - 3 to 2 at compile time, and generate code that stores the value 2 in difference. But it's not required to do so. The C standard specifies the behavior of programs; it doesn't specify how that behavior must be implemented. But it's safe to assume that whatever compiler you're using will replace 5 - 3 by 2.

Even if you write:

int difference = 2;

a compiler could legally generate code that loads the value 5 into a register, subtracts 3 from it, and stores the contents of the register into difference. That would be a silly thing to do, but the language standard doesn't exclude it.

As long as the final result is that difference has the value 2, the language standard doesn't care how it's done.

On the other hand, if you write:

switch (foo) {
    case 5 - 3: /* ... */
    case 2:     /* ... */
}

then the compiler must compute the result so it can diagnose the error (you can't have two case labels with the same value.

Finally, if you define difference at file scope (outside any function), then the initial value does have to be constant. But the real distinction in that case is not whether 5 - 3 will be evaluated at compile time, it's whether you're allowed to use a non-constant expression.

Reference: The latest draft of the 2011 C standard is N1570 (large PDF); constant expressions are discussed in section 6.6.

like image 135
Keith Thompson Avatar answered Oct 18 '22 05:10

Keith Thompson


The standard does not specify this sort of thing. It says nothing about potential optimizations like this (and for good reason. A standard defines semantics, not implementation).

Why not look at the disassembly for your compiler? That will give you a definitive answer.

...

So let's do that.

Here is the output from VC++ 10:

#include <iostream>

#define A 5
#define B 3

int main() {
    int x = A - B;
    std::cout << x;  // make sure the compiler doesn't toss it away
010A1000  mov         ecx,dword ptr [__imp_std::cout (10A2048h)]  
010A1006  push        2  
010A1008  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (10A2044h)]  
    return 0;
010A100E  xor         eax,eax  

As you can see, it just replaced the occurrence of x with a static value of 2 and pushed it onto the stack for the call to cout. It did not evaluate the expression at runtime.

like image 28
Ed S. Avatar answered Oct 18 '22 05:10

Ed S.