C++标准是否保证非静态聚合对象的填充字节初始化为零?

inkz8wg9  于 2023-01-10  发布在  其他
关注(0)|答案(1)|浏览(134)

C++是否支持这样一种语言构造,它允许我们将一个对象及其所有填充字段初始化为零?我在www.example.com上找到了一些cppreference.com关于zero-initialization的令人鼓舞的措辞,建议在某些情况下,填充字节也将被清零。
引自cppreference.com:zero-initialization
在下列情况下执行零初始化:
1.作为非类类型和没有构造函数的值初始化类类型的成员的值初始化序列的一部分,包括未提供初始值设定项的聚合的元素的值初始化。
零初始化的影响是:

  • 如果T是标量类型,则对象的初始值是显式转换为T的整数常数零。
  • 如果T是非联合类类型,则所有基类和非静态数据成员都初始化为零,所有填充都初始化为零。构造函数(如果有)将被忽略。
  • ...

我们将在value-initializationaggregate-initializationlist-initialization中找到对零初始化的引用。
我用最新的GCC和clang C编译器进行了测试,它们的行为似乎有所不同。
坦率地说,我很努力地解析这些规则,特别是考虑到不同的编译器行为,我不知道如何正确地解释这些规则。
参见代码here(至少需要C
11)。下面是结果:
给定:Foo

struct Foo
{
    char x;
    int y;
    char z;
};

| 构造|g++|铿锵++|
| - ------|- ------|- ------|
| 喷火()|第一个月|x:[----][----][----][----],v: 0|
| | y:[----][----][----][----],v: 0| y:[----][----][----][----],v: 0|
| | z:[----][0x4A][0x4B][0x4C],v: 0| z:[----][----][----][----],v: 0|
| | | |
| 福{}| x:[----][----][----][----],v: 0| x:[----][0x42][0x43][0x44],v: 0|
| | y:[----][----][----][----],v: 0| y:[----][----][----][----],v: 0|
| | z:[----][----][----][----],v: 0| x1米11米1x|
这里[----]表示包含所有位0的字节,并且[0x..]是垃圾值。
正如你所看到的,编译器输出表明填充没有被初始化。Foo()Foo{}都是值初始化。另外Foo{}是聚合初始化,缺少初始化器。为什么零初始化规则没有被触发?为什么填充规则没有被触发?
我已经明白,依赖于填充字节为零不是一个好主意,甚至可能是未定义的行为,但我认为这不是这个问题的重点。

  • 问题1:标准是否提供了可靠地初始化填充字节的方法?
  • 问题2:另见:does c initialize structure padding,是否适用?
  • 问题3:这些编译器是否符合标准?
  • 问题4:编译器明显不同行为的解释是什么?
bprjcwpo

bprjcwpo1#

只有当类对象初始化为零时,填充位才会为零,如引号中所示。
对于自动和动态存储持续时间对象,零初始化只在对象是值初始化的,并且它有一个未删除的隐式默认构造函数,并且没有其他用户提供的默认构造函数时发生。[dcl.init.general]/8.1这些条件在这里满足。
值初始化应该总是与()初始化器一起发生。([dcl.init.general]/16.4)
{}作为初始值设定项也可能发生值初始化。但是,如果类是一个聚合,则首选聚合初始化,它不会导致值初始化。([dcl.init.list]/3.4)
CWG 1301在C14之前更改了聚合初始化优于值初始化的优先级,这也可能适用于C11。在C++11之前,规则可能有所不同,我没有检查。
因此,我认为Clang的行为是正确的,而GCC在Foo()上是错误的,同时对Foo{}做了不必要的工作(尽管正如下面的@PeterCordes所指出的,将整个对象(包括填充)归零实际上更有效)。
请注意,我并不完全清楚检查非零初始化填充字节的值是否像您这样具有定义良好的行为。
对于默认初始化的情况阅读成员具有未定义的行为,因为它的值将是不确定的。
我认为在new可能初始化它们之前,填充也应该有不确定的值,在这种情况下,如果没有零初始化,检查它们的值将导致未定义的行为。

相关问题