【问题标题】:What does {0} mean when initializing an object?初始化对象时 {0} 是什么意思?
【发布时间】:2010-09-10 11:28:09
【问题描述】:

{0}用于初始化对象时,是什么意思?我在任何地方都找不到对{0} 的任何引用,而且由于大括号,Google 搜索没有帮助。

示例代码:

SHELLEXECUTEINFO sexi = {0}; // what does this do?
sexi.cbSize = sizeof(SHELLEXECUTEINFO);
sexi.hwnd = NULL;
sexi.fMask = SEE_MASK_NOCLOSEPROCESS;
sexi.lpFile = lpFile.c_str();
sexi.lpParameters = args;
sexi.nShow = nShow;

if(ShellExecuteEx(&sexi))
{
    DWORD wait = WaitForSingleObject(sexi.hProcess, INFINITE);
    if(wait == WAIT_OBJECT_0)
        GetExitCodeProcess(sexi.hProcess, &returnCode);
}

没有它,上面的代码会在运行时崩溃。

【问题讨论】:

    标签: c++ c


    【解决方案1】:

    这里发生的事情称为聚合初始化。这是 ISO 规范第 8.5.1 节中聚合的(缩写)定义:

    聚合是一个数组或类,没有用户声明的构造函数,没有私有或受保护的非静态数据成员,没有基类,也没有虚函数。

    现在,使用{0} 来初始化这样的聚合基本上是0 整个事情的一个技巧。这是因为在使用聚合初始化时您不必指定所有成员,并且规范要求默认初始化所有未指定的成员,这意味着对于简单类型设置为 0

    这是规范中的相关引用:

    如果列表中的初始值设定项少于列表中的成员 聚合,那么每个成员不 显式初始化应为 默认初始化。 示例:

    struct S { int a; char* b; int c; };
    S ss = { 1, "asdf" };
    

    1初始化ss.a,用ss.b初始化 "asdf"ss.c 的值为 int() 形式的表达式,即 0.

    你可以找到关于这个主题的完整规范here

    【讨论】:

    • 反应很好。只是想补充一点,使用 {0} 初始化聚合与仅使用 {} 初始化聚合相同。也许前者使内置类型归零变得更加明显。
    • 一些编译器会阻塞 {},这就是使用 {0} 的原因
    • 不完全......在 C++ 中,如果第一个成员不能为零构造,那么 {0} 将不起作用。例如:结构 A { B b;诠释我;字符 c; };结构 B { B(); B(字符串); };一个 = {}; // 此语句不能重写为 'A a = {0}'。
    • @Branan,这是因为在 C 中“{}”无效。在 C++ 中它是。 @don.neufeld,这已经随着 C++03 改变(默认初始化 -> 值初始化)。请记住,这句话引用了 C++98 标准。
    • 那么,这是否取代了使用 ZeroMemory()(在 VC++ 中)的需要?
    【解决方案2】:

    需要注意的一点是,这种技术不会将填充字节设置为零。例如:

    struct foo
    {
        char c;
        int  i;
    };
    
    foo a = {0};
    

    不等于:

    foo a;
    memset(&a,0,sizeof(a));
    

    在第一种情况下,c 和 i 之间的填充字节未初始化。你为什么要关心?好吧,如果您将此数据保存到磁盘或通过网络或其他方式发送,您可能会遇到安全问题。

    【讨论】:

    • 当然,如果你'write(f, &a, sizeof(a))'这只是一个安全问题,它可以在不同的处理器/编译器上产生不同的文件编码。如果没有 memset,格式良好的输出将是安全的。
    • 此外,如果您通过网络发送内容,您将始终将对齐设置为打包。这样您就可以尽可能少地获得额外的填充字节。
    • 应该注意的是,虽然规范不要求初始化填充,但任何理智的编译器都会这样做,因为它只需要花费时间来“围绕”它进行初始化。
    • 我想对“不是”一词的使用提出异议。填充字节由实现定义。编译器可以随意将 foo a = {0} 转换为 memset(&a, 0, sizeof(a)) 。不需要“跳过”填充字节并且设置 foo.c 和 foo.i。 +1(潜在的)安全漏洞
    • @Leushenko 第 19 点说“所有未明确初始化的子对象”,所以我倾向于第 21 点(你引用的)是草率的。如果规范允许在最后一个初始化程序之前未初始化填充,那将是真正奇怪的,之后填充必须为零,特别是考虑到在使用指定的初始化程序时初始化程序可能会出现乱序。
    【解决方案3】:

    请注意,空的聚合初始化器也可以工作:

    SHELLEXECUTEINFO sexi = {};
    char mytext[100] = {};
    

    【讨论】:

      【解决方案4】:

      回答ShellExecuteEx() 崩溃的原因:您的SHELLEXECUTEINFO“sexi”结构有很多成员,而您只是初始化其中的一些。

      例如,成员sexi.lpDirectory 可能指向任何地方,但ShellExecuteEx() 仍会尝试使用它,因此您会遇到内存访问冲突。

      当您包含该行时:

      SHELLEXECUTEINFO sexi = {0};
      

      在其余的结构设置之前,您告诉编译器在初始化您感兴趣的特定成员之前将 所有 结构成员清零。ShellExecuteEx() 知道如果@987654327 @ 为零,它应该忽略它。

      【讨论】:

        【解决方案5】:

        {0} 是 C 和 C++ 中任何(完整对象)类型的有效初始化程序。这是用于将对象初始化为 zero 的常用习语(继续阅读以了解其含义)。

        对于标量类型(算术和指针类型),大括号是不必要的,但它们是明确允许的。引用 ISO C 标准的N1570 draft,第 6.7.9 节:

        标量的初始值设定项应该是单个表达式,可选择用大括号括起来。

        它将对象初始化为零(0 用于整数,0.0 用于浮点,空指针用于指针)。

        对于非标量类型(结构、数组、联合),{0} 指定对象的 first 元素初始化为零。对于包含结构、结构数组等的结构,这是递归应用的,因此第一个标量元素根据类型设置为零。与任何初始化程序一样,任何未指定的元素都设置为零。

        中间大括号({, })可以省略;例如,这两个都是有效且等效的:

        int arr[2][2] = { { 1, 2 }, {3, 4} };
        
        int arr[2][2] = { 1, 2, 3, 4 };
        

        这就是为什么您不必为第一个元素是非标量的类型编写例如{ { 0 } }

        所以这个:

        some_type obj = { 0 };
        

        是将obj 初始化为零的简写方式,这意味着obj 的每个标量子对象如果是整数则设置为0,如果是浮点数则设置为0.0,或空指针如果是指针。

        C++ 的规则类似。

        在您的特定情况下,由于您将值分配给 sexi.cbSize 等等,很明显 SHELLEXECUTEINFO 是一个结构或类类型(或者可能是一个联合,但可能不是),所以不是所有这些适用,但正如我所说,{ 0 } 是一个常见的成语,可用于更一般的情况。

        (不一定)等同于使用memset 将对象的表示设置为全位为零。浮点 0.0 和空指针都不一定表示为全位为零,{ 0 } 初始化程序不一定将填充字节设置为任何特定值。不过,在大多数系统上,它可能会产生相同的效果。

        【讨论】:

        • 在 C++ 中,{0} 不是没有构造函数接受 0 的对象的有效初始化程序;也不适用于第一个元素是这样的聚合(或没有元素的聚合)
        【解决方案6】:

        我也用它来初始化字符串,例如。

        char mytext[100] = {0};
        

        【讨论】:

        • 当然,如果 mytext 使用的是字符串,char mytext[100];我的文本[0] = '\0';将具有与提供空字符串相同的效果,但只会导致实现将第一个字节归零。
        • @Chris:我经常希望有一种用于部分对象初始化的语法。能够“声明和初始化”x,然后对 y 进行同样的操作,然后是 z,这比必须声明 x、y 和 z,然后初始化 x、y 和 z,但在仅初始化 100 个字节时要好得多实际上需要初始化似乎相当浪费。
        【解决方案7】:

        自从我在 c/c++ 中工作以来已经有一段时间了,但是 IIRC,同样的快捷方式也可以用于数组。

        【讨论】:

          【解决方案8】:

          我一直想知道,为什么你应该使用像

          这样的东西
          struct foo bar = { 0 };
          

          这里有一个测试用例来说明:

          检查.c

          struct f {
              int x;
              char a;
          } my_zero_struct;
          
          int main(void)
          {
              return my_zero_struct.x;
          }
          

          我用gcc -O2 -o check check.c 编译,然后用readelf -s check | sort -k 2 输出符号表(这是在x64 系统上的ubuntu 12.04.2 上使用gcc 4.6.3)。摘录:

          59: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
          48: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
          25: 0000000000601018     0 SECTION LOCAL  DEFAULT   25 
          33: 0000000000601018     1 OBJECT  LOCAL  DEFAULT   25 completed.6531
          34: 0000000000601020     8 OBJECT  LOCAL  DEFAULT   25 dtor_idx.6533
          62: 0000000000601028     8 OBJECT  GLOBAL DEFAULT   25 my_zero_struct
          57: 0000000000601030     0 NOTYPE  GLOBAL DEFAULT  ABS _end
          

          这里的重要部分是,my_zero_struct__bss_start 之后。 C 程序中的“.bss”部分是在调用main 之前设置为零的内存部分,请参阅wikipedia on .bss

          如果你把上面的代码改成:

          } my_zero_struct = { 0 };
          

          然后生成的“检查”可执行文件看起来完全相同至少与 ubuntu 12.04.2 上的 gcc 4.6.3 编译器相同; my_zero_struct 仍在 .bss 部分中,因此在调用 main 之前它将自动初始化为零。

          cmets 中的提示,memset 可能会初始化“完整”结构也不是一种改进,因为 .bss 部分已完全清除,这也意味着“完整”结构设置为零。

          可能是 C 语言标准没有提及这些,但在现实世界的 C 编译器中,我从未见过不同的行为。

          【讨论】:

          • 全局和静态变量始终默认初始化为 0 或默认 ctor。但是如果你在本地声明 f 的实例,你可能会得到不同的结果。
          【解决方案9】:

          将整个结构初始化为空/零/空值是一种语法糖。

          加长版

          SHELLEXECUTEINFO sexi;
          sexi.cbSize = 0;
          sexi.fMask = 0;
          sexi.hwnd = NULL;
          sexi.lpVerb = NULL;
          sexi.lpFile = NULL;
          sexi.lpParameters = NULL;
          sexi.lpDirectory = NULL;
          sexi.nShow = nShow;
          sexi.hInstApp = 0;
          sexi.lpIDList = NULL;
          sexi.lpClass = NULL;
          sexi.hkeyClass = 0;
          sexi.dwHotKey = 0;
          sexi.hMonitor = 0;
          sexi.hProcess = 0;
          

          短版

          SHELLEXECUTEINFO sexi = {0};
          

          这不是更容易吗?

          这也很好,因为:

          • 您不必寻找每个成员并对其进行初始化
          • 您不必担心以后添加新成员时可能不会对其进行初始化
          • 你不必call ZeroMemory

          【讨论】:

            【解决方案10】:

            {0} 是一个匿名数组,其元素为 0。

            这用于用 0 初始化数组的一个或所有元素。

            例如int arr[8] = {0};

            在这种情况下, arr 的所有元素都将被初始化为 0。

            【讨论】:

            • {0} 不是匿名数组。这甚至不是一个表情。这是一个初始化器。
            猜你喜欢
            • 2020-07-17
            • 1970-01-01
            • 2013-07-16
            • 2012-02-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-12-13
            相关资源
            最近更新 更多