在C ++ 11标准中,有一个关于支持统一初始化的数组的说明:
如果可以如此分配具有相同初始化程序的显式数组,则实现可以自由地在只读存储器中分配数组。
GCC / Clang / VS会利用这个吗?或者使用此功能的每个初始化是否受到堆栈上的其他数据的影响,以及此隐藏阵列的额外初始化时间?
例如,给出以下示例:
void function()
{
std::vector<std::string> values = { "First", "Second" };
...
上面提到的每个编译器是否将后备数组存储在与声明为static const
的变量相同的内存中的统一初始化中?当调用函数或应用程序初始化时,每个编译器是否会初始化后备数组? (我不是在谈论将要创建的std::initializer_list<std::string>
,而是它所指的“隐藏数组”。
答案 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
...
因此,在函数执行时创建/填充此数组,不会涉及“静态”存储。