在initializer_list后面存储“隐藏数组”

时间:2012-08-29 15:08:30

标签: c++ visual-c++ gcc c++11 clang

在C ++ 11标准中,有一个关于支持统一初始化的数组的说明:

  

如果可以如此分配具有相同初始化程序的显式数组,则实现可以自由地在只读存储器中分配数组。

GCC / Clang / VS会利用这个吗?或者使用此功能的每个初始化是否受到堆栈上的其他数据的影响,以及此隐藏阵列的额外初始化时间?

例如,给出以下示例:

void function()
{
    std::vector<std::string> values = { "First", "Second" };
...

上面提到的每个编译器是否将后备数组存储在与声明为static const的变量相同的内存中的统一初始化中?当调用函数或应用程序初始化时,每个编译器是否会初始化后备数组? (我不是在谈论将要创建的std::initializer_list<std::string>,而是它所指的“隐藏数组”。

2 个答案:

答案 0 :(得分:2)

这是我尝试至少回答GCC的问题。我对gcc的汇编输出的理解并不是很好,所以请在必要时更正。

使用initializer_test.cpp

#include <vector>

int main()
{
    std::vector<long> values = { 123456, 123457, 123458 };
    return 0;
}

使用以下命令行使用gcc v4.6.3进行编译:

g++ -Wa,-adhln -g initializer_test.cpp -masm=intel -std=c++0x -fverbose-asm | c++filt | view -

我得到以下输出(减少到希望相关的位):

   5:initializer_test.cpp ****     std::vector<long> values = { 123456, 123457, 123458 };
 100                    .loc 2 5 0
 101 0009 488D45EF      lea rax, [rbp-17]   # tmp62,
 102 000d 4889C7        mov rdi, rax    #, tmp62
 103                    .cfi_offset 3, -24
 104 0010 E8000000      call    std::allocator<long>::allocator()   #
 104      00
 105 0015 488D45D0      lea rax, [rbp-48]   # tmp63,
 106 0019 BA030000      mov edx, 3  #,                      <-- Parameter 3
 106      00
 107 001e BE000000      mov esi, OFFSET FLAT:._42   #,      <-- Parameter 2
 107      00
 108 0023 4889C7        mov rdi, rax    #, tmp63            <-- Parameter 1
 109 0026 E8000000      call    std::initializer_list<long>::initializer_list(long const*, unsigned long)   #
 109      00
 110 002b 488D4DEF      lea rcx, [rbp-17]   # tmp64,
 111 002f 488B75D0      mov rsi, QWORD PTR [rbp-48] # tmp65, D.10602
 112 0033 488B55D8      mov rdx, QWORD PTR [rbp-40] # tmp66, D.10602
 113 0037 488D45B0      lea rax, [rbp-80]   # tmp67,
 114 003b 4889C7        mov rdi, rax    #, tmp67
 115                .LEHB0:
 116 003e E8000000      call    std::vector<long, std::allocator<long> >::vector(std::initializer_list<long>, std::allocator<long> const&)  #
 116      00
 117                .LEHE0:
 118                    .loc 2 5 0 is_stmt 0 discriminator 1
 119 0043 488D45EF      lea rax, [rbp-17]   # tmp68,
 120 0047 4889C7        mov rdi, rax    #, tmp68
 121 004a E8000000      call    std::allocator<long>::~allocator()  #

 1678                   .section    .rodata
 1679 0002 00000000         .align 16
 1679      00000000 
 1679      00000000 
 1679      0000
 1682               ._42:
 1683 0010 40E20100         .quad   123456
 1683      00000000 
 1684 0018 41E20100         .quad   123457
 1684      00000000 
 1685 0020 42E20100         .quad   123458
 1685      00000000 

现在,如果我在x86-64 System V AMD64 ABI调用约定(我已注释到代码清单的参数)的上下文中正确理解了第109行的调用,那么显示支持数组存储在.rodata中,我将其视为与静态const数据相同的内存。至少对于gcc 4.6而言。

执行类似的测试,但启用了优化功能(-O2),似乎initializer_list已经过优化:

  70                    .file 2 "/usr/include/c++/4.6/ext/new_allocator.h"
  71                    .loc 2 92 0
  72 0004 BF180000      mov edi, 24 #,
  72      00
  73 0009 E8000000      call    operator new(unsigned long) #
  73      00
  74                .LVL1:
  75                    .file 3 "/usr/include/c++/4.6/bits/stl_algobase.h"
  76                    .loc 3 366 0
  77 000e 488B1500      mov rdx, QWORD PTR ._42[rip]    # ._42, ._42
  77      000000
  90                    .file 4 "/usr/include/c++/4.6/bits/stl_vector.h"
  91                    .loc 4 155 0
  92 0015 4885C0        test    rax, rax    # D.11805
 105                    .loc 3 366 0
 106 0018 488910        mov QWORD PTR [rax], rdx    #* D.11805, ._42
 107 001b 488B1500      mov rdx, QWORD PTR ._42[rip+8]  # ._42, ._42
 107      000000
 108 0022 48895008      mov QWORD PTR [rax+8], rdx  #, ._42
 109 0026 488B1500      mov rdx, QWORD PTR ._42[rip+16] # ._42, ._42
 109      000000
 110 002d 48895010      mov QWORD PTR [rax+16], rdx #, ._42
 124                    .loc 4 155 0
 125 0031 7408          je  .L8 #,
 126                .LVL3:
 127                .LBB342:
 128                .LBB343:
 129                    .loc 2 98 0
 130 0033 4889C7        mov rdi, rax    #, D.11805
 131 0036 E8000000      call    operator delete(void*)  #

总而言之,std::initializer_list在gcc中看起来非常优秀。

答案 1 :(得分:-2)

首先:VC ++,从版本VS11 = VS2012开始,它的初始版本不支持初始化列表,所以这个问题对于VS atm来说有点没有实际意义,但是我确定他们会修补它,它应该在几个月(或几年)内变得相关。

作为附加信息,我将添加VS 2012对本地数组初始化的作用,每个人都可以得出它自己的结论,这意味着它们何时实现初始化列表:

这里是VC ++ 2012在编译器的默认release mode中吐出的内置数组的初始化:

int _tmain(int argc, _TCHAR* argv[])
{
00B91002  in          al,dx  
00B91003  sub         esp,28h  
00B91006  mov         eax,dword ptr ds:[00B94018h]  
00B9100B  xor         eax,ebp  
00B9100D  mov         dword ptr [ebp-4],eax  
00B91010  push        esi  
    int numbers[] = {1,2,3,4,5,6,7,8,9};
00B91011  mov         dword ptr [numbers],1  
00B91018  mov         dword ptr [ebp-24h],2  
00B9101F  mov         dword ptr [ebp-20h],3  
00B91026  mov         dword ptr [ebp-1Ch],4  
00B9102D  mov         dword ptr [ebp-18h],5  
00B91034  mov         dword ptr [ebp-14h],6  
00B9103B  mov         dword ptr [ebp-10h],7  
00B91042  mov         dword ptr [ebp-0Ch],8  
00B91049  mov         dword ptr [ebp-8],9  

...

因此,在函数执行时创建/填充此数组,不会涉及“静态”存储。