我正在尝试发现 printf 的所有功能,并且我已经尝试过:
printf("Test:%+*0d", 10, 20);
打印
测试:%+100d
我首先使用了 flag +
,然后是宽度*
和重新使用 flag 0
。
为什么要输出这个?我故意printf()
以不好的方式使用,但我想知道为什么它显示数字 100?
我正在尝试发现 printf 的所有功能,并且我已经尝试过:
printf("Test:%+*0d", 10, 20);
打印
测试:%+100d
我首先使用了 flag +
,然后是宽度*
和重新使用 flag 0
。
为什么要输出这个?我故意printf()
以不好的方式使用,但我想知道为什么它显示数字 100?
这是因为,您正在向编译器提供语法上的废话,所以它可以自由地做任何它想做的事情。相关阅读,未定义的行为。
在启用警告的情况下编译您的代码,它会告诉您类似的信息
警告:格式为 [-Wformat=] 的未知转换类型字符“0”
printf("Test:%+*0d", 10, 20);
^
正确地说,该陈述应该是
printf("Test:%+*.0d", 10, 20); // note the '.'
其中,0
用作精度
相关,引用C11
第 7.21.6.1 章,(强调我的)
一个可选精度,给出 , , , , ,和转换出现的最小位数
d
,,i
,o
,u
, ,x
和X
转换的小数点字符后出现的位数,最大有效位数和转换,或为 s 转换写入的最大字节数。精度采用句点 ( ) 后跟星号(稍后描述)或可选十进制整数的形式;a
A
e
E
f
F
g
G
.
*
如果仅指定周期,则精度为零。如果精度与任何其他转换说明符一起出现,则行为未定义。
printf("Test:%+0*d", 10, 20);
其中,0
用作标志。根据语法,所有标志应该一起出现,在任何其他转换规范条目之前,您不能将它放在转换规范中的任何位置并期望编译器遵循您的意图。
再次引用,(和我的重点)
每个转换规范都由字符引入
%
。之后%
,依次出现以下内容:
- 零个或多个标志(以任何顺序)[...]
- 可选的最小字段宽度[...]
- 可选精度[...]
- 可选的长度修饰符[...]
- 转换说明符[ ....]
补充Sourav Ghosh 的回答;一个重要的概念是未定义的行为,这很棘手。请务必阅读 Lattner 的博客:每个 C 程序员都应该了解的关于未定义行为的知识。另请参阅此。
因此,故意(或可能取决于)在代码中留下一些未定义的行为是故意的渎职行为。不要那样做。在极少数情况下您想这样做(我看不到任何情况),请记录下来并在一些评论中证明自己的合理性。
请注意,如果确实printf
是由 C 标准库实现的,它可以(并且通常是)由编译器专门处理(使用GCC和 GNU libc,使用 internal 可能会发生这种魔法__builtin_printf
)
C99 和 C11 标准部分指定了 的行为,printf
但确实留下了一些未定义的行为案例以简化实现。您不太可能完全理解或能够模仿这些案例。并且实现本身可能会改变(例如,在我的 Debian Linux 上,升级libc
可能会改变 的未定义行为printf
)
如果您想了解更多,请printf
研究一些 C 标准库实现的源代码(例如musl-libc,其代码可读性很强)和 GCC 实现的源代码(假设是 Linux 操作系统)。
但是 GNU libc 和 GCC(甚至是 Linux 内核,通过系统调用)的维护者可以自由地更改未定义的行为(printf
以及其他任何东西)
在实践中, 如果使用GCCgcc -Wall
,请始终使用(并且可能也)编译。不要接受任何警告(所以改进你自己的代码,直到你没有得到任何警告)。-g
您的printf
格式不正确:标志必须在宽度说明符之前。
在它*
作为宽度说明符处理后,printf
需要一个.
或一个长度修饰符或一个转换说明符,0
如果不是这些,则行为未定义。
您的库的实现printf
做了一些奇怪的事情,它似乎*
通过用实际的宽度参数替换它来处理......实现的副作用。其他人可能会做其他事情,包括中止程序。%s
如果随后进行转换,这种格式错误将特别危险。
将代码更改为printf("Test:%+0*d", 10, 20);
应该产生预期的输出:
Test:+000000020