【问题标题】:printf("%d %d %d\n",++a, a++,a) output [duplicate]printf("%d %d %d\n",++a, a++,a) 输出 [重复]
【发布时间】:2025-11-25 23:00:01
【问题描述】:

可能重复:
Could anyone explain these undefined behaviors (i = i++ + ++i , i = i++, etc…)

我无法理解这个程序的输出(使用gcc)。

main()
{
  int a=10;
  printf("%d %d %d\n",++a, a++,a);
}

输出:

12 10 12

另外,请解释printf()的参数的评估顺序。

【问题讨论】:

  • 当你发布作业时,这没关系,至少表明你在把它发布到 SO 之前已经尝试解决这个谜题。只是分享你到目前为止的想法。其他的都太懒惰了。
  • 然后阅读更多......和适当的文档:函数指示符、实际参数和实际参数中的子表达式的评估顺序未指定,但在实际调用之前有一个序列点.
  • 不保证在 printf 中从右到左进行评估。

标签: c printf


【解决方案1】:

编译器将按照当时感觉的任何顺序评估printf 的参数。这可能是一个优化的事情,但不能保证:标准没有指定它们的评估顺序,也没有定义实现。没有办法知道。

但是标准规定的,在一次操作中修改同一个变量两次是未定义的行为; ISO C++03, 5[expr]/4:

在前一个和下一个序列点之间,一个标量对象的存储值最多只能通过表达式的计算修改一次。此外,只能访问先验值以确定要存储的值。对于完整表达式的子表达式的每个允许排序,都应满足本段的要求;否则行为未定义。

printf("%d %d %d\n",++a, a++,a); 可以做很多事情;以你期望的方式工作,或者以你永远无法理解的方式工作。

你不应该写这样的代码。

【讨论】:

  • 实际上,顺序是未指定的,不是实现定义的。不同之处在于编译器文档必须定义在实现定义的情况下会发生什么,而不是在未指定的情况下。
  • 啊,好吧--然后会更新答案
  • @Steve Jessop:如果编译器文档将实现定义的行为定义为未指定?
  • @Stephen Canon:那么实现不符合标准(1.3.5)。记录一些“未记录”的东西是没有意义的,写“这可以做任何事情”这句话并不是记录行为!不过,我敢肯定,您可能会争辩说,附带源代码的编译器是自记录的,无论如何,我怀疑任何人都不会因为他们的 C++ 实现是否符合规定而被告上法庭。
【解决方案2】:

AFAIK 函数调用的参数没有定义的评估顺序,每个编译器的结果可能会有所不同。在这种情况下,我可以猜测中间参数首先被评估,然后是第一个和第三个。

【讨论】:

  • 是的,依赖任何给定的评估顺序是一个常见的错误。它今天在这个编译器上工作,然后它停止在另一个编译器上工作。
  • 但我已经读到评估是在 printf 中从右到左完成的。那为什么会出现这个问题。
  • 顺序未指定,但是在同一个表达式中修改同一个左值两次(中间没有序列点)实际上是未定义的行为,因此可以做任何事情——包括崩溃。
  • 函数调用中的逗号分隔参数不是逗号运算符,它只是语法的一部分:foo(a, b) - 这不是逗号运算符; x = a, b - 这是一个逗号运算符。
  • printf 无法控制其参数的评估顺序,这取决于编译器。 printf 只是一个随机函数。
【解决方案3】:

正如 haggai_e 所暗示的,参数按以下顺序计算:中、左、右。 要完全理解为什么会出现这些特定数字,您必须了解增量是如何工作的。

a++ 的意思是“用 a 做某事,然后再增加它”。 ++a 的意思是“先增加一个,然后用新的值做一些事情”。

在您的特定示例中, printf 首先评估 a++,读取 10 并打印它,然后才将其递增到 11。 printf 然后评估 ++a,首先递增它,读取 12 并将其打印出来。 printf 计算的最后一个变量按原样 (12) 读取,并在没有任何更改的情况下打印。

虽然它们是按随机顺序评估的,但它们会按照您提到的顺序显示。这就是为什么你得到 12 10 12 而不是 10 12 12。

【讨论】:

  • 是的......你可以用同样的方式解释其他可能的输出:11 11 12 12 11 12 12 10 10 等等......但是有什么意义呢?知道评估顺序的规则(不保证)和增量的工作就足够了。
  • 我在解释输出,因为那是提问者的问题。每个内燃机的工作原理都相同,但您仍然需要一种类型的发动机来更好地了解每个内燃机的内部工作原理。现在用 Example 代替 Engine,用 printf 代替燃烧,这就是我的意思。
  • 除非你假设太多规律性。没有中间序列点的 a++ 和 a++ 的使用是未定义的。您描述的行为未指定。您无法以符合标准的方式解释发生了什么,只能解释一个特定的实现发生了什么。