简体   繁体   English

C与C ++对象的静态初始化

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM