C++条件运算符 a ? b : c ; 是右结合的,但是这个右结合要怎么理解呢?

对于a ? b : c ? d : e这样的表达式如果按照右结合来解读的话,那不应该是先运算c,然后返回d或者e,返回后再参与到a ? b : d / e;这样的运算的吗?

但实际代码的编译测试的结果显然大家都已经知道并非如此,是先计算a,或者返回b,或者返回 (c ? d : e)这个整体的结果。

为什么是先计算a,而不是先计算c?右结合到底要怎么理解?网上的回答基本上都是错误的,个别的说法是对的,但是不完全准确,下面就具体说说这个右结合的理解。

先来看三个例子:

1. a + b*c - d;

2. a = b = c;

3. f()*g() / g();

上面的这三个例子分别涉及了表达式运算符的优先级、结合律、求值顺序。

对于第一个例子,因为乘法运算符(*)的优先级比加减运算符("+","-")的优先级高,所以乘法优先,乘法结合的更紧密,故而第一个例子相当于 a + (b*c) - d;

对于第二个例子,同一个运算对象b,有两个赋值运算符("="),同一运算符或者属于同一组的运算符的优先级是相等的,这时就无法根据优先级来判断谁优先,谁结合紧密,因此需要另外的东西来判定,这就是结合律。

结合律只用于表达式中同一个对象左右出现两个相同优先级的操作符的情况,用于消除歧义。相当于添加()来调节优先级

根据结合律来判断谁优先结合或者结合的更紧密,由于赋值运算符("=")是右结合的,因此右边优先,将b及其右边视为一个整体,从右向左计算,即 a = b = c;相当于 a = (b = c);而b=c;又是一个子表达式,先对子表达式进行运算,即将c的值赋给b,赋值运算完毕后返回赋值运算符左侧的对象,即返回b,然后继续参与a=b;的运算。

同一组运算符的意思是指几个运算符优先级相同,属于一个分组,C++中将不同优先级分成了若干组,比如"+"和"-"就属于同一组。

 从第一个和第二个例子可以看到,结合律确定表达式计算方向。第一个例子左结合,从左向右计算,第二个例子右结合,从右向左计算。

对于第三个例子,是两个优先级相同,但是是不同的运算符,根据结合律从左向右结合,进行计算。这里就会出现2个名词,一个是表达式的计算顺序,或者叫表达式的计算方向,另一个叫运算对象的求值顺序。在第三个例子当中,由于是左结合的,因此表达式的计算方向是从左往右。但是毕竟f( )和g( )是两个函数,参与运算的是函数的返回值,而不是函数,因此需要事先对这两个函数进行调用,调用完毕后,将返回值拿过来参与运算,假设f( )和g( )的返回值分别是f和g,即最终参与表达式运算的是f * g / g,这里确定了是返回值f先与返回值g相乘,然后再与返回值g做除法运算。但是有没有规定先调用f( )呢?没有!!!没有!!!没有!!!重要的事情说三遍,C++只确定了表达式的计算方向,并没有规定要先获取哪个参与运算的对象

到这里,我们已经知道了表达式的两个行为特征了,如下:

1. 复合表达式是会考虑优先级和结合律的。

2. 运算对象的求值顺序与优先级、结合律没有关系。

大多数运算符都没有规定表达式中运算对象的求值顺序,对于互不影响的函数之间,这并没有什么问题,但如果这几个函数共同影响同一个全局变量就会出现问题。

因此在C++ Primer第五版的123页中才会有这么一说:

“因为表达式的行为不可预知,因此不论编辑器生成什么样的代码程序都是错误的。”

是的,因为求值顺序没有规定,怎么样都有可能,这样的代码即使语法毫无问题,他也是错误的!

所以有两条经验准则用于书写复合表达式:

1. 拿不准的时候最好用括号来强制让表达式的组合关系符合程序逻辑的要求。

2. 如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象

OK,到目前为止,我似乎还没说多少关于条件运算符。接下来,我们用以上了解到的内容来看一看条件运算符。

条件运算符是右结合的

上面的规定是毫无疑问的,那么按照上面的知识来理解,对于 a ? b : c ? d : e按照右结合来解读先运算c,然后返回d或者e,返回后再参与到a ? b : d/e;是这样吗?

很显然不是,为什么?开头前面的例子都是左结合从左边开始计算,右结合从右边开始计算,为什么这个不是?

原因在于:前面的表达式中的运算符没有规定运算对象求值顺序,结合律只能在确定结合对象和计算方向后,按照结合性来计算表达式。但有四个是特例,这四个特殊的运算符规定了求值顺序和计算方向,它们分别如下:

1. 逻辑与&&,先求左侧对象,左侧为真,再求右侧,左侧为假,则不再求右侧

2. 逻辑或 || ,先求左侧对象,左侧为假,再求右侧,左侧为真,则不再求右侧

3. 条件运算符 条件 ? 表达式1 : 表达式2 ,先对条件判断,为真,对表达式1进行计算,为假,对表达式2进行计算

4. 逗号运算符“,”先求逗号运算符左侧的值,然后再对表达式右侧的求值。

其中,第一条和第二条的求值策略,我们给它一个术语,叫做:short-circuit-evaluation(短路求值)。

