2

我试图找到确认或反驳以下声明的文件

char test[5]="";

导致一个缓冲区初始化为所有相同的空字符

memset(test,'\0',sizeof(test));

但无法找到(或理解/破译)任何东西。我专门在旧规范中寻找细节,C99 参考也可以。谢谢

4

4 回答 4

5

这不是您问题的完整答案,但有助于清除其他答案中的错误信息

在 ANSI C89 中,相关的标准文本是(第 3.5.7 节):

如果具有自动存储持续时间的对象未显式初始化,则其值是不确定的。

字符类型的数组可以由字符串字面量初始化,可选地用大括号括起来。字符串文字的连续字符(如果有空间或数组大小未知,则包括终止的空字符)初始化数组的成员。

它仅指定与字符串文字对应的数组元素的初始化。所以尾随数组元素没有显式初始化,因此具有不确定的值。

还有一段话:

如果列表中的初始化程序少于聚合的成员,则聚合的其余部分应隐式初始化,与具有静态存储持续时间的对象相同。

但这并不适用,因为我们不是从列表中初始化的(“列表”表示用大括号括起来的列表,而不是字符串文字)。


在 C90(我不确定是否可以合法链接到)中,这些部分被重新编号,因此包含这些段落的部分变为 6.5.7。后一段的措辞也发生了变化:

如果大括号括起来的列表中的初始化程序少于聚合的成员,则聚合的其余部分应隐式初始化,与具有静态存储持续时间的对象相同。


在 C90 TC1 ( HTML , PDF ) 中,上述内容保持不变。

然而,在缺陷报告 60中,提出了一个关键问题:

当 char(或 wchar_t)数组使用包含的字符数少于数组的字符串字面量进行初始化时,数组的其余元素是否已初始化?

子条款 6.5.7 初始化,第 72 页,只说(强调我的):

如果大括号括起来的列表中的初始化程序少于聚合的成员,则聚合的其余部分应隐式初始化,与具有静态存储持续时间的对象相同。

更正

在第 72 页的 6.5.7 小节中,语义的倒数第二段(在示例之前),在逗号之后添加:

用于初始化已知大小数组的字符串文字或宽字符串文字中的或更少字符,以及 character 或 wchar_t 类型的元素

看来,建议修复中的零初始化确实是标准编写者的意图,因为在C90 TC2中,我们看到了同样的关键更改:

第 72 页

在第 72 页的 6.5.7 小节中,语义的倒数第二段(在示例之前),在逗号之后添加:

用于初始化已知大小数组的字符串文字或宽字符串文字中的或更少字符,以及 character 或 wchar_t 类型的元素

给我们:

如果大括号括起来的列表中的初始化程序少于聚合的成员,或者用于初始化已知大小的数组的字符串文字或宽字符串文字中的字符更少,并且字符或 wchar_t 的元素类型的其余部分聚合应隐式初始化,与具有静态存储持续时间的对象相同。

请注意,TC1 的日期是 1994 年,尽管它是在 1995 年出版的。TC2 的日期是 1996 年。令人困惑的是,DR60 的日期是 1993 年 7 月 16 日,因此早于 TC1。也许那时 TC1 的工作已经太先进了,无法处理新的缺陷报告并且积压了?在任何情况下,TC2 大多只是针对缺陷报告的一组更正,表明该更改首先出现在 C95 中而不是在 C95 中,并且空终止符之后字符的零初始化是 C89 标准编写者所拥有的故意的。


在 ISO C99(原始版本,没有技术勘误)中,该段落现在重新编号为 6.7.8/21 并再次更改。删除了“宽字符串文字”“字符或 wchar_t 类型的元素”的提及:

如果大括号括起来的列表中的初始化程序少于聚合的元素或成员,或者用于初始化已知大小数组的字符串文字中的字符少于数组中的元素,则聚合的其余部分应隐式初始化与具有静态存储持续时间的对象相同。

意味着尾随数组元素被初始化为空字节。

