`printf("%.-1s\n", "foo")` 会调用未定义的行为吗?
Does `printf("%.-1s\n", "foo")` invoke undefined behaviour?
根据the standards:
Each conversion specification is introduced by the character %. After
the %, the following appear in sequence:
- Zero or more flags [...].
- An optional minimum field width. [...]
- An optional precision that gives [...] the maximum number of bytes to be written for s conversions. The precision takes the form of a period (.) followed by [...] an optional decimal integer;
- An optional length modifier [...]. + A conversion specifier character [...].
- An optional minimum field width. [...]
- A conversion specifier character [...].
以后:
A negative precision argument is taken as if the precision were
omitted.
根据我对标准定义的解释,我对 printf("%.-1s\n", "foo")
的期望是:
我从标准中引用的第二个引述表明我们可以传递一个负精度参数,这样的精度将被忽略。
所以,printf("%.-1s\n", "foo")
应该等同于 printf("%s\n", "foo")
,这将显示 "foo\n"
和 return 4
.
然而,这是我使用的系统 (osx) 上的实际 printf("%.-1s\n", "foo")
行为:
printf("%.-1s\n", "foo")
显示 " \n"
和 returns 2
.
这显然与我的预期不同。
- 是不是我对标准的理解有误?
- 此行为是否未定义?
- 传递负精度(编辑:不带星号)真的可行吗?
N1570-§7.21.6.1/p5:
As noted above, a field width, or precision, or both, may be indicated by an asterisk. In this case, an int argument supplies the field width or precision. The arguments specifying field width, or precision, or both, shall appear (in that order) before the argument (if any) to be converted. A negative field width argument is taken as a - flag followed by a positive field width. A negative precision argument is taken as if the precision were omitted.
标准指定仅当星号用作格式字符串中的精度并且负值作为参数传递时才适用,如下所示
printf("%.*s\n", -1, "foo"); // -1 will be ignored
第 4 段说:
[...] The precision takes the form of a period (.
) followed either by an
asterisk *
(described later) or by an optional decimal integer; [...]
但它没有具体说明十进制整数是否应大于 0
(如 7.21.6.2/p3 节中字段宽度 scanf
所述)。标准在这一点上似乎模棱两可,结果可能取决于机器。
- Is somehow my interpretation of the standards wrong?
我把你的解释总结为:
So, printf("%.-1s\n", "foo")
should be equivalent to printf("%s\n",
"foo")
, which would display "foo\n"
and return 4.
没有。您引用的关于负精度 arguments 被忽略的规定不适用于这种情况。该条款讨论的是在格式字符串中将精度指定为 *
并将该值作为单独的 printf
参数传递的选项:
printf("%.*s\n", -1, "foo");
在这种情况下,负精度参数导致 printf()
的行为就好像没有指定精度一样。你的情况不同。
另一方面,这里的标准并不要求格式字符串中出现的精度值是非负十进制整数。它确实在其他几个地方以这种方式限定了术语 "decimal integer",包括在同一部分的前面,但在关于精度字段的段落中没有这样做。
- Is this behaviour undefined?
没有。对所需语义有两种相互矛盾的解释(见下文),但无论哪种方式,标准都定义了行为。它可以被解释为
当负精度值直接出现在格式字符串中时,针对负精度参数描述的行为也适用。这具有一致性的优势,并且这是您报告观察到的行为。然而,
标准的字面解读表明,当精度在格式字符串中表示为十进制负整数时,该部分中描述的普通语义适用;对于 s
指令,负精度表示要输出的最大字符数。
您观察到的行为与前一种解释不一致,但考虑到输出少于 0 字节的实际困难,后一种解释没有成功实施对我来说不足为奇。我倾向于猜测后者是您的实现试图实现的。
我怀疑在某个阶段无意遗漏了为精度字段提供负值的可能性,但无论是否有意,标准似乎都允许这样做。
在像"%-5d"
这样的格式中,宽度不是-5;相反,- 符号是 "flag character",表示值应在给定宽度的字段中左对齐。使用 "non-negative" 作为宽度与“-”是标志字符而不是符号这一事实有关。虽然标准没有指定精度必须是非负数,但很难想象任何非人为的目的会通过说在句点和一些小数位之间遇到“-”的实现必须忽略这些数字的内容。某些实现可能会以这种方式处理事情,但很可能许多实现没有任何代码来显式处理该位置的“-”,并且会将其视为与开头的“-”相同格式或将其视为任何其他没有定义含义的字符,具体取决于哪个更方便。我认为没有理由将这两种行为视为 "defective".
根据the standards:
Each conversion specification is introduced by the character %. After the %, the following appear in sequence:
- Zero or more flags [...].
- An optional minimum field width. [...]
- An optional precision that gives [...] the maximum number of bytes to be written for s conversions. The precision takes the form of a period (.) followed by [...] an optional decimal integer;
- An optional length modifier [...]. + A conversion specifier character [...].
- An optional minimum field width. [...]
- A conversion specifier character [...].
以后:
A negative precision argument is taken as if the precision were omitted.
根据我对标准定义的解释,我对 printf("%.-1s\n", "foo")
的期望是:
我从标准中引用的第二个引述表明我们可以传递一个负精度参数,这样的精度将被忽略。
所以,printf("%.-1s\n", "foo")
应该等同于 printf("%s\n", "foo")
,这将显示 "foo\n"
和 return 4
.
然而,这是我使用的系统 (osx) 上的实际 printf("%.-1s\n", "foo")
行为:
printf("%.-1s\n", "foo")
显示 " \n"
和 returns 2
.
这显然与我的预期不同。
- 是不是我对标准的理解有误?
- 此行为是否未定义?
- 传递负精度(编辑:不带星号)真的可行吗?
N1570-§7.21.6.1/p5:
As noted above, a field width, or precision, or both, may be indicated by an asterisk. In this case, an int argument supplies the field width or precision. The arguments specifying field width, or precision, or both, shall appear (in that order) before the argument (if any) to be converted. A negative field width argument is taken as a - flag followed by a positive field width. A negative precision argument is taken as if the precision were omitted.
标准指定仅当星号用作格式字符串中的精度并且负值作为参数传递时才适用,如下所示
printf("%.*s\n", -1, "foo"); // -1 will be ignored
第 4 段说:
[...] The precision takes the form of a period (
.
) followed either by an asterisk*
(described later) or by an optional decimal integer; [...]
但它没有具体说明十进制整数是否应大于 0
(如 7.21.6.2/p3 节中字段宽度 scanf
所述)。标准在这一点上似乎模棱两可,结果可能取决于机器。
- Is somehow my interpretation of the standards wrong?
我把你的解释总结为:
So,
printf("%.-1s\n", "foo")
should be equivalent toprintf("%s\n", "foo")
, which would display"foo\n"
and return 4.
没有。您引用的关于负精度 arguments 被忽略的规定不适用于这种情况。该条款讨论的是在格式字符串中将精度指定为 *
并将该值作为单独的 printf
参数传递的选项:
printf("%.*s\n", -1, "foo");
在这种情况下,负精度参数导致 printf()
的行为就好像没有指定精度一样。你的情况不同。
另一方面,这里的标准并不要求格式字符串中出现的精度值是非负十进制整数。它确实在其他几个地方以这种方式限定了术语 "decimal integer",包括在同一部分的前面,但在关于精度字段的段落中没有这样做。
- Is this behaviour undefined?
没有。对所需语义有两种相互矛盾的解释(见下文),但无论哪种方式,标准都定义了行为。它可以被解释为
当负精度值直接出现在格式字符串中时,针对负精度参数描述的行为也适用。这具有一致性的优势,并且这是您报告观察到的行为。然而,
标准的字面解读表明,当精度在格式字符串中表示为十进制负整数时,该部分中描述的普通语义适用;对于
s
指令,负精度表示要输出的最大字符数。
您观察到的行为与前一种解释不一致,但考虑到输出少于 0 字节的实际困难,后一种解释没有成功实施对我来说不足为奇。我倾向于猜测后者是您的实现试图实现的。
我怀疑在某个阶段无意遗漏了为精度字段提供负值的可能性,但无论是否有意,标准似乎都允许这样做。
在像"%-5d"
这样的格式中,宽度不是-5;相反,- 符号是 "flag character",表示值应在给定宽度的字段中左对齐。使用 "non-negative" 作为宽度与“-”是标志字符而不是符号这一事实有关。虽然标准没有指定精度必须是非负数,但很难想象任何非人为的目的会通过说在句点和一些小数位之间遇到“-”的实现必须忽略这些数字的内容。某些实现可能会以这种方式处理事情,但很可能许多实现没有任何代码来显式处理该位置的“-”,并且会将其视为与开头的“-”相同格式或将其视为任何其他没有定义含义的字符,具体取决于哪个更方便。我认为没有理由将这两种行为视为 "defective".