4

当我们像这样初始化一个数组时int a[5] = {0},编译器会将所有 5 个元素设为 0。这真的很好,紧凑初始化和有用的功能。

但我想知道为什么编译器没有int a[5]={1}类似地初始化?为什么它不能使所有 5 个元素都为 1?为什么标准不强制要求它?这不是一个很棒的功能吗?是不是不见了?

此外,如果初始化程序中的元素数量小于数组的大小,则编译器可以使用初始化程序中的最后一个元素初始化剩余的元素。意思int a[5]={1,2,3}是,等价于int a[5]={1,2,3,3,3}。同样,int a[10]={1,2,3,0}等价于int a[10]={1,2,3,0,0,0,0,0,0,0};

如果标准要求它,这一切都不是一个很棒的功能吗?或者这个缺失的功能有什么好的理由吗?


在 C99 中有一个叫做指定初始化器的东西,它的用法如下:

指定初始化器可以与常规初始化器结合使用,如下例所示:

int a[10] = {2, 4, [8]=9, 10}

在这个例子中,a[0] 被初始化为 2,a 1被初始化为 4,a[2] 到 a[7] 被初始化为 0,a[9] 被初始化为 10。

很有趣。但即使这个特性在 C++ 中也没有。

4

4 回答 4

10

为什么它不能使所有 5 个元素都为 1?

因为你误解了什么{}意思。(实际上,在 C++ 中,这样做的更好方法是,{}而不是{0})。该语法{0}并不意味着您希望聚合中的所有元素都设置为零。相反,它表示您想要一个聚合,其中第一个元素零分配给指定的变量(可以是数组或 C++ 中的类类型)。因为聚合通常具有比零值更多的字段,所以聚合中的剩余元素是默认构造的。内置或 POD 类型的默认值是将所有字段设置为零,因此您实际上已将整个聚合设置为零。

至于具体为什么,考虑以下。根据当前标准,以下断言都不会失败:

struct abc
{
    char field1;
    int field2;
    char field3;
};

int main()
{
    abc example = {'a', static_cast<int>('b')};
    //All three asserts pass
    assert(example.field1 == 'a');
    assert(example.field2 == static_cast<int>('b'));
    assert(example.field3 == '\0');

    int example2[3] = {static_cast<int>('a'), 42};
    assert(example2[0] == static_cast<int>('a'));
    assert(example2[1] == 42);
    assert(example2[2] == 0);
}

您期望field3在您提议的标准变更中具有什么价值?即使您将它定义为聚合初始化程序中的最后一个元素,如上所示,这也会破坏与现有代码的兼容性,现有代码假定其余元素是默认构造的。


编辑:刚刚意识到你的问题是根据数组提出的——但答案对于结构或数组都是一样的,所以这并不重要。

EDIT2:为了使这更符合标准,对类/结构的引用已替换为下面的“聚合”,其中涵盖了结构和数组的情况。

于 2011-01-23T07:11:05.970 回答
4

是的,他们本可以这样做,但他们没有,现在改变这种行为为时已晚。C 和 C++ 背后的决定几乎在每一步都考虑到了性能和极简主义,所以我想,如果没有别的,这里也会发挥作用。

这样的功能并没有让我觉得太棒了。这是一个非常简单的语法糖,我很少发现需要将这样的数组初始化为 0 以外的任何值。

于 2011-01-23T07:05:37.563 回答
2

典型的运行时库提供了一种功能,可以轻松地将数据初始化为 0。一般来说,它存储在可执行文件的特定部分中,由编译器和链接器组织。在程序启动时,运行时启动代码使用类似memset()将所有初始化数据清除为 0 的方法。这意味着零字节不必存储在可执行文件本身中。

相反,如果您将数据初始化为零以外的值,则该数据的字节必须存储在可执行文件本身中,因为自动初始化程序仅初始化为零。

因此,如果您要声明一个大数组char(比如说一个兆字节?)并用 初始化它{0},那么该数组的可执行文件中就不会存储字节。另一方面,如果您要{1}在您的方案下对其进行初始化,则必须将一兆字节的1字节存储在可执行文件本身中。通过更改初始化列表中的一个字符,可执行文件的大小会增加一兆字节。

我相信这样的方案会违反最小意外原则

于 2011-01-23T07:12:38.067 回答
0

我个人发现更“合乎逻辑”(即简单)具有固定的默认初始化程序,而不是仅针对数组重复最后一个初始化程序的另一条规则。这可能看起来“实用”(即有用),但它在 IMO 逻辑上更复杂。

话虽如此,但我认为您在尝试将逻辑应用于 C++ 之类的语言时犯了一个大错误。

C++ 是一门复杂的语言,其规则是长期演变历史的结果,而其目前的形式是许多人甚至正式委员会的工作成果(仅最后一部分就可以解释任何事情)。

像 C++ 这样的语言不能通过逻辑来推断,它必须像历史一样被研究。除非您是Hari Seldon ,否则您真的无法使用逻辑推理来推断历史。

如果您尝试使用逻辑而不是学习,那么在 C++ 的某些地方您将遭受很多痛苦。仅举几个...

  • 为什么默认调度是静态的(即错误)?
  • 为什么空指针没有关键字?
  • 为什么两个无符号的差是无符号的?
  • 为什么有符号和无符号之间的总和是无符号的?
  • 如果无符号表示“ Z_{2^n}的元素”,那么为什么大小是无符号的?
  • 为什么std::string s; s=3.141592654;是完全有效的 C++?
  • 为什么在 C++0Xi = i++ + 1;中是未定义的行为并且i = ++i + 1;是有效的?
  • 为什么double x=3.14; int y(int(x));不意味着y将是3?
于 2011-01-23T08:27:09.030 回答