`printf("%.- 1s \ n"," foo")`是否调用未定义的行为?

时间:2017-06-19 19:58:45

标签: c printf language-lawyer standards undefined-behavior

根据the standards

  

每个转换规范都由字符%引入。后   %,以下按顺序出现:

     
      
  • 零个或多个标志[...]。
  •   
  • 可选的最小字段宽度。 [...]
  •   
  • 一个可选的精度,它给出了为s转换写入的最大字节数。精度采用句点(。)后跟[...]可选十进制整数的形式;
  •   
  • 可选长度修改器[...]。 +转换说明符[...]。
  •   
  • 可选的最小字段宽度。 [...]
  •   
  • 转换说明符字符[...]。
  •   

后来:

  

负精度参数就像精度一样   删去。

根据我对标准定义的解释,我期望printf("%.-1s\n", "foo")

我从标准中得到的第二个引用表明我们可以传递一个负精度参数,并且这个精度会被忽略。

因此,printf("%.-1s\n", "foo")应该等同于printf("%s\n", "foo"),它会显示"foo\n"并返回4

然而,这是我使用的系统(osx)上的实际printf("%.-1s\n", "foo")行为:

printf("%.-1s\n", "foo")显示" \n"并返回2

这显然与我的预期不同。

  • 我对标准的解释是错误的吗?
  • 这种行为是否未定义?
  • 传递负精度(编辑:没有星号)实际上可能吗?

3 个答案:

答案 0 :(得分:4)

N1570-§7.21.6.1/ p5:

  

如上所述,字段宽度或精度或两者可以用星号表示。 在这种情况下一个int参数提供字段宽度或精度。指定字段宽度或精度或两者的参数应在要转换的参数(如果有)之前(按此顺序)出现。负字段宽度参数被视为 - 标志,后跟正字段宽度。 采用负精度参数,就好像省略了精度一样

Standard指定仅当星号在格式字符串中用作精度并且负值作为参数传递时才适用,如下所示

printf("%.*s\n", -1, "foo");  // -1 will be ignored  

在第4段中它说:

  

[...]精度采用句点(.)的形式,后跟一个   星号*(稍后描述)或可选的十进制整数; [...]

但没有具体说明十进制整数是否应大于0(如7.21.6.2/p3部分中字段宽度scanf所述)。标准在这一点上看起来很模糊,结果可能与机器有关。

答案 1 :(得分:1)

  
      
  • 我对标准的解释是错误的吗?
  •   

我将你的解释总结为:

  

因此,printf("%.-1s\n", "foo")应该等同于printf("%s\n", "foo"),它会显示"foo\n"并返回4.

没有。您引用的关于被忽略的负精度参数的规定不适用于这种情况。该条款讨论了在格式字符串中将精度指定为*并将该值作为单独的printf参数传递的选项:

printf("%.*s\n", -1, "foo");

在这种情况下,负精度参数会导致printf()表现得好像没有指定精度。你的情况不同。

另一方面,标准不要求格式字符串中出现的精度值为非负十进制整数。它确实在其他几个地方使用“十进制整数”这一术语,包括在同一部分的早期,但在关于精度字段的段落中没有这样做。

  
      
  • 这种行为是否未定义?
  •   

没有。对所需语义有两种相互矛盾的解释(见下),但无论哪种方式,标准都定义了行为。它可以解释为

  • 如果在格式字符串中直接显示负精度值,则对负精度参数描述的行为也适用。这具有一致性的优点,它是您报告观察的行为。然而,

  • 标准的文字读数表明,当精度在格式字符串中表示为负十进制整数时,则该部分中描述的普通语义适用;对于s指令,负的精度表示要输出的最大字符数。

您观察到的行为与前面的解释不一致,但考虑到输出少于0个字节的实际困难,我不会感到惊讶,后者的解释没有成功实现。我倾向于猜测后者是你的实现试图实现的。

我怀疑在某个阶段无意中遗漏了为精确领域提供负值的可能性,但是有意或无意,标准似乎允许它。

答案 2 :(得分:1)

"%-5d"这样的格式,宽度不是-5;相反, - 符号是一个“标志字符”,表示一个值应该在给定宽度的字段中左对齐。对于宽度的使用“非负”与“ - ”是标志字符而不是标志的事实相关联。虽然标准没有规定精度必须是非负的,但很难想象任何非人为的目的可以通过说在句点和某些十进制数字之间遇到“ - ”的实现必须忽略这些数字的内容。有些实现可能会以某种方式处理事情,但很可能许多实现没有任何代码在该位置显式处理“ - ”,并且会在开头处将其视为“ - ”。格式或将其视为没有定义含义的任何其他字符,具体取决于哪个更方便。我认为没有理由将这两种行为视为“有缺陷”。