(注意:原始的 C99 可能是受版权保护的材料,所以我不能在上面发布指向它的链接。不过,这就是它所说的。是最后一个免费可用的工作草稿的链接。还有两个草稿之后,但 WG14 网站已将其删除。不过,该措辞在 N843 工作草案中,并且仍然存在于后来合并 TC3 的 C99 中。


我一直找不到任何 C95 (ISO/IEC 9899:1990/AMD1:1995) 的免费副本。因此,我无法准确回答在 C89 和 C99 之间的哪个点进行了“宽字符串文字”和“wchar_t”更改。此外,C99 基本原理文档中未提及该主题。

当然,C99 的行为可能是 C89 作者的意图,而缺少的文本是疏忽,但是在没有任何类型的文档的情况下,我们无法得出任何结论,并且可能有来自的编译器那个时候不初始化尾随元素。

希望其他拥有这些文件(或倾向于从 ISO 商店购买它们!)的人可以提供准确的答案。

于 2020-03-04T22:51:28.467 回答
3

快速总结

C99 和更高版本保证剩余的字符被初始化为零。C89/C90/C95 不做此保证,也不指定剩余字符的值。这可能是一个无意的疏忽,我推测大多数或所有 C99 之前的编译器无论如何都会对剩余的字符进行零初始化。如果您使用的是符合 C99 或更高版本的编译器,则可以保证零初始化。

血淋淋的细节

char test[5]="";

由于 C89/C90 标准中的缺陷,仅保证初始化test[0]'\0'. 的其他元素test未指定。

C95 修正案没有解决这个问题。

C99 标准纠正了这一缺陷,要求test将其初始化为全零。

另一个例子:

char foo[5] = "foo";

在 C89/C90、C95 中,语言保证了foo[0]=='f', foo[1]=='o', foo[2]=='o', foo[3]=='\0',但对 . 的值只字未提foo[4]。在 C99 和更高版本中,它保证被初始化,就像你写的一样:

char foo[5] = { 'f', 'o', 'o', '\0' };

在 C 标准的所有版本中,它保证foo[4]=='\0'.

引文

1989 ANSI C 标准和 1990 ISO C 标准是等效的,不同之处仅在于非规范的介绍材料和章节的重新编号。1995 年的修正案更新了标准,但并未影响阵列初始化。

1990 ISO C 标准第 6.5.7 节说:

字符类型的数组可以由字符串字面量初始化,可选地用大括号括起来。字符串文字的连续字符(如果有空间或数组大小未知,则包括终止的空字符)初始化数组的元素。

稍后在同一部分:

如果大括号括起来的列表中的初始化程序少于聚合的成员,则聚合的其余部分应隐式初始化,与具有静态存储持续时间的对象相同。

它指定将大括号括起来的列表的尾随成员初始化为零,但不会对字符串文字初始化程序进行相同的声明。(我推测这是一个无意的疏忽,并且大多数编译器无论如何都会将剩余的元素填充为零,因为在某些情况下他们已经不得不这样做了。)

每个版本的 C 标准都有一组与之相关的缺陷报告:

C90 缺陷报告 #060由 PJ Plauger 和/或 Larry Jones 于 1993 年提交,提出了这个问题:

char(or wchar_t) 的数组使用包含的字符数少于数组的字符串字面量进行初始化时,数组的其余元素是否已初始化?
第 72 页的第 6.5.7 节初始化仅说(强调我的):

如果大括号括起来的列表中的初始化程序少于聚合的成员,则聚合的其余部分应隐式初始化,与具有静态存储持续时间的对象相同。

对此缺陷报告的响应导致 C99 标准第 6.7.8 节第 21 节(强调添加)中的措辞修改:

如果大括号括起来的列表中的初始化程序少于聚合的元素或成员,或者用于初始化已知大小数组的字符串文字中的字符少于数组中的元素,则聚合的其余部分应隐式初始化与具有静态存储持续时间的对象相同。

于 2020-03-08T02:41:12.627 回答
1

来自 C 标准(6.7.9 初始化)

10 如果具有自动存储持续时间的对象未显式初始化,则其值是不确定的。如果具有静态或线程存储持续时间的对象未显式初始化,则:

— 如果它具有指针类型,则将其初始化为空指针;

— 如果它具有算术类型,则将其初始化为(正或无符号)零;

...

21 如果大括号括起来的列表中的初始值设定项少于聚合的元素或成员,或者用于初始化已知大小数组的字符串文字中的字符少于数组中的元素,则聚合的剩余部分应隐式初始化与具有静态存储持续时间的对象相同。

这意味着在这个声明中

char test[5] = "";

数组的所有五个元素都初始化为零。第一个元素由字符串文字的终止零显式初始化,所有其他元素都以与具有静态存储持续时间的对象相同的方式隐式初始化。

至少从 C99 标准开始是有效的。

下面有一个演示程序,显示了用零初始化字符数组的不同方法。

#include <stdio.h>

int main(void) 
{
    enum { N = 5 };

    char s1[N] = "";
    char s2[N] = { "" };
    char s3[N] = { 0 };
    char s4[N] = { [0] = 0 };
    char s5[N] = { [N-1] = 0 };

    char * s[] = { s1, s2, s3, s4, s5 };

    for ( size_t i = 0; i < sizeof( s ) / sizeof( *s ); i++ )
    {
        for ( size_t j = 0; j < N; j++ )
        {
            printf( "%d", s[i][j] );
        }
        putchar( '\n' );
    }
    return 0;
}

程序输出为

00000
00000
00000
00000
00000
于 2020-03-04T22:17:33.273 回答
0

从代码清晰的角度来看,如果数组的目的是保存字符串,那么

char test[5] = "";

用长度为零的字符串初始化数组,其余字节无关紧要。如果它们确实重要,那么数组并不是真正的字符串,您应该使用

char test[5] = {0};

澄清这一点。

于 2020-03-04T22:21:36.590 回答