c++系列文章(12):表达式

本文详细介绍了C++中的表达式概念,包括运算符的优先级和结合律,算术、逻辑和关系运算符,赋值运算符,递增递减运算符,成员访问运算符,条件运算符,位运算符,以及类型转换规则。重点讨论了运算对象的求值顺序和隐式类型转换,强调了类型安全和潜在的未定义行为。
摘要由CSDN通过智能技术生成

  表达式由一个或多个运算对象组成,对表达式求值将得到一个结果字面值和变量是最简单的表达式,其结果就是字面值和变量的值。把一个运算符和一个或多个运算对象组合起来可以生成较复杂的表达式。

表达式的基本概念

  C++定义了一元运算符和二元运算符,此外还有一个作用于三个运算对象的三元运算符。函数调用也是一种特殊的运算符,它对运算对象的数量没有限制。
  在表达式求值的过程中,运算对象常常需要由一种类型转换成另一种类型。例如整数能被转换成浮点数、浮点数也能转换成整数、但指针不能转换成浮点数,此外小整数类型(如bool、char、short等)通常会被提升成较大的整数类型,主要是int。
  C++语言定义了运算符作用于内置类型和复合类型的运算对象时所执行的操作当运算符作用于类类型的运算对象时,用户可以自定义其含义,称之为重载操作符。IO库的>>和<<运算符以及string对象、vector对象和迭代器使用的运算符都是重载运算符。当我们使用重载运算符时,其包括的运算对象的类型和返回值的类型,都是由该运算符定义的,但是运算对象的个数、运算符的优先级和结合律都是无法改变的
  C++的表达式要不然是右值,要不然就是左值。这两个名词是从C语言中继承过来,原本的意思是:左值可以位于赋值语句的左侧,右值则不能。但在C++语言中,二者的区别就没那么简单:一个左值表达式的求值结果是一个对象或者一个函数,然而以常量对象为代表的某些左值实际上不能作为赋值语句的左侧对象;此外,某些表达式的求值结果是对象,但他们是右值而非左值。做一个简单的归纳:当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)
  不同运算符对运算对象的要求各不相同,有的需要左值运对象,有的需要右值运算对象;返回值也有差异,有的得到左值结果,有的得到右值结果。一个重要的原则是:在需要右值的地方可以用左值来代替,但不能把右值当成左值使用。当一个左值被当成右值使用时,实际使用的是它的内容(值)
以下是几种我们熟悉的运算符要用到左值:
  赋值运算符需要一个(非常量)左值作为其左侧运算对象,得到的结果也是一个左值;
  取地址符作用于一个左值运算对象,返回一个指向该运算对象的指针,这个指针是一个右值;
  内置解引用运算符、下标运算符、迭代器解引用运算符、string和vector的下标运算符的求值结果都是左值;
  内置类型和迭代器的递增递减运算符作用于左值运算对象,其前置版本所得的结果也是左值;
  使用关键字decltype的时候,左值和右值也有所不同。**如果表达式的求值结果是左值,decltype作用于该表达式(不是变量)得到一个引用类型。*例如,假定p的类型是int,因为解引用运算符生成左值,所以decltype(*p)的结果是int&。另一方面,因为取地址运算符生成右值,所以decltype(&p)的结果是是一个指向整型指针的指针。

优先级和结合律

  复合表达式是指含有两个或多个运算符的表达式。求复合表达式的值需要首先将运算符合运算对象合理地组合在一起,优先级与结合律决定了运算对象组合的方式。**优先级规定了运算对象的组合方式,但是没有说明运算对象按照什么顺序求值,在大多数情况下,不会明确指定求值的顺序。**对于如下的表达式:

int i = f1() * f2();

  我们知道f1和f2一定会在执行乘法之前被调用,因为毕竟相乘的是这两个函数的返回值,但是我们无法知道到底f1在f2之前调用还是f2在f1之前调用。**对于那些没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为。**例如<<运算符没有明确规定何时以及如何对运算对象求值,因此下面的输出表达式是未定义的:

int i = 0;
cout << i << " " << ++i << endl; //未定义

  编译器可能先求++i的值再求i的值,此时输出的结果是1 1;也有可能先求i的值再求++i的值,输出的结果是0 1;甚至编译器还可能左完全不同的操作。因为此表达式的行为不可预知的。
  在处理符合表达式时,有两条经验准则十分重要:1、拿不准的时候最好用括号;2、如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象。

算术运算符

算术运算符
  上述的算术运算符都满足左结合律,既当优先级相同时按照从左到右的顺序进行组合。**除非另做特殊说明,算术运算符都能作用于任意算术类型以及任意能转换为算术类型的类型。**算术运算符的运算对象和求值结果都是右值。在表达式求值之前,小整数类型的运算对象被提升成较大的整数类型,所有运算对象最终会转换成同一类型。对大多数运算符来说,布尔类型的运算对象将被提升为int类型。
  算术表达式可能产生未定义的结果:一部分原因是数学性质本身,例如除数是0的情况;另外一部分则源于计算机的特点,例如溢出,当计算的结果超出该类型所能表示的范围时就会产生溢出。
  假设某个机器的short类型占16位,则最大的short数值是32767,则下面的复合赋值语句将会产生溢出:

short short_value = 32767; //short类型占16位,能表示的最大值时32767
short_value += 1; //该计算导致溢出
cout << short_value << endl;

  给short_value赋值的语句是未定义的,这是因为表示一个带符号32768需要17位,但是short类型只有16位。很多系统在编译和运行时都不报溢出错误,而是产生未定义的结果。例如在我的系统中,输出的结果是-32768,该值发生了“环绕”,符号位本来是0,由于溢出被改写成了1,于是结果变成了一个负值。在别的系统中也许会有其他结果,程序的行为可能不同甚至直接崩溃。
  对于取余运算符%,在C++11新标准中,m%(-n)等于m%n,(-m)%n等于-(m%n),例如-21%-8的结果是-5,21%5的结果是1,既先全部按照正数取余,如果m为正,则余数为正,如果m为负,则余数为负

逻辑和关系运算符

  关系运算符作用于算术类型或指针类型,逻辑运算符作用于任意能转换成布尔值的类型。逻辑运算符和关系运算符的返回值都是布尔类型。值为0的运算

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值