如何在printf占位符中使用等于表达式?

我有以下代码片段:

main( ) { int k = 35 ; printf ( "\n%d %d %d", k == 35, k = 50, k > 40 ) ; } 

产生以下输出

 0 50 0 

我不确定我是否理解printf的第一个值是如何变为0 。 当k的值与35比较时,理想情况下它应该返回(并因此打印)1,但它如何打印为零? 产生的其他两个值500都是正确的,因为在第二个值中,k的值取50 ,而对于第三个值,k的值( 35 )与40比较。 因为35 < 40 ,所以它打印0。

任何帮助将不胜感激,谢谢。

**更新**

在研究了关于这个主题以及undefined behavior ,我在一本关于C的书中看到了这一点,最后给出了源代码。

调用约定调用约定表示在遇到函数调用时参数被传递给函数的顺序。 这里有两种可能性:

  1. 参数可能从左向右传递。
  2. 参数可能从右到左传递。

C语言遵循第二顺序。

考虑以下函数调用:

 fun (a, b, c, d ) ; 

在这个调用中,参数是从左向右还是从右向左传递并不重要。 但是,在某些函数调用中,传递参数的顺序成为一个重要的考虑因素。 例如:

 int a = 1 ; printf ( "%d %d %d", a, ++a, a++ ) ; 

看来这个printf( )输出1 2 3 。 然而事实并非如此。 令人惊讶的是,它输出3 3 1

这是因为C的调用约定是right to left 。 也就是说,首先通过表达式a++传递1 ,然后将a递增到2 。 然后传递++a结果。 也就是说, a增加到3然后通过。 最后,传递a最新值,即3 。 因此, right to left order 1,3,3通过。 一旦printf( )收集它们,它就按照我们要求它们打印它们的顺序打印它们(而不是它们被传递的顺序)。 因此打印3 3 1

 **Source: "Let Us C" 5th edition, Author: Yashwant Kanetkar, Chapter 5: Functions and Pointers** 

无论这个问题是否重复,我发现这些新信息对我有帮助,所以决定分享。 注意:这也支持Mr.Zurg在下面的评论中提出的索赔。

不幸的是,对于那些读过那本书的人来说,这是完全错误的。 C99标准草案明确规定了此代码未定义的行为。 使用Wikipedia条目快速检查未定义的行为包含一个类似的示例,并将其标识为未定义。 我不会留下它,但还有其他容易获得的来源,这是正确的,而不必诉诸标准。

那标准说的是什么? 在第6.5节中, 表达3段说:

运算符和操作数的分组由语法表示.74)除了稍后指定的(对于函数调用(),&&,||,?:和逗号运算符),子表达式的评估顺序和顺序发生哪些副作用都是未指明的。

因此,除非指定,否则未指定子表达式的评估顺序,并且第6.5.2.2函数调用10段说:

函数指示符的评估顺序,实际参数和实际参数中的子表达式是未指定的,但在实际调用之前有一个序列点。

所以在你的例子中:

 printf ( "\n%d %d %d", k == 35, k = 50, k > 40 ) ; ^ ^ ^ ^ 1 2 3 4 

子表达式1 to 4可以按任何顺序进行评估,我们无法知道每个子表达式的副作用何时会发生,尽管我们知道它们都必须在实际调用函数之前生效。

因此, k = 50可以首先评估,也可以在其间或任何地方进行评估,无论何时进行评估,改变k值的副作用都可以在执行实际函数之后立即进行。 这意味着结果是不可预测的,可以想象在不同的​​执行期间改变变化。

接下来我们要解决这个问题如何成为未定义的行为。 第6.5节第2段对此进行了说明:

在前一个和下一个序列点之间,一个对象的存储值最多只能通过表达式的计算来修改一次.72)此外,先前的值应该是只读的,以确定要存储的值.73)

因此,在您的示例中,我们不会多次修改k ,但除了确定要存储的值之外,我们还使用先前值来执行其他操作。 那么未定义行为的含义是什么? 在未定义行为的定义中,标准说:

可能的未定义行为包括完全忽略不完整结果的情况,在翻译或程序执行期间以环境特征(有或没有发出诊断消息)的特定文档执行,终止翻译或执行(发布时)一条诊断信息)。

因此编译器可以忽略它,或者也可能产生不可预测的结果,这涵盖了相当多的不良行为。 臭名昭着的说:

当编译器遇到[给定的未定义构造]时,让恶魔飞出你的鼻子是合法的

这对我来说听起来很不可取。

Interesting Posts