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. CLOUDSTACK接管VCENTER,意外频出,但最终搞定

    没办法,第一次吃螃蟹,总是经常住. 还好,我有一颗不肯媚俗的心~~ 但二级存储,和 存储的性能,也必须纳入考虑范围了.

  2. GitHub上最火的开源项目SlidingMenu导入出错的终极解决方案

    SlidingMenu 开源项目下载地址 : https://github.com/jfeinstein10/slidingmenu Actionbarsherlock 开源项目下载地址 :http: ...

  3. Mysql 主从复制,读写分离设置

    一个简单完整的 Mysql 主从复制,读写分离的示意图. 1. 首先搭建 Mysql 主从架构,实现 将 mater 数据自动复制到 slave MySQL 复制的工作方式很简单,一台服务器作为主机, ...

  4. 全表扫描引发的db file sequential read

    今天我要做一个SQL调优,监控该SQL, 利用ASH 监控 该SQL是在sid=4848 上面跑的 db file sequential read等待事件有3个参数:file#,first block ...

  5. -_-#【Angular】自定义过滤器

    AngularJS学习笔记 <!DOCTYPE html> <html ng-app="Demo"> <head> <meta chars ...

  6. HBase Endpoint

    引言   假设HBase某张表有1000个Region,里面存储着100万行数据,现在需要统计满足某些条件的行数,普通的做法是使用Filter(过滤条件),通过HBase API将满足过滤条件的行数据 ...

  7. HBase Compact

    Region Compact请求是在Region MemStore Flush之后被触发的: boolean shouldCompact = region.flushcache(); // We ju ...

  8. 聚类算法:K均值、凝聚层次聚类和DBSCAN

    聚类分析就仅根据在数据中发现的描述对象及其关系的信息,将数据对象分组(簇).其目标是,组内的对象相互之间是相似的,而不同组中的对象是不同的.组内相似性越大,组间差别越大,聚类就越好. 先介绍下聚类的不 ...

  9. C#获取字符串生成图片后的长度

    1.    使用g.MeasureString()获得 使用MeasureString测量出来的字符宽度,总是比实际宽度大一些,而且随着字符的长度增大,貌似实际宽度和测量宽度的差距也越来越大了.查了一 ...

  10. tungsten

    ./tools/tungsten-installer --master-slave -a --datasource-type=mysql --master-host=master.puppet.org ...