[英]C vs C++ Static Initialization of Objects
I have a question regarding initialization of fairly large sets of static data. 我有一个关于初始化大量静态数据的问题。
See my three examples below of initializing sets of static data. 请参阅下面的三个初始化静态数据集的示例。 I'd like to understand the program load time & memory footprint implications of the methods shown below.
我想了解以下所示方法对程序加载时间和内存占用的影响。 I really don't know how to evaluate that on my own at the moment.
我现在真的不知道该如何评估。 My build environment is still on the desktop using Visual Studio, however the embedded targets will be compiled for VxWorks using GCC.
我的构建环境仍在使用Visual Studio的桌面上,但是将使用GCC为VxWorks编译嵌入式目标。
Traditionally, I've used basic C-structs for this sort of thing, although there's good reason to move this data into C++ classes moving forward. 传统上,我使用基本的C结构进行此类操作,尽管有充分的理由将这些数据移入C ++类。 Dynamic memory allocation is frowned upon in the embedded application and is avoided wherever possible.
动态内存分配在嵌入式应用程序中不受欢迎,并且在任何可能的情况下都避免使用。
As far as I know, the only way to initialize a C++ class is through its constructor, shown below in Method 2. I am wondering how that compares to Method 1. Is there any appreciable additional overhead in terms of ROM (Program footprint), RAM (Memory Footprint), or program load time? 据我所知,初始化C ++类的唯一方法是通过其构造函数,如下面的方法2所示。我想知道与方法1相比如何。在ROM(程序占用空间)方面是否存在任何明显的额外开销, RAM(内存占用量)还是程序加载时间? It seems to me that the compiler may be able to optimize away the rather trivial constructor, but I'm not sure if that's common behavior.
在我看来,编译器可能可以优化一些琐碎的构造函数,但是我不确定这是否是常见行为。
I listed Method 3, as I've considered it, although it just seems like a bad idea. 我列出了方法3,尽管我认为这是一个坏主意,但我认为它是。 Is there something else that I'm missing here?
我还有其他想念的地方吗? Anyone else out there initialize data in a similar manner?
还有其他人以类似的方式初始化数据吗?
/* C-Style Struct Storage */
typedef struct{
int a;
int b;
}DATA_C;
/* CPP Style Class Storage */
class DATA_CPP{
public:
int a;
int b;
DATA_CPP(int,int);
};
DATA_CPP::DATA_CPP(int aIn, int bIn){
a = aIn;
b = bIn;
}
/* METHOD 1: Direct C-Style Static Initialization */
DATA_C MyCData[5] = { {1,2},
{3,4},
{5,6},
{7,8},
{9,10}
};
/* METHOD 2: Direct CPP-Style Initialization */
DATA_CPP MyCppData[5] = { DATA_CPP(1,2),
DATA_CPP(3,4),
DATA_CPP(5,6),
DATA_CPP(7,8),
DATA_CPP(9,10),
};
/* METHOD 3: Cast C-Struct to CPP class */
DATA_CPP* pMyCppData2 = (DATA_CPP*) MyCData;
In C++11 , you can write this: 在C ++ 11中,您可以这样编写:
DATA_CPP obj = {1,2}; //Or simply : DATA_CPP obj {1,2}; i.e omit '='
instead of 代替
DATA_CPP obj(1,2);
Extending this, you can write: 扩展此内容,您可以编写:
DATA_CPP MyCppData[5] = { {1,2},
{3,4},
{5,6},
{7,8},
{9,10},
};
instead of this: 代替这个:
DATA_CPP MyCppData[5] = { DATA_CPP(1,2),
DATA_CPP(3,4),
DATA_CPP(5,6),
DATA_CPP(7,8),
DATA_CPP(9,10),
};
Read about uniform initialization. 阅读有关统一初始化的信息。
I've done a bit of research in this, thought I'd post the results. 我已经对此进行了一些研究,以为我会发布结果。 I used Visual Studio 2008 in all cases here.
我在所有情况下都使用Visual Studio 2008。
Here's the disassembly view of the code from Visual Studio in Debug Mode: 这是在调试模式下Visual Studio中代码的反汇编视图:
/* METHOD 1: Direct C-Style Static Initialization */
DATA_C MyCData[5] = { {1,2},
{3,4},
{5,6},
{7,8},
{9,10},
};
/* METHOD 2: Direct CPP-Style Initialization */
DATA_CPP MyCppData[5] = { DATA_CPP(1,2),
010345C0 push ebp
010345C1 mov ebp,esp
010345C3 sub esp,0C0h
010345C9 push ebx
010345CA push esi
010345CB push edi
010345CC lea edi,[ebp-0C0h]
010345D2 mov ecx,30h
010345D7 mov eax,0CCCCCCCCh
010345DC rep stos dword ptr es:[edi]
010345DE push 2
010345E0 push 1
010345E2 mov ecx,offset MyCppData (1038184h)
010345E7 call DATA_CPP::DATA_CPP (103119Ah)
DATA_CPP(3,4),
010345EC push 4
010345EE push 3
010345F0 mov ecx,offset MyCppData+8 (103818Ch)
010345F5 call DATA_CPP::DATA_CPP (103119Ah)
DATA_CPP(5,6),
010345FA push 6
010345FC push 5
010345FE mov ecx,offset MyCppData+10h (1038194h)
01034603 call DATA_CPP::DATA_CPP (103119Ah)
DATA_CPP(7,8),
01034608 push 8
0103460A push 7
0103460C mov ecx,offset MyCppData+18h (103819Ch)
01034611 call DATA_CPP::DATA_CPP (103119Ah)
DATA_CPP(9,10),
01034616 push 0Ah
01034618 push 9
0103461A mov ecx,offset MyCppData+20h (10381A4h)
0103461F call DATA_CPP::DATA_CPP (103119Ah)
};
01034624 pop edi
01034625 pop esi
01034626 pop ebx
01034627 add esp,0C0h
0103462D cmp ebp,esp
0103462F call @ILT+325(__RTC_CheckEsp) (103114Ah)
01034634 mov esp,ebp
01034636 pop ebp
Interesting thing to note here is that there is definitely some overhead in program memory usage and load time, at least in non-optimized debug mode. 这里要注意的有趣一点是,至少在非优化调试模式下,程序内存使用和加载时间肯定会存在一些开销。 Notice that Method 1 has zero assembly instructions, while method two has about 44 instructions.
请注意,方法1的汇编指令为零,而方法2的汇编指令约为44。
I also ran compiled the program in Release mode with optimization enabled, here is the abridged assembly output: 我还以启用优化的方式在发布模式下运行了程序,这是经过删节的程序集输出:
?MyCData@@3PAUDATA_C@@A DD 01H ; MyCData
DD 02H
DD 03H
DD 04H
DD 05H
DD 06H
DD 07H
DD 08H
DD 09H
DD 0aH
?MyCppData@@3PAVDATA_CPP@@A DD 01H ; MyCppData
DD 02H
DD 03H
DD 04H
DD 05H
DD 06H
DD 07H
DD 08H
DD 09H
DD 0aH
END
Seems like the compiler indeed optimized away the calls to the C++ constructor. 似乎编译器确实优化了对C ++构造函数的调用。 I could find no evidence of the constructor ever being called anywhere in the assembly code.
我找不到在汇编代码中任何地方调用过构造函数的证据。
I thought I'd try something a bit more. 我以为我会再尝试一些。 I changed the constructor to:
我将构造函数更改为:
DATA_CPP::DATA_CPP(int aIn, int bIn){
a = aIn + bIn;
b = bIn;
}
Again, the compiler optimized this away, resulting in a static dataset: 再次,编译器对此进行了优化,从而得到了静态数据集:
?MyCppData@@3PAVDATA_CPP@@A DD 03H ; MyCppData
DD 02H
DD 07H
DD 04H
DD 0bH
DD 06H
DD 0fH
DD 08H
DD 013H
DD 0aH
END
Interesting, the compiler was able to evaluate the constructor code on all the static data during compilation and create a static dataset, still not calling the constructor. 有趣的是,编译器能够在编译期间对所有静态数据评估构造函数代码,并创建静态数据集,但仍不调用构造函数。
I thought I'd try something still a bit more, operate on a global variable in the constructor: 我以为我会尝试更多一些,对构造函数中的全局变量进行操作:
int globalvar;
DATA_CPP::DATA_CPP(int aIn, int bIn){
a = aIn + globalvar;
globalvar += a;
b = bIn;
}
And in this case, the compiler now generated assembly code to call the constructor during initialization: 在这种情况下,编译器现在生成了汇编代码以在初始化期间调用构造函数:
??__EMyCppData@@YAXXZ PROC ; `dynamic initializer for 'MyCppData'', COMDAT
; 35 : DATA_CPP MyCppData[5] = { DATA_CPP(1,2),
00000 a1 00 00 00 00 mov eax, DWORD PTR ?globalvar@@3HA ; globalvar
00005 8d 48 01 lea ecx, DWORD PTR [eax+1]
00008 03 c1 add eax, ecx
0000a 89 0d 00 00 00
00 mov DWORD PTR ?MyCppData@@3PAVDATA_CPP@@A, ecx
; 36 : DATA_CPP(3,4),
00010 8d 48 03 lea ecx, DWORD PTR [eax+3]
00013 03 c1 add eax, ecx
00015 89 0d 08 00 00
00 mov DWORD PTR ?MyCppData@@3PAVDATA_CPP@@A+8, ecx
; 37 : DATA_CPP(5,6),
0001b 8d 48 05 lea ecx, DWORD PTR [eax+5]
0001e 03 c1 add eax, ecx
00020 89 0d 10 00 00
00 mov DWORD PTR ?MyCppData@@3PAVDATA_CPP@@A+16, ecx
; 38 : DATA_CPP(7,8),
00026 8d 48 07 lea ecx, DWORD PTR [eax+7]
00029 03 c1 add eax, ecx
0002b 89 0d 18 00 00
00 mov DWORD PTR ?MyCppData@@3PAVDATA_CPP@@A+24, ecx
; 39 : DATA_CPP(9,10),
00031 8d 48 09 lea ecx, DWORD PTR [eax+9]
00034 03 c1 add eax, ecx
00036 89 0d 20 00 00
00 mov DWORD PTR ?MyCppData@@3PAVDATA_CPP@@A+32, ecx
0003c a3 00 00 00 00 mov DWORD PTR ?globalvar@@3HA, eax ; globalvar
; 40 : };
00041 c3 ret 0
??__EMyCppData@@YAXXZ ENDP ; `dynamic initializer for 'MyCppData''
FYI, I found this page helpful in setting up visual studio to output assembly: How do I get the assembler output from a C file in VS2005 仅供参考,我发现此页面有助于设置Visual Studio以输出程序集: 如何从VS2005中的C文件获取汇编程序输出
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.