关于c ++:初始化对象时{0}意味着什么?

关于c ++:初始化对象时{0}意味着什么?

What does {0} mean when initializing an object?

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

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}

如果没有它,上述代码将在运行时崩溃。


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

An aggregate is an array or a class with no user-declared constructors, no private or protected non-static data members, no base classes, and no virtual functions.

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

以下是规范中的相关报价:

If there are fewer initializers in the list than there are members in the
aggregate, then each member not
explicitly initialized shall be
default-initialized.
Example:

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

initializes ss.a with 1, ss.b with
"asdf", and ss.c with the value of an
expression of the form int(), that is,
0.

您可以在这里找到关于这个主题的完整规范


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

1
2
3
4
5
6
7
struct foo
{
    char c;
    int  i;
};

foo a = {0};

不同于:

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

在第一种情况下,C和I之间的pad字节未初始化。你为什么会在意?好吧,如果您将这些数据保存到磁盘上,或者通过网络或其他方式发送,那么您可能会遇到安全问题。


请注意,空聚合初始值设定项也可以工作:

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

回答为什么ShellExecuteEx()崩溃:你的SHELLEXECUTEINFOsexi结构有许多成员,而你只是初始化其中的一些成员。

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

当包括行时:

1
SHELLEXECUTEINFO sexi = {0};

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


EDCOX1·0是任意(完整对象)类型的有效初始化器,在C和C++中都是有效的初始化器。这是一个常用的习惯用法,用于将对象初始化为零(继续阅读以了解这意味着什么)。

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

The initializer for a scalar shall be a single expression, optionally enclosed in braces.

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

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

中间支撑({}可以省略;例如,这两个都是有效的和等效的:

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

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

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

因此:

1
some_type obj = { 0 };

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

这些规则类似于C++。

在您的特定情况下,由于您要为sexi.cbSize等赋值,很明显SHELLEXECUTEINFO是一个结构或类类型(或者可能是一个联合,但可能不是),因此并非所有这些都适用,但正如我所说,{ 0 }是一个常见的习惯用法,可以在更一般的情况下使用。

这并不(必然)等同于使用memset将对象的表示设置为所有位零。浮点0.0和空指针都不必表示为所有位零,{ 0 }初始值设定项不必将填充字节设置为任何特定值。然而,在大多数系统中,它可能具有相同的效果。


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

1
char mytext[100] = {0};


这是一段时间以来,我工作在C/C++ +,但Irc,同样的快捷方式也可以用于数组。


我一直在想,你为什么要用

1
struct foo bar = { 0 };

下面是一个测试案例来解释:

检查C

1
2
3
4
5
6
7
8
9
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系统上Ubuntu12.04.2上的GCC4.6.3)。Excerpt:

1
2
3
4
5
6
7
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被称为see wikipedia on.bss之前设置为零的内存部分。

如果将上述代码更改为:

1
} my_zero_struct = { 0 };

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

注释中的提示是,memset可以初始化"完整"结构也不是一个改进,因为.bss部分被完全清除,这也意味着"完整"结构被设置为零。

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


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

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

例如,int arr[8]=0

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


推荐阅读