最后梳理一下,对于条件运算符,它是右结合的,对于 a ? b : c ? d : e这样的符合表达式,将最右边优先结合视为一个整体,相当于a ? b : (c ? d : e),但是并不是先对这个运算对象进行求值,如果没有规定求值顺序,可能先求b,也可能先求(c ? d : e),也可能先求a,然后再把a或b的最终结果或者(c ? d : e)的最终结果拿来从右向左开始参与表达式运算。也即运算对象求值不知道谁优先,但是表达式计算方向却是从右先左的。

但是条件运算符规定了求值的顺序和计算方向,必须先求条件a,然根据a的真假来求b或者(c ? d : e)。因此这里的右结合只起了怎么组合该复合表达式的作用,最终的求值顺序和表达式计算方向被该运算符的规定指明了。

关于C++条件运算符(三目运算符)右结合的说明的更多相关文章

  1. C语言的三目运算符(x=a?b:c):条件运算符

    三目运算符使用是为了有条件判断的选择赋值 x = a ? b : c 先计算 a表达式 是否为真.若为真,x 的值便是 b表达式的值,否则 x的值便是 c表达式的值. 条件运算符是右结合的. 如:a ...

  2. PHP运算符:算数运算符、逻辑运算符、三目运算符、位运算符、字符串运算符。

    赋值运算符 PHP 赋值运算符用于向变量写值. PHP 中基础的赋值运算符是 "=". 这意味着右侧复制表达式会为左侧运算数设置值. _______________________ ...

  3. C/C++三目运算符

    三目运算符,又称条件运算符,是计算机语言(C,C++,Java等)的重要组成部分.它是唯一有3个操作数的运算符,所以有时又称为三元运算符.一般来说,三目运算符的结合性是右结合的. 对于条件表达式b ? ...

  4. Java中的三目运算符可能出现的问题

    你真的了解Java中的三目运算符吗? 原创 2018-04-27 刨根问底的 Hollis Hollis Hollis 微信号 hollischuang 功能介绍 一个对Coding有着独特追求的人. ...

  5. 运算符:三目运算符,运算符优先级,sizeof,自增自减,取余

    一://---------运算符-----------// 1.运算符是告诉编译程序执行特定算术或逻辑操作的符号. 2.按照功能划分: 算术运算符. 关系运算符与逻辑运算符.按位运算符. 3.运算符根 ...

  6. javaScript 三目运算符初探

    三目运算符 三目运算符,又称条件运算符,是计算机语言的重要组成部分.它是唯一有3个操作数的运算符,所以有时又称为三元运算符.一般来说,三目运算符的结合性是右结合的. 定义 对于条件表达式b ? x : ...

  7. 《新版阿里巴巴Java开发手册》提到的三目运算符的空指针问题到底是个怎么回事?

    最近,阿里巴巴Java开发手册发布了最新版--泰山版,这个名字起的不错,一览众山小. 新版新增了30+规约,其中有一条规约引起了作者的关注,那就是手册中提到在三目运算符使用过程中,需要注意自动拆箱导致 ...

  8. (三目运算符)PHP中问号?和冒号: 的作用

    <表达式1>?<表达式2>:<表达式3>; "?"运算符的含义是: 先求表达式1的值, 如果为真, 则执行表达式2,并返回表达式2的结果 ; 如 ...

  9. Java的算数运算符、关系运算符、逻辑运算符、位运算符

    JAVA的运算符,分为四类: 算数运算符.关系运算符.逻辑运算符.位运算符 算数运算符(9):+  -  *  /  %  ++  -- 关系运算符(6):==  !=  >  >=  & ...

随机推荐

  1. grunt serve Fatal error: Port 35729 is already in use by another process.

    y@y:~$ lsof | grunt y 0t0 TCP *: (LISTEN) Optimizin y 0t0 TCP *: (LISTEN) v8:Sweepe y 0t0 TCP *: (LI ...

  2. cf C. New Year Ratings Change

    http://codeforces.com/contest/379/problem/C 思路:先排序,然后判断如果rating>ans,ans=rating否则ans++;然后对应的位置输出就可 ...

  3. keil中使用_at_绝对地址定位

    使用_at_关键字对存储器进行绝对地址定位程序如下: #include<reg51.h> ] _at_ 0x8000; main() { LED_Data[] = 0x23; } 在kei ...

  4. Android 4.4 上实现透明导航栏和状态栏 Translucent system bar

    Translucent system UI styling To get the most impact out of your content, you can now use new window ...

  5. Ultra-QuickSort(归并排序求逆序对数)

    Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 34283   Accepted: 12295 Description In ...

  6. COJ 0538 购物问题

    购物问题 难度级别:C: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 由于换季,ACM商场推出优惠活动,以超低价格出售若干种商品.但是,商场 ...

  7. COJ 0342 逆序对(一)

    传送门:http://oj.cnuschool.org.cn/oj/home/problem.htm?problemID=312 试题描述: 给你一个大小为N的int数组A.请你统计有多少数对(Ai, ...

  8. 【转】TCP三次握手过程

    写的非常明白:http://www.cnblogs.com/rootq/articles/1377355.html TCP协议三次握手过程分析 TCP(Transmission Control Pro ...

  9. cryptopp开源库的使用(一):md5加密

    项目总是各种新需求,最近遇到需要对字符串进行md5加密,确保传输字符串的有效性. 考虑到跨平台性和通用性,选择了cryptopp开源库,这里主要是用静态库调用. 1.引入头文件和lib库 #inclu ...

  10. shell变量自增的几种方式

    #!/bin/sh a= a=$(($a+)) a=$[$a+] a=`` let a++ let a+= ((a++)) echo $a 输出 : 转载自:http://blog.csdn.net/ ...