将非空终止字符串传递给printf会导致意外值

时间:2015-06-23 18:40:25

标签: c string printf

这个C程序给出了一个奇怪的结果:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
   char str1[5] = "abcde";
   char str2[5] = " haha";

   printf("%s\n", str1);
   return 0;
}

当我运行此代码时,我得到:

abcde haha

我只想打印第一个字符串,从代码中可以看出 为什么要打印它们?

6 个答案:

答案 0 :(得分:34)

由于C字符串中的空终止字符,

"abcde"实际上是6个字节长。当你这样做时:

char str1[5] = "abcde";

您没有存储空终止字符,因此它不是正确的字符串。

执行此操作时:

char str1[5] = "abcde";
char str2[5] = " haha";
printf("%s\n", str1);

恰好第二个字符串存储在第一个字符串之后,尽管这不是必需的。通过在非空终止的字符串上调用printf,您已经导致了未定义的行为。

更新

正如clcto的评论中所述,可以通过不明确指定数组的大小并让编译器根据字符串来确定它来避免这种情况:

char str1[] = "abcde";

或者使用指针,如果它适用于您的用例,尽管它们不相同:

char *str1 = "abcde";

答案 1 :(得分:19)

字符串str1str2都不会以空值终止。因此声明

 printf("%s\n", str1);  

将调用未定义的行为 printf逐个打印字符串中的字符,直到遇到字符串中不存在的'\0'为止。在这种情况下,printf继续超过字符串的结尾,直到它在内存中的某处找到空字符。在你的情况下,似乎printf超过字符串"abcde"的结尾并继续打印第二个字符串" haha"中的字符,这些字符偶然位于内存中的第一个字符串之后。

最好更改块

   char str1[5] = "abcde";
   char str2[5] = " haha";  

 char str1[] = "abcde";
 char str2[] = " haha";  

避免这个问题。

答案 2 :(得分:11)

从技术上讲,此行为不是意外,它是未定义:您的代码正在向缺少printf的空终止符的C字符串传递指针,这是未定义的行为。

但是,在您的情况下,编译器会在内存中背靠背放置两个字符串,因此printf在打印null后会运行到str2终止符,这解释了结果你得到了。

如果您只想打印第一个字符串,请为null终止符添加空格,如下所示:

char str1[6] = "abcde";

更好的是,让编译器为您计算正确的大小:

char str1[] = "abcde";

答案 3 :(得分:6)

您已调用未定义的行为。这里:

   char str1[5] = "abcde";

str1具有上述五个字母的空间,但没有空终止符。 然后,您尝试将非空终止字符串传递给printf进行打印的方式,调用未定义的行为。

通常,在大多数情况下,将非空终止字符串传递给期望(C)字符串的标准函数并不是一个好主意。

答案 4 :(得分:4)

在C这样的声明中

char str1[5] = "abcde";

是允许的。实际上有6个初始化器,因为字符串文字包括终止零。但是在左侧声明了一个只有5个元素的字符数组。所以它不包括终止零。

看起来像是

char str1[5] = { 'a', 'b', 'c', 'd', 'e', '\0' };

如果要在C ++中编译此声明,则编译器会发出错误。

最好声明数组而不明确指定其大小。

char str1[] = "abcde";

在这种情况下,它的大小将等于字符串文字中的字符数,包括终止零等于6.你可以写

printf("%s\n", str1);

否则该函数会继续打印数组之外的字符,直到它符合零字符。

尽管如此,这不是一个错误。只需在printf调用中正确指定格式说明符:

printf("%5.5s\n", str1);

您将获得预期的结果。

答案 5 :(得分:-1)

%s说明符搜索空终止。因此,您需要将'\0'添加到字符串的末尾

char str1[6] = "abcde\0";