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

Man*_*iri 1 c printf equality undefined-behavior assignment-operator

我有以下代码片段:

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

产生以下输出

0 50 0
Run Code Online (Sandbox Code Playgroud)

我不知道我理解的第一个值怎么printf0.当值k与之比较时35,理想情况下它应该返回(并因此打印)1,但它如何打印为零?产生的其他两个值 - 50并且0都是正确的,因为在第二个值中,k的值被视为50,而对于第三个值,k的值(即35)被比较40.因为35 < 40,所以它打印0.

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

**更新**

在研究了关于这个主题的更多内容之后undefined behavior,我在一本关于C的书中看到了这一点,最后给出了源代码.

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

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

C语言遵循第二顺序.

考虑以下函数调用:

fun (a, b, c, d ) ;
Run Code Online (Sandbox Code Playgroud)

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

int a = 1 ;
printf ( "%d %d %d", a, ++a, a++ ) ;
Run Code Online (Sandbox Code Playgroud)

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

这是因为C的调用约定来自right to left.也就是说,首先1通过表达式a++然后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**
Run Code Online (Sandbox Code Playgroud)

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

Sha*_*our 7

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

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

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

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

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

所以在你的例子中:

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

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

因此k = 50可以在第一个或最后一个或任何地方进行评估,无论何时进行评估,更改值的副作用都k可以在执行实际功能之后立即进行.这意味着结果是不可预测的,可以想象在不同的​​执行期间改变变化.

接下来我们必须解决这是如何成为未定义的行为.6.52节中对此进行了说明,其中说:

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

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

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

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

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

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