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. ISO7816协议的几个关键时间特性

    PPS: PPS是在PCK起始延后12个etu后完成,这个在2004版中是16etu 进行错误提示时,错误提示的延时时间是1etu到2etu 当D=64时,终端必须确保当前发出的第一个字符和最后一个接 ...

  2. Keil增量编译build异常

    装完MDK4.6/4.7a,发现没有修改源文件的情况下,单击build,每次都是重新编译,之前测试过重新建立工程/装回4.22,解决了该问题,当时并没多想原因.这次又出现了这个问题,重装了很久,也都这 ...

  3. Linux2.6内核--对块IO层操作的讨论

          当一个块被调入内存时(也就是说,在读入后或等待写出时),它要存储在缓冲区中.每个缓冲区与一个块对应,它相当于是磁盘块在内存中的表示.块包含一个或多个扇区,但大小不能超过一页,所以一页可以容 ...

  4. COJN 0483 800501求最大非空子矩阵

    800501求最大非空子矩阵 难度级别:B: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩 ...

  5. UVa 11292 - Dragon of Loowater(排序贪心)

    Once upon a time, in the Kingdom of Loowater, a minor nuisance turned into a major problem.The shore ...

  6. C++11 可变参数模板

    在C++11之前, 有两个典型的受制于模板功能不强而导致代码重复难看的问题, 那就 function object 和 tuple. 拿 function objects 来说, 需要一个返回类型参数 ...

  7. 数字信号处理Day2-小波基与规范正交化

    我们有这么一张灰度图64*64 我们能够定义出4096个基,各自是某一位是0其它是1,在这样的情况下,假设我们传输图片,那么就相当于传输原始数据 如果传到一半,网络坏了. 于是,我们得到 我们能够计算 ...

  8. spring mvc事务注解

    @Transactional(noRollbackFor=RuntimeException.class)方法事务说明@Transactional(RollbackFor=Exception.class ...

  9. Android使用GridView实现日历功能(详细代码)

    代码有点多,发个图先: 如果懒得往下看的,可以直接下载源码吧(0分的),最近一直有人要,由于时间太久了,懒得找出来整理,今天又看到有人要,正好没事就整理了一下 http://download.csdn ...

  10. VC++中操作XMLWin32实例

    摘要:VC++中操作XML XML在Win32程序方面应该没有在Web方面应用得多,很多Win32程序也只是用XML来存存配置信息而已,而且没有足够的好处的话还不如用ini.VC++里操作XML有两个 ...