C中几乎相同程序的不同输出

时间:2016-11-03 08:24:10

标签: c arrays string char printf

样本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的不同,基于数组大小的声明。

3 个答案:

答案 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";