What's the difference between const array and static const array in C/C++ What's the difference between const array and static const array in C/C++ c c

What's the difference between const array and static const array in C/C++


A local variable declared as static has a lifetime of the entire running program, and is typically stored in the data segment. Compilers implement this by having a section that has the values in them.

Local variables not declared as static typically live on the stack and must be initialized every time the variable's scope is entered.

Looking at the assembly for the static case, MSVC 2015 outputs the following:

; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.24215.1     TITLE   MyLBP.c    .686P    .XMM    include listing.inc    .model  flatINCLUDELIB LIBCMTINCLUDELIB OLDNAMESCONST   SEGMENT?Arr@?1??tfuuuuuuu@@9@9 DQ 04060c00000000000r   ; 134   ; `tfuuuuuuu'::`2'::Arr    DQ  03fe15efd20a7955br      ; 0.542845    DQ  03fdf59701e4b19afr      ; 0.489834    DQ  0bfd8e38e9ab7fcb1r      ; -0.388889    DQ  0bfe59f22c01e68a1r      ; -0.675676    DQ  0bfeb13b15d5aa410r      ; -0.846154    DQ  0bfe2c2355f07776er      ; -0.586207    DQ  03fefffffbf935359r      ; 1    ...    ORG $+1036128CONST   ENDSPUBLIC  _tfuuuuuuuEXTRN   __fltused:DWORD; Function compile flags: /Odtp_TEXT   SEGMENT_Ind$ = 8                       ; size = 4_tfuuuuuuu PROC; File c:\users\dennis bush\documents\x2.c; Line 4    push    ebp    mov ebp, esp; Line 106    mov eax, DWORD PTR _Ind$[ebp]    fld QWORD PTR ?Arr@?1??tfuuuuuuu@@9@9[eax*8]; Line 107    pop ebp    ret 0_tfuuuuuuu ENDP_TEXT   ENDSEND

While gcc 4.8.5 outputs the following:

    .file   "MyLBP.c"    .text    .globl  tfuuuuuuu    .type   tfuuuuuuu, @functiontfuuuuuuu:.LFB0:    .cfi_startproc    pushq   %rbp    .cfi_def_cfa_offset 16    .cfi_offset 6, -16    movq    %rsp, %rbp    .cfi_def_cfa_register 6    movl    %edi, -4(%rbp)    movl    -4(%rbp), %eax    cltq    movq    Arr.1724(,%rax,8), %rax    movq    %rax, -16(%rbp)    movsd   -16(%rbp), %xmm0    popq    %rbp    .cfi_def_cfa 7, 8    ret    .cfi_endproc.LFE0:    .size   tfuuuuuuu, .-tfuuuuuuu    .section    .rodata    .align 32    .type   Arr.1724, @object    .size   Arr.1724, 1238400Arr.1724:    .long   0    .long   1080082432    .long   547853659    .long   1071734525    .long   508238255    .long   1071602032    .long   2595749041    .long   -1076305010    .long   3223218337    ...    .ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-16)"    .section    .note.GNU-stack,"",@progbits

So both define the data globally and reference that global array directly.

Now lets look at the non-static code. First for VSMC2015:

; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.24215.1     TITLE   MyLBP.c    .686P    .XMM    include listing.inc    .model  flatINCLUDELIB LIBCMTINCLUDELIB OLDNAMESPUBLIC  _tfuuuuuuuPUBLIC  __real@3e45798ee2308c3aPUBLIC  __real@3f40e1cf9350aa3cPUBLIC  __real@3f43b1f90beff84bPUBLIC  __real@3f4c6220dc6e8066PUBLIC  __real@3f4ea4c648794089PUBLIC  __real@3f50023666188dc0PUBLIC  __real@3f53957e56f300e9PUBLIC  __real@3f55235d7d33b25fPUBLIC  __real@3f5828f66e5bd33aPUBLIC  __real@3f5c044284dfce31PUBLIC  __real@3f5c87c05341c674...EXTRN   @__security_check_cookie@4:PROCEXTRN   __chkstk:PROCEXTRN   _memset:PROCEXTRN   ___security_cookie:DWORDEXTRN   __fltused:DWORD;   COMDAT __real@bff0000000000000CONST   SEGMENT__real@bff0000000000000 DQ 0bff0000000000000r   ; -1CONST   ENDS;   COMDAT __real@bfefffffdfc9a9adCONST   SEGMENT__real@bfefffffdfc9a9ad DQ 0bfefffffdfc9a9adr   ; -1CONST   ENDS;   COMDAT __real@bfefffffbf935359CONST   SEGMENT__real@bfefffffbf935359 DQ 0bfefffffbf935359r   ; -1CONST   ENDS;   COMDAT __real@bfefffff9f5cfd06CONST   SEGMENT__real@bfefffff9f5cfd06 DQ 0bfefffff9f5cfd06r   ; -1CONST   ENDS;   COMDAT __real@bfefffff7f26a6b3CONST   SEGMENT__real@bfefffff7f26a6b3 DQ 0bfefffff7f26a6b3r   ; -1CONST   ENDS;   COMDAT __real@bfefffff5ef05060CONST   SEGMENT__real@bfefffff5ef05060 DQ 0bfefffff5ef05060r   ; -1CONST   ENDS...; Function compile flags: /Odtp_TEXT   SEGMENT_Arr$ = -1238404                    ; size = 1238400__$ArrayPad$ = -4                   ; size = 4_Ind$ = 8                       ; size = 4_tfuuuuuuu PROC; File c:\users\dennis bush\documents\x2.c; Line 4    push    ebp    mov ebp, esp    mov eax, 1238404                ; 0012e584H    call    __chkstk    mov eax, DWORD PTR ___security_cookie    xor eax, ebp    mov DWORD PTR __$ArrayPad$[ebp], eax; Line 5    movsd   xmm0, QWORD PTR __real@4060c00000000000    movsd   QWORD PTR _Arr$[ebp], xmm0    movsd   xmm0, QWORD PTR __real@3fe15efd20a7955b    movsd   QWORD PTR _Arr$[ebp+8], xmm0    movsd   xmm0, QWORD PTR __real@3fdf59701e4b19af    movsd   QWORD PTR _Arr$[ebp+16], xmm0    movsd   xmm0, QWORD PTR __real@bfd8e38e9ab7fcb1    movsd   QWORD PTR _Arr$[ebp+24], xmm0    movsd   xmm0, QWORD PTR __real@bfe59f22c01e68a1    movsd   QWORD PTR _Arr$[ebp+32], xmm0    movsd   xmm0, QWORD PTR __real@bfeb13b15d5aa410    movsd   QWORD PTR _Arr$[ebp+40], xmm0    movsd   xmm0, QWORD PTR __real@bfe2c2355f07776e    movsd   QWORD PTR _Arr$[ebp+48], xmm0    ...    push    1036128                 ; 000fcf60H    push    0    lea eax, DWORD PTR _Arr$[ebp+202272]    push    eax    call    _memset    add esp, 12                 ; 0000000cH; Line 106    mov ecx, DWORD PTR _Ind$[ebp]    fld QWORD PTR _Arr$[ebp+ecx*8]; Line 107    mov ecx, DWORD PTR __$ArrayPad$[ebp]    xor ecx, ebp    call    @__security_check_cookie@4    mov esp, ebp    pop ebp    ret 0_tfuuuuuuu ENDP_TEXT   ENDSEND

The initializers are still stored globally. However, notice how each value is given a name internally and that 2 move instruction are generated for each value in the array. Creating those names and the explicit moves is why it takes so long to generate the code.

And now the gcc 4.8.5 version:

    .file   "MyLBP.c"    .section    .rodata    .align 32.LC0:    .long   0    .long   1080082432    .long   547853659    .long   1071734525    .long   508238255    .long   1071602032    .long   2595749041    .long   -1076305010    .long   3223218337    .long   -1075470558    ...    .text    .globl  tfuuuuuuu    .type   tfuuuuuuu, @functiontfuuuuuuu:.LFB0:    .cfi_startproc    pushq   %rbp    .cfi_def_cfa_offset 16    .cfi_offset 6, -16    movq    %rsp, %rbp    .cfi_def_cfa_register 6    subq    $1238416, %rsp    movl    %edi, -1238404(%rbp)    leaq    -1238400(%rbp), %rax    movl    $.LC0, %ecx    movl    $1238400, %edx    movq    %rcx, %rsi    movq    %rax, %rdi    call    memcpy                       ;   <--------------  call to memcpy    movl    -1238404(%rbp), %eax    cltq    movq    -1238400(%rbp,%rax,8), %rax    movq    %rax, -1238416(%rbp)    movsd   -1238416(%rbp), %xmm0    leave    .cfi_def_cfa 7, 8    ret    .cfi_endproc.LFE0:    .size   tfuuuuuuu, .-tfuuuuuuu    .ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-16)"    .section    .note.GNU-stack,"",@progbits

Rather than generating explicit instructions to copy each value, gcc just calls memcpy to copy the values from global data into the local array, so generating the initialization code is much faster.

So the moral of the story is that MSVC is being very inefficient in how it initializes local variables.

Also, as noted in the comments, this is a confirmed bug which is due to be fixed in VS 2019.


const or not, a non-static function local must be constructed whenever the function is entered and the declaration is reached. Your compiler is spending time generating the code to perform that action at runtime, which may be arduous when the initialiser is super long.

By constrast, a static of this form can just have its initial value plonked into the executable somewhere, with no runtime spin-up needed.

It does sound like a bit of a QoI issue with your compiler if you're really seeing a big difference in build times (particularly as 1.2MB isn't that much data), but the two pieces of code are fundamentally different and huge initialisers for things destined to live "on the stack" are typically something to avoid.