开发者社区> 问答> 正文

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

在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仍然存在,那么如果添加它会产生什么区别static呢?为什么编译时间会发生如此大的变化? 问题来源于stack overflow

展开
收起
保持可爱mmm 2020-02-09 13:50:02 540 0
1 条回答
写回答
取消 提交回答
  • 声明为的局部变量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 初始化程序仍存储在全局中。但是,请注意如何在内部给每个值一个名称,并为array中的每个值生成2条移动指令。创建这些名称和明确的动作是为什么生成代码需要这么长时间的原因。

    现在是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在初始化局部变量方面效率非常低下。

    另外,如评论中所述,这是已确认的错误,将在VS 2019中修复。

    分享

    2020-02-09 13:50:21
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
使用C++11开发PHP7扩展 立即下载
GPON Class C++ SFP O;T Transce 立即下载
GPON Class C++ SFP OLT Transce 立即下载