样本1:
char a []={'h','i'};
int i;
for(i=0;a[i]!='\0';i++){
printf("%c",a[i]);
}
printf("%s",a);
输出:hi☻hi♥
样本2:
char a []={'h','i'};
int i;
for(i=0;a[i]!='\0';i++){
char l = a[i];
printf("%c",a[i]);
}
printf("%s",a);
输出:HII♥喜♥♦
样本3:
char a [5]={'h','i'};
int i;
for(i=0;a[i]!='\0';i++){
printf("%c",a[i]);
}
printf("%s",a);
输出:hihi
为什么这三个程序的输出不同?
样本1和样本2几乎是类似的代码,除了额外的行char l = a[i]
,样本3与样本1和2的不同,基于数组大小的声明。
答案 0 :(得分:5)
在C中,数组只有一个大小,但没有终止符。因此,两个字符的数组(如前两个示例)将包含您指定的两个字符,而不是其他任何字符。当你循环寻找"终结者"你将走出界限并拥有未定义的行为。
第三种情况不同,因为您定义了一个包含五个元素的数组,但只初始化前两个元素。然后,C标准要求将数组的其余部分初始化为零,这与字符'\0'
相同。虽然第三个例子中的数组仍然没有明确的终止符,但它的初始值与字符串终止符的初始值相同。
答案 1 :(得分:3)
对于示例1和2,通过将非空终止数组作为参数传递给%s
中的printf()
来调用undefined behavior。
对于像
这样的定义 char a []={'h','i'};
a
将被分配内存以仅保存两个元素,将不会分配额外的空间来存储终止空值,在这种情况下使用大括号括起初始化列表。
引用章节§7.21.6.1,将%s
格式说明符与printf()
系列一起使用,
s
如果不存在l长度修饰符,则参数应为指向初始值的指针 字符类型数组的元素。 280) 数组中的字符是 写入(但不包括)终止空字符。如果 指定精度,不超过写入的多个字节。 如果 未指定精度或大于数组的大小,数组应该 包含空字符。
OTOH,如果是样本3,则用于
之类的定义char a [5]={'h','i'};
数组 以null结尾,因此输出正确。在这种情况下,数组以空值终止,因为,您在声明时提供了数组大小,并且在括号括起列表中提供了较少数量的initiliazers,因此其余元素初始化为0
(就好像他们有static
存储空间。相关的,C11
,章节§6.7.9,(强调我的)
如果括号括起的列表中的初始值设定项少于元素或成员 用于初始化已知数组的字符串文字中的聚合或更少字符 大小比数组中的元素大,聚合的其余部分应为 隐式初始化与具有静态存储持续时间的对象相同。
答案 2 :(得分:1)
要使printf("%s",a)
起作用,a
指向的内存块必须以0结尾。
以for (i=0; a[i]!='\0'; i++)
开头的代码也是如此。
在所有示例中,此内存块以'i'
结尾,而不是以0结尾。
您可以通过将a
的初始化更改为以下任一项来解决此问题:
char a[] = {'h','i',0};
char a[] = {'h','i','\0'};
char a[] = "hi";
char *a = "hi";