【问题标题】:how will this expression will evaluate in C?这个表达式将如何在 C 中求值?
【发布时间】:2017-01-14 03:46:46
【问题描述】:

我不知道如何在 C 中计算这个表达式? 我对 printf 中表达式的评估有点困惑? 如果 printf 中的表达式从右到左计算,那么表达式的计算应该在遇到 (c>10) 后停止,但它在输出屏幕上打印了 '1'?

这不是 C 的确切语法,而是问我的一个问题。

integer a = 50, b = 25, c = 0;
printf( a > 45 || b > 50 && c > 10 );

【问题讨论】:

  • a > 45 || b > 50 && c > 10 将分组为(a > 45 )|| (b > 50 && c > 10)。子表达式的求值顺序从左到右。
  • 请不要写这样的代码,多加几个括号不会伤害任何人。
  • 我忘了补充一点,除了||, &&?: 之外,没有定义运算符操作数的求值顺序。所以,不要一概而论我在第一条评论中提到的顺序。
  • "如果 printf 中的表达式从右到左求值..." 你为什么会考虑从右到左求值作为你的第一个猜测? “从右到左求值”的想法从何而来?

标签: c printf


【解决方案1】:

这个表达式

a > 45 || b > 50 && c > 10

等价于表达式

( a > 45 ) || ( b > 50 && c > 10 )

因此,如果此子表达式 ( a > 45 ) 的计算结果为 true,则第二个子表达式将不会计算并且结果等于 1(int 类型)。

否则这个子表达式

( b > 50 && c > 10 )

等价于

( b > 50 ) && ( c > 10 )

如果( b > 50 ) 的计算结果为假,则整个结果为假(C 中的 0)并且第二个子表达式不会计算。否则结果为 子表达式c > 10。如果 c > 10 则结果是 int 类型的对象,值为 1,如果不是 ( c > 10 ),则结果值为 0。

如果变量具有问题中显示的值

int a = 50, b = 25, c = 0;

然后是第一个子表达式

a > 45

计算结果为 true,结果为 int 类型的 1。第二个子表达式

b > 50 && c > 10

甚至不会评估。

考虑以下演示程序

#include <stdio.h>

int f( int x )
{
    printf( "f is called for %d\n", x );
    return x;
}

int main( void ) 
{
    int a = 50, b = 25, c = 0;

    f( f( a ) > 45 || f( b ) > 50 && f( c ) > 10 ); 

    return 0;
}

它的输出是

f is called for 50
f is called for 1

如您所见,计算表达式f( a ) &gt; 45 就足以得到等于1 的结果。

【讨论】:

  • 你太棒了。这个概念不能比这更清楚地解释了。谢谢你回答我的问题。
【解决方案2】:

这将是未定义的行为。 printf 的第一个参数必须是格式字符串。它很可能会崩溃。拯救你的是“整数”不是一种类型,所以它不会编译。

【讨论】:

  • 应该是我负责吧。我修复了格式,使它看起来像精确的代码,但 OP 已经提到 这不是 C 的精确语法,所以我猜 OP 知道这一点。
【解决方案3】:

||&amp;&amp; 运算符都强制从左到右求值。在评估右侧操作数之前,将完全评估左侧操作数(并应用所有副作用)。

此外,两个运算符短路 - 根据左侧操作数的值,右侧操作数可能根本不计算。

对于

a || b

如果a 为真,那么无论b 的值如何,整个表达式 都为真,因此不计算b

同样,对于

a && b

如果a 为假,那么无论b 的值如何,整个表达式 都是假的,因此不会评估b

&amp;&amp; 的优先级高于||,所以

a || b && c 

将被解析为

a || (b && c)

a && b || c 

将被解析为

(a && b) || c  

所以...

a > 45 || b > 50 && c > 10

被解析为

a > 45 || (b > 50 && c > 10 )

由于a == 50a &gt; 45 为真。由于a &gt; 45|| 运算符的左侧操作数,因此无论右侧操作数如何,整个表达式都为真,因此根本不计算b &gt; 50 &amp;&amp; c &gt; 10

表达式的结果是1(真)。

不幸的是,printf 期望它的第一个参数指向一个字符串(格式字符串),而1 很可能不是您平台上的有效地址,因此这段代码的结果很可能是段错误。简单的解决方法是编写

printf( "%d\n", a > 45 || b > 50 && c > 10 );

【讨论】:

    【解决方案4】:

    表达式出现的上下文影响是否评估它,但不影响如何。对较大表达式的子表达式求值的顺序由operator precedence and associativity 控制。

    在表达式中的运算符中,&gt; 的优先级最高,然后是 &amp;&amp;,然后是 ||。所有从左到右的联想。因此,您的表达式的计算方式与这样编写时完全相同:

    (a > 45) || ((b > 50) && (c > 10))
    

    此外,&amp;&amp;|| 运算符执行短路评估。这对您来说似乎是一个困惑点。短路求值意味着如果&amp;&amp;|| 操作的结果由左侧操作数的值确定,则不评估该操作的右侧操作数 .这不会影响是否或如何评估 其他 操作,除非短路操作的结果用作操作数。

    但是,在这种情况下,因为|| 关联从左到右,所以首先评估a &gt; 45,产生1 (true)。因为这决定了|| 操作的结果,所以它的右手操作数不被计算。相对运算符优先级产生的右侧操作数是整个表达式的其余部分,如上所示,因此不会计算任何一个。然而,即使它被评估,整个表达式的结果仍然是1,因为左侧操作数的计算结果为1,而不管右侧的子表达式如何。这就是为什么 || 的右侧不需要计算的原因。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-23
      • 1970-01-01
      相关资源
      最近更新 更多