将非空终止字符串传递给printf会导致意外值
这个C程序给出了一个奇怪的结果:
#include #include int main(int argc, char *argv[]) { char str1[5] = "abcde"; char str2[5] = " haha"; printf("%s\n", str1); return 0; }
当我运行这段代码时,我得到:
abcde haha
我只想打印第一个字符串,从代码中可以看出。
为什么要打印它们?
由于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";
字符串str1
和str2
都不是以null结尾。 因此声明
printf("%s\n", str1);
将调用未定义的行为。
printf
逐个打印字符串中的字符,直到遇到字符串中不存在的'\0'
。 在这种情况下, printf
继续超过字符串的结尾,直到它在内存中的某处找到空字符。 在你的情况下,似乎printf
超过了字符串"abcde"
的结尾并继续打印第二个字符串" haha"
的字符,这是偶然位于内存中的第一个字符串之后。
更好地改变块
char str1[5] = "abcde"; char str2[5] = " haha";
至
char str1[] = "abcde"; char str2[] = " haha";
避免这个问题。
从技术上讲,这种行为并不是意料之外的 ,它是未定义的 :您的代码将指向C字符串的指针传递给缺少printf
null终止符,这是未定义的行为。
但是,在您的情况下,编译器会在内存中背靠背放置两个字符串,因此printf
在打印str2
后会运行到null
终止符,这会解释您得到的结果。
如果您只想打印第一个字符串,请为null终止符添加空格,如下所示:
char str1[6] = "abcde";
更好的是,让编译器为您计算正确的大小:
char str1[] = "abcde";
您已调用未定义的行为。 这里:
char str1[5] = "abcde";
str1
具有上述五个字母的空间,但没有空终止符。 然后,您尝试传递非null终止字符串到printf
进行打印的方式,调用未定义的行为。
通常,在大多数情况下,将非空终止字符串传递给期望(C)字符串的标准函数并不是一个好主意。
在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);
你会得到预期的结果。
%s
说明符搜索空终止。 因此,您需要在字符串的末尾添加'\0'
char str1[6] = "abcde\0";