c++ - C/C++ 中的 const 数组和静态 const 数组有什么区别

标签 c++ c arrays static constants

在 Visual Studio 2015(Win7、x64、Debug 配置)中编译以下代码需要 非常、非常、非常很长时间(即超过 10 分钟)

double tfuuuuuuu(int Ind)
{
  const double Arr[600 * 258] = {3.5453, 45.234234234, 234234.234,// extends to 258 values for each line
                                // 599 lines here.....
                                };                     
  return Arr[Ind];
}

但是当我添加 static 关键字时,编译需要半秒钟

double tfuuuuuuu(int Ind)
{
  static const double Arr[600 * 258] = {3.5453, 45.234234234, 234234.234,// extends to 258 values for each line
                                // 599 lines here.....
                                };                     
  return Arr[Ind];
}

我知道 static 意味着变量将在调用之间保持其值,但是如果数组是 const 无论如何如果我添加 会有什么区别静态?为什么编译时间变化如此之大?

编辑:

实际代码可见here ,(编译处于 Debug模式)

最佳答案

声明为static 的局部变量具有整个运行程序的生命周期,通常存储在数据段中。编译器通过在其中包含值的部分来实现这一点。

未声明为静态的局部变量通常存在于堆栈中,并且必须在每次进入变量范围时进行初始化。

查看 static 案例的程序集,MSVC 2015 输出以下内容:

; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.24215.1 

    TITLE   MyLBP.c
    .686P
    .XMM
    include listing.inc
    .model  flat

INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES

CONST   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 $+1036128
CONST   ENDS
PUBLIC  _tfuuuuuuu
EXTRN   __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   ENDS
END

虽然 gcc 4.8.5 输出以下内容:

    .file   "MyLBP.c"
    .text
    .globl  tfuuuuuuu
    .type   tfuuuuuuu, @function
tfuuuuuuu:
.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, 1238400
Arr.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

因此,两者都全局定义数据并直接引用该全局数组。

现在让我们看看非静态代码。 VSMC2015 第一:

; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.24215.1 

    TITLE   MyLBP.c
    .686P
    .XMM
    include listing.inc
    .model  flat

INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES

PUBLIC  _tfuuuuuuu
PUBLIC  __real@3e45798ee2308c3a
PUBLIC  __real@3f40e1cf9350aa3c
PUBLIC  __real@3f43b1f90beff84b
PUBLIC  __real@3f4c6220dc6e8066
PUBLIC  __real@3f4ea4c648794089
PUBLIC  __real@3f50023666188dc0
PUBLIC  __real@3f53957e56f300e9
PUBLIC  __real@3f55235d7d33b25f
PUBLIC  __real@3f5828f66e5bd33a
PUBLIC  __real@3f5c044284dfce31
PUBLIC  __real@3f5c87c05341c674
...
EXTRN   @__security_check_cookie@4:PROC
EXTRN   __chkstk:PROC
EXTRN   _memset:PROC
EXTRN   ___security_cookie:DWORD
EXTRN   __fltused:DWORD
;   COMDAT __real@bff0000000000000
CONST   SEGMENT
__real@bff0000000000000 DQ 0bff0000000000000r   ; -1
CONST   ENDS
;   COMDAT __real@bfefffffdfc9a9ad
CONST   SEGMENT
__real@bfefffffdfc9a9ad DQ 0bfefffffdfc9a9adr   ; -1
CONST   ENDS
;   COMDAT __real@bfefffffbf935359
CONST   SEGMENT
__real@bfefffffbf935359 DQ 0bfefffffbf935359r   ; -1
CONST   ENDS
;   COMDAT __real@bfefffff9f5cfd06
CONST   SEGMENT
__real@bfefffff9f5cfd06 DQ 0bfefffff9f5cfd06r   ; -1
CONST   ENDS
;   COMDAT __real@bfefffff7f26a6b3
CONST   SEGMENT
__real@bfefffff7f26a6b3 DQ 0bfefffff7f26a6b3r   ; -1
CONST   ENDS
;   COMDAT __real@bfefffff5ef05060
CONST   SEGMENT
__real@bfefffff5ef05060 DQ 0bfefffff5ef05060r   ; -1
CONST   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   ENDS
END

初始化程序仍然全局存储。但是,请注意每个值是如何在内部命名的,并且 为数组中的每个值生成了 2 个移动指令。创建这些名称和明确的 Action 是生成代码需要这么长时间的原因。

现在是 gcc 4.8.5 版本:

    .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, @function
tfuuuuuuu:
.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

gcc 不是生成显式指令来复制每个值,而是调用 memcpy 将值从全局数据复制到本地数组中,因此生成初始化代码要快得多。

所以这个故事的寓意是,MSVC 在初始化局部变量方面效率很低。

此外,正如评论中所述,这是一个 confirmed bug这将在 VS 2019 中修复。

关于c++ - C/C++ 中的 const 数组和静态 const 数组有什么区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56057662/

相关文章:

c++ - yaml-cpp 甚至为 const 节点修改底层容器?

c - 使用 MPI 发送 glib 哈希表

c - 指向 C 中二维数组中的数组

c++ - 在opencv中找到物体的凸包?

c++ - C++中的复杂数组初始化

c++ - 字符串数组长度 C++ 问题?

c++ - 创建类的可变包装器

c++ - Qt Creator 仅显示 android 作为可用工具包

c++ - 在 OpenCV 上使用 GPU 时如何确定线程数?

python - C 或 Python 用于创建套接字连接的性能和可伸缩性?