Does the compiler optimize references to constant variables?

Question

When it comes to the C and C++ languages, does the compiler optimize references to constant variables so that the program automatically knows what values are being referred to, instead of having to peek at the memory locations of the constant variables? When it comes to arrays, does it depend on whether the index value to point at in the array is a constant at compile time?

For instance, take a look at this code:

int main(void) {
    1:  char tesst[3] = {'1', '3', '7'};
    2:  char erm = tesst[1];
}

Does the compiler "change" line 2 to "char erm = '3'" at compile time?


Show source
| c   | c++   | performance   | memory-management   | optimization   2016-12-19 15:12 3 Answers

Answers ( 3 )

  1. 2016-12-19 15:12

    Depends on the compiler version, optimization options used and many other things. If you want to make sure that the const variables are optimized and if they are compile time constants you can use something like constexpr in c++. It is guaranteed to be evaluated at compile time unlike normal const variables.

    Edit: constexpr may be evaluated at compile time or runtime. To guarantee compile-time evaluation, we must either use it where a constant expression is required (e.g., as an array bound or as a case label) or use it to initialize a constexpr. so in this case

    constexpr char tesst[3] = {'1','3','7'};
    constexpr char erm = tesst[1];
    

    would lead to compile time evaluation. Nice read at https://isocpp.org/blog/2013/01/when-does-a-constexpr-function-get-evaluated-at-compile-time-stackoverflow

  2. 2016-12-19 15:12

    It mostly depends on the level of optimization and which compiler you are using.

    With maximum optimizations, the compiler will indeed probably just replace your whole code with char erm = '3';. GCC -O3 does this anyway.

    But then of course it depends on what you do with that variable. The compiler might not even allocate the variable, but just use the raw number in the operation where the variable occurs.

  3. 2016-12-19 15:12

    I personally would expect the posted code to turn into "nothing", since neither variable is actually used, and thus can be removed.

    But yes, modern compilers (gcc, clang, msvc, etc) should be able to replace that reference to the alternative with it's constant value [as long as the compiler can be reasonably sure that the content of tesst isn't being changed - if you pass tesst into a function, even if its as a const reference, and the compiler doesn't actually know the function is NOT changing that, it will assume that it does and load the value].

    Compiling this using clang -O1 opts.c -S:

    #include <stdio.h>
    
    int main()
    {
        char tesst[3] = {'1', '3', '7'};
        char erm = tesst[1];
    
        printf("%d\n", erm);
    }
    

    produces:

    ...
    
    main:
        pushq   %rax
    .Ltmp0:
        movl    $.L.str, %edi
        movl    $51, %esi
        xorl    %eax, %eax
        callq   printf
        xorl    %eax, %eax
        popq    %rcx
        retq
    
     ...
    

    So, the same as printf("%d\n", '3');.

    [I'm using C rather than C++ because it would be about 50 lines of assembler if I used cout, as everything gets inlined]

    I expect gcc and msvc to make a similar optimisation (tested gcc -O1 -S and it gives exactly the same code, aside from some symbol names are subtly different)

    And to illustrate that "it may not do it if you call a function":

    #include <stdio.h>
    
    extern void blah(const char* x);
    
    int main()
    {
        char tesst[3] = {'1', '3', '7'};
        blah(tesst);
        char erm = tesst[1];
    
        printf("%d\n", erm);
    }
    
    
    main:                                   # @main
        pushq   %rax
        movb    $55, 6(%rsp)
        movw    $13105, 4(%rsp)         # imm = 0x3331
        leaq    4(%rsp), %rdi
        callq   blah
        movsbl  5(%rsp), %esi
        movl    $.L.str, %edi
        xorl    %eax, %eax
        callq   printf
        xorl    %eax, %eax
        popq    %rcx
        retq
    

    Now, it fetches the value from inside tesst.

◀ Go back