-2

我正在尝试发现 printf 的所有功能,并且我已经尝试过:

printf("Test:%+*0d", 10, 20);

打印

测试:%+100d

我首先使用了 flag +,然后是宽度*和重新使用 flag 0

为什么要输出这个?我故意printf()不好的方式使用,但我想知道为什么它显示数字 100?

4

3 回答 3

2

这是因为,您正在向编译器提供语法上的废话,所以它可以自由地做任何它想做的事情。相关阅读,未定义的行为

在启用警告的情况下编译您的代码,它会告诉您类似的信息

警告:格式为 [-Wformat=] 的未知转换类型字符“0”
printf("Test:%+*0d", 10, 20);
^

正确地说,该陈述应该是

  • printf("Test:%+*.0d", 10, 20); // note the '.'

    其中,0用作精度

    相关,引用C11第 7.21.6.1 章,(强调我的)

    一个可选精度,给出 , , , , ,和转换出现的最小位数d,, i, o, u, ,xX转换的小数点字符后出现的位数,最大有效位数和转换,或为 s 转换写入的最大字节数。精度采用句点 ( ) 后跟星号(稍后描述)或可选十进制整数的形式;aAeEfFgG.*如果仅指定周期,则精度为零。如果精度与任何其他转换说明符一起出现,则行为未定义。

  • printf("Test:%+0*d", 10, 20);

    其中,0用作标志。根据语法,所有标志应该一起出现,在任何其他转换规范条目之前,您不能将它放在转换规范中的任何位置并期望编译器遵循您的意图

    再次引用,(和我的重点

    每个转换规范都由字符引入%。之后%依次出现以下内容:

    • 零个或多个标志(以任何顺序)[...]
    • 可选的最小字段宽度[...]
    • 可选精度[...]
    • 可选的长度修饰符[...]
    • 转换说明符[ ....]
于 2016-11-13T14:58:52.553 回答
1

补充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

于 2016-11-13T15:22:47.540 回答
1

您的printf格式不正确:标志必须在宽度说明符之前。

在它*作为宽度说明符处理后,printf需要一个.或一个长度修饰符或一个转换说明符,0如果不是这些,则行为未定义。

您的库的实现printf做了一些奇怪的事情,它似乎*通过用实际的宽度参数替换它来处理......实现的副作用。其他人可能会做其他事情,包括中止程序。%s如果随后进行转换,这种格式错误将特别危险。

将代码更改为printf("Test:%+0*d", 10, 20);应该产生预期的输出:

Test:+000000020
于 2016-11-13T15:37:29.510 回答