C语言程序设计案例精粹
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.6 逻辑运算符与逻辑表达式

逻辑运算符分为一元运算符(单目运算符)和二元运算符(双目运算符)两种,主要用于两个操作对象之间的连接。C语言中提供了三种逻辑运算符:逻辑与(&&)、逻辑或(||)和逻辑非(!)。前两种为双目运算符,具有左结合性。后一种为单目运算符,具有右结合性。逻辑运算符中非运算符(!)的优先级最高,高于算术运算符。逻辑与(&&)和逻辑或(||)的优先级相同,低于关系运算符,高于赋值运算符。例如,表达式!a<b && c>b等价于((!a)<b)&& (c>d)。

与关系表达式类似,逻辑表达式的值也有“真(1)”和“假(0)”两种。不同的运算符逻辑表达式的执行规则也不同。逻辑表达式中总是将0作为假,非0作为真。

逻辑表达式的一般形式为:

操作对象1逻辑运算符 操作对象2

其中操作对象和逻辑运算符之间可以有一个或多个空格,也可以没有空格。

3.6.1 逻辑与(&&)

逻辑与运算(&&)中参与运算的两个操作对象都为真(非0)时,结果才为真(1),否则为假(0)。例如,表达式-4<10 && 3!=0,表达式中&&优先级低于关系运算符<和!=,所以表达式等价于(-4<10)&&(3!=0),表达式-4<10和表达式3!=0都为真,因此原表达式的结果为真。

逻辑与运算符具有自左至右的结合性,因此C语言规定,当运算符左边为假时,即判断表达式为假,而不再判断运算符右边是否为真或假。例如,表达式0>2 && 3!=5,由于表达式0>2为假,因此便不再判断表达式3!=5,而直接认为原表达式为假(0)。

范例3.9 LogicAndOperater.c

LogicAndOperater.c程序分析逻辑与运算符的优先级与结合性,并分析逻辑与表达式执行过程中对两侧表达式执行顺序的影响。(光盘\chat3\ LogicAndOperater.c)

          01   #include<stdio.h>
          02   main()
          03   {
          04       int i=0, j=5, k=7;
          05       int op=0;
          06       op=i++&&j==k--;                      //逻辑与操作
          07       printf("i=%d, j=%d, k=%d, op=%d\n", i, j, k, op);
          08   }

程序第6行赋值运算符右侧表达式i++ && j==k--的执行顺序为:首先判断i++是否为真,若为真,则继续判断j==k--是否为真,若为真,则原表达式为真;若为假,则原表达式为假。若i++为假,则原表达式为假,并结束判断,将0赋给变量op。程序中由于i赋初值为0,因此表达式i++为0,即为假,所以原表达式为假(0),结束判断,将0赋给op。综上论述,程序并没有执行表达式j==k--,因此k的值并没有变化。

程序运行输出结果为:

i=1, j=5, k=7, op=0

作者心得:

若程序第4行在对i赋值时其值非0,例如i = 100,由于i++非0,为真,则程序将继续执行&&运算符右侧表达式j==k--,从而使变量k作--运算。读者可修改程序作验证。同时,对于逻辑运算符两侧表达式的判断,只要表达式非0,即作为真看待。

3.6.2 逻辑或(||)

逻辑或运算(||)中参与运算的两个操作对象只要有一个为真(非0),结果即为真(1),否则为假(0)。例如,表达式-4<10 || 3==0,表达式中||优先级低于关系运算符<和==,所以表达式等价于(-4<10)||(3==0),表达式-4<10为真,因此原表达式的结果为真。

逻辑或运算符具有自左至右的结合性,因此C语言规定,当运算符左边为真(非0)时,即判断表达式为真(1),而不再判断运算符右边是否为真或假。例如,表达式2 || 3!=5,由于常量2为真(非0),因此便不再判断表达式3!=5,而直接判断原表达式为真(1)。

范例3.10 LogicOrOperater.c

LogicOrOperater.c程序分别设置逻辑与表达式和逻辑或表达式。在范例3.7基础上进一步分析逻辑与表达式的执行顺序,分析逻辑或的执行顺序,并与逻辑与表达式进行比较。分析产生的结果。(光盘\chat3\ LogicOrOperater.c)

          01   #include<stdio.h>
          02   main()
          03   {
          04       int i=0, j=5, k=7;
          05       int op1=0, op2=0;
          06       op1=++i&&j==k--;                     //逻辑与操作
          07       op2=j++||j&&++k;                      //逻辑或操作
          08       printf("i=%d, j=%d, k=%d, op1=%d, op2=%d\n", i, j, k, op1, op2);
          09   }

程序第6行中,表达式++i && j==k--,等价于(++i)&&(j==k--)。由于表达式++i值为1,同时i作自增1运算,因此继续执行表达式j==k--,表达式k--值为7,然后k作自减运算,因此j==k--为假(0),所以原表达式++i && j==k—为假(0),并将0赋给op1。执行完程序第6行之后,i的值变为1,k的值变为6。

程序第7行中,对于表达式j++ || j&&++k,等价于(j++)||(j&&(++k))。首先判断j++是否为真,由于j初始值为5,因此表达式j++值为5,非0,因此判断原表达式为真(1),同时j作自增1运算。程序继续执行第8行,并放弃表达式j&&(++k)的执行,因此k的值保持为6。

程序运行输出结果为:

i=1, j=6, k=6, op1=0, op2=1

3.6.3 逻辑非(!)

逻辑非运算符(!)为一元运算符,该运算符只能放在操作对象左边。其一般表达形式为:

!操作对象

非运算符(!)和操作对象间可以有一个或多个空格,也可以没有空格(建议不加空格)。

逻辑非运算符优先级高于其他两种逻辑运算符,也高于算术运算符。当操作对象为真(非0)时,结果为假(0),操作对象为假(0)时,结果为真(1)。

范例3.11 LogicNoneOperator.c

LogicNoneOperator.c程序分析逻辑非表达式的真和假,验证当变量a为非0时,! a的含义,并分析复合逻辑运算时表达式的执行顺序与结果。(光盘\chat3\ LogicNoneOperator.c)

          01   #include<stdio.h>
          02   main()
          03   {
          04       int a=1, b=2, c=3;
          05       int op=0;
          06       op=! a||--b&&--b||! c-3;                    //逻辑与操作
          07       printf("a=%d, ! a=%d, ! ! a=%d\n", a, ! a, ! ! a);           //输出变量a的非(!)操作值
          08       printf("a=%d, b=%d, c=%d, op=%d\n", a, b, c, op);
          09   }

程序第6行中,表达式!a || --b && --b || !c-3等价于(!a) || ((--b) && ((--b) || (!c-3))),按照自左至右的运算顺序,表达式变为数值形式。

(!1) || ((--b) && ((--b) || (!c-3))),取非运算,转化为0 || ((--b) && ((--b) || (!c-3))),b作自减运算,转化为0 || (1 && ((--b) || (!c-3))),b作自减运算,转化为0 || (1 && (0 || (!3-3))),取非运算,转化为0 || (1 && (0 || (0-3))),化简为0 || (1 && (0 || -3)),化简为0 || (1 &&1),化简为0||1,化简为1

因此,原表达式的值为1,并将1赋给变量op。此时b的值为0。

程序第7行中,由于a为1,因此!a的值为0,对于!!a,由于非运算符为右结合性,因此等价于!(!a),等价于!(!1),等价于!(0),结果为1。

程序运行输出结果为:

a=1, !a=0, !!a=1

a=1, b=0, c=3, op=1