当初始化一个对象时,{0}意味着什么?

{0}被用来初始化一个对象时,它意味着什么?我在任何地方都找不到{0}的任何引用,而且由于花括号,谷歌搜索没有帮助。

示例代码:

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);
}

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

64441 次浏览

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

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

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

以下是该规范的相关引用:

属性中的成员个数小于列表中的初始化式个数 聚合,那么每个成员都不是 显式初始化的 default-initialized。 例子:< / p >
struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };

初始化ss.a1ss.b"asdf",和ss.c的值 int()形式的表达式,即: 0 . < / p >

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

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

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

char mytext[100] = {0};

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

struct foo
{
char c;
int  i;
};


foo a = {0};

并不等同于:

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

在第一种情况下,c和i之间的pad字节是未初始化的。你为什么关心这个?好吧,如果你把数据保存到磁盘上或者通过网络发送,你可能会有安全问题。

注意,一个空的聚合初始化式也可以工作:

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

关于ShellExecuteEx()崩溃的原因:你的SHELLEXECUTEINFO "sexi"结构体有很多成员,你只初始化了其中的一些成员。

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

当你包括这行:

SHELLEXECUTEINFO sexi = {0};

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

我一直在想,为什么你应该使用一些像

struct foo bar = { 0 };

下面是一个要解释的测试用例:

check.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”部分是内存中被设置为0的部分。

如果你将上面的代码更改为:

} my_zero_struct = { 0 };

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

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

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

{0}是包含其元素为0的匿名数组

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

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

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

在C和c++中,{0}是任何(完整对象)类型的有效初始化式。这是一个常用的习语,用于将对象初始化为(继续阅读,看看这意味着什么)。

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

标量的初始化式应该是一个单独的表达式,可以选择用大括号括起来。

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

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

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

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


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

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

所以这个:

some_type obj = { 0 };

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

这些规则与c++类似。

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

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

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

长版本

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};

那不是简单多了吗?

这也很好,因为:

  • 你不需要找到每个成员并初始化它
  • 您不必担心在稍后添加新成员时可能无法初始化
  • 你不必ZeroMemory