在`printf`中通过格式说明符传递空字节

19

为什么当我使用ASCII表中的空字符时,printf会打印一个空格而不是停止?我的意思是:

printf("Hello%c, world", 0); //Hello , world
printf("Hello%c, world", '\0'); //Hello , world

只有在字符串本身中放置转义字符时,printf 才停止输出字符串:

printf("Hello\0, world"); //Hello

我在Windows 8,Windows 10(使用cygwin、MinGW、Netbeans、Code::Blocks),XUbuntu上尝试了这个方法,结果都一样。

问题出在哪里?我问了一个朋友,但他说他没有这样的问题,三个例子都执行得很好。


2
因为在ASCII表中,0是NULL/零终止符的编号,并且它的转义字符等价于'\0'。 - sofia.bul
2
@rozina,因为'\0'是空终止字符? - David Ranieri
这个问题同样适用于C++和C,它们都有相同的“printf”规范,因此我认为应该有两个标签。 - emlai
1
@rozina 我的问题很基本。我是C语言和编程方面的新手,正在努力理解逻辑、编程和特别是C语言。所以不要惊讶,我选择了这样一个毫无意义的例子。重点是,根据我的教练,所有三个示例都必须打印相同的输出,但它们并没有。 - sofia.bul
1
@rozina,我知道,我只是在回答你的问题:“为什么你认为前两个应该只打印Hello?” - David Ranieri
显示剩余12条评论
4个回答

25

printf("Hello\0, world"); 使用它的参数作为一个 C 字符串,因此它会解码字符串直到找到一个空字符(NUL),所以它仅在遇到 \0 后停止,忽略后续内容。

printf("Hello%c, world", 0); 解码其参数(直到在其中找到空字符 - 即在 d 之后),同时它找到了一个 %c,所以它用参数给定的字符(其 ASCII 码是 NUL)替换它,并向终端发送一个 NUL 字符,然后继续运行。

Printf 手册说明:

这些函数根据指定如何转换输出的格式字符串控制输出,包括后续参数[...]。


1
你有关于这个标准的任何参考资料吗? - emlai
2
这是C字符串和printf格式的定义。 - Jean-Baptiste Yunès
1
问题仍然存在 - 为什么在某些编译器(如我朋友的编译器)中,这三个示例执行相同。 - sofia.bul
1
哪些编译器?你确定你的朋友真的测试过吗? - Jean-Baptiste Yunès
是的,我相信他。他在Linux环境中使用NetBeans。我会深入调查这件事,并在有误解的情况下通知您。 - sofia.bul
显示剩余2条评论

6
你正在依赖于printf()的实现细节。低级终端输出函数需要字符串长度作为参数。printf()有两种方法来做到这一点。
一种显而易见的方法是格式化字符串,然后使用strlen()。这是你希望的方法。
但这很低效,因为它需要对字符串缓冲区进行双重遍历并追加0。另一种方法是在替换字段时跟踪格式化字符串的长度,每追加一个字符就简单地增加它。由于它继续超过%c,所以现在你将得到包括%c之后所有内容的更大长度。嵌入式0的终端函数处理方式也是一个实现细节,因为它不是可打印字符。看到它被替换成空格并不罕见。
明智的做法是不要依赖实现细节。

1
printf("Hello%c, world", 0); //Hello , world
printf("Hello%c, world", '\0'); //Hello , world

在这两种情况下,您正在尝试打印与字符代码 0 对应的字符值,这不是可打印字符。我没有找到关于此的详细说明,但我怀疑尝试打印null字符值的行为未指定,甚至可能未定义。无论哪种方式,在这种情况下,我都不希望它被视为字符串终止符。
printf("Hello\0, world"); //Hello

在这种情况下,空字符是字符串常量的一部分,并被编译器解释为字符串终止符。

NUL字符被定义为输出的一种NOP。 - Jean-Baptiste Yunès
1
@Jean-BaptisteYunès:章节和节选?我在标准中没有找到任何明确的语言,但在iPad上搜索pdf文件有点麻烦。 - John Bode
ASCII标准。7.24 NULNULL)。控制字符用于完成媒体填充或时间填充。可以在数据流中插入或删除NUL字符,而不影响该流的信息内容,但这样的操作可能会影响信息布局和/或设备控制。 - Jean-Baptiste Yunès
@Jean-BaptisteYunès:非ASCII实现怎么样?我在C标准中寻找描述使用%c打印空字符时应发生的情况的语言,但我找不到任何信息。这让我认为该行为未定义,任何结果都有可能。 - John Bode
1
@JohnBode C标准只涵盖了C语言。打印字符与语言无关,而与打印该字符的设备有关(这就是ASCII标准所涵盖的内容)。如果我们按照您的逻辑,打印任何字符都将是“未定义行为”。 - Maël Nison
1
@MaëlNison:相反,5.2.2描述了字符显示语义,包括非图形控制字符(警报、换页、换行等)的预期行为。 “将打印字符(由isprint函数定义)写入显示设备的意图是在活动位置显示该字符的图形表示,然后将活动位置移动到当前行的下一个位置。”空字符不是打印字符,也没有相应的图形表示。我认为这种行为是未定义的,但我不确定。 - John Bode

0
简而言之: %c 表示打印一个字符,所以 printf 打印的是值为 0 的 NUL 字符。 NUL 是不可打印的字符。因此我们只能看到一个空格。
"Hello\0, world" 是一个字符串字面量,strlen("Hello\0, world") 的结果是 5。因此,printf 将打印出 "Hello"。
您可以在 cppreference 网站上了解更多信息:string literal

字符串字面量是由双引号括起来的零个或多个多字节字符序列,例如 "xyz"。空字符('\0')总是附加到字符串字面量中,因此,字符串字面量 "Hello" 是一个 const char[6],其中包含字符 'H'、'e'、'l'、'l'、'0' 和 '\0'。如果字符串字面量中嵌入了空字符,则表示它包含多个字符串的数组。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接