morelinux@gx 人生五十年 如梦亦如幻 有生斯有死 壮士何所憾

C++ primer读书笔记(三)--- 表达式

2016-07-03
c++

运算符求值顺序
  • 运算符的优先级规定了运算对象的组合方式,但是没有说明运算对象按照什么顺序求值。对于没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为。

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

    因为程序是未定义的,所以我们无法推断它的行为。编译器可能先求++i的值再求i的值,此时输出结果为1 1;也可能先求i的值再求++i的值,输出结果为0 1;甚至编译器还可能做完全不同的操作。

  • 有4种运算符明确规定了运算对象的求值顺序。

    • 逻辑与&&运算符,它规定先求左侧运算对象的值,只有当左侧运算对象的值为真时才继续求右侧运算对象的值。
    • 逻辑或||运算符,当且仅当左侧运算对象为假时才对右侧运算对象求值。
    • 条件?:运算符
    • 逗号,运算符
  • 运算对象的求值顺序与优先级和结合律无关,在一条形如f() + g() * h() + j()的表达式中:

    • 优先级规定,g()的返回值和h()的返回值相乘。
    • 结合律规定,f()的返回值先与g()和h()的乘积相加,所得的结果再与j()的返回值相加。
    • 对于这些函数的调用顺序没有明确规定。

    如果f、g、h和j是无关函数,它们既不会改变同一个对象的状态也不执行IO任务,那么函数的调用顺序不受限制。反之,如果其中某几个函数影响同一个对象,则它是一条错误的表达式,将产生未定义的行为。

算数运算符(左结合律)
  • 整数相除结果还是整数,也就是说,如果商含小数部分,直接丢弃。

  • 当计算的结果超出该类型所能表示的范围时就会 溢出

    short short_value = 32767;   //如果short类型占16位,则能表示的最大值是32767
    short_value += 1;            //该计算导致溢出
    cout << "short value: " << short_value << endl; // -32768
    

    很多系统在编译和运行时都不报溢出错误,像其他未定义的行为一样,溢出的结果是不可预知的。通常情况下该值会发生 环绕(wrapped around)

  • 除法运算中,C++11新标准规定商一律向0取整(即直接切除小数部分)。

  • 如果m%n不等于0,则它的符号和m相同。 (-m)/n = m/(-n) = -(m/n) (-m)%n = -(m%n) m%(-n) = m%n

    21 % 6;    // 3
    21 / 6;    // 3
    21 % 7;    // 0
    21 / 7;    // 3
    -21 % -8;  // -5
    -21 / -8;  // 2
    21 % -5;   // 1
    21 / -5;   // -4
    
递增递减运算符
  • 递增和递减运算符有两种形式:前置版本和后置版本。

    除非必要,否则不用递增递减运算符的后置版本。前置版本的递增运算符避免了不必要的工作,它把值加1后直接返回改变了运算符对象。与之相比,后置版本需要将原始值存储下来以便于返回这个为修改的内容。如果我们不需要修改前的值,那么后置版本的操作就是一种浪费。

位运算符
  • 位运算符的运算对象可以是带符号的,也可以是无符号的。如果运算对象是带符号的且它的值为负,那么位运算符如何处理运算对象的“符号位”依赖与机器。而且,此时的左移操作可能会改变符号位的值,因此是一种未定义的行为。

    关于符号位如何处理没有明确的规定,所以强烈建议仅将位运算符用于处理无符号类型。

  • 二进制位或者向左移动或者向右移动,移出边界之外的位就被舍弃掉了。

    • 左移运算符<<在右侧插入值为0的二进制位
    • 右移运算符>>的行为则依赖与其左侧运算对象的类型

      • 如果该运算对象是无符号类型,则在左侧插入值为0的二进制位;
      • 如果该运算对象是带符号类型,则在左侧插入符号位的副本或值为0的二进制位,如何选择要视具体环境而定。
运算符优先级表
结合律 运算符 功能 用法
:: 全局作用域 ::name
:: 类作用域 class::name
:: 命名空间作用域 namespace::name
. 成员选择 object.member
-> 成员选择 pointer->member
[] 下标 expr[expr]
() 函数调用 func(expr_list)
() 类型构造 type(expr_list)
++ 后置递增运算 lvalue++
后置递减运算 lvalue–
typeid 类型ID typeid(type)
typeid 运行时类型ID typeid(expr)
explicit cast 类型转换 cast_name(expr)
++ 前置递增运算 ++lvalue
前置递减运算 –lvalue
~ 位求反 ~expr
! 逻辑非 !expr
- 一元负号 -expr
+ 一元正号 +expr
* 解引用 *expr
& 取地址 &expr
() 类型转换 (type)expr
sizeof 对象的大小 sizeof expr
sizeof 类型的大小 sizeof(type)
sizeof 参数包的大小 sizeof…(name)
new 创建对象 new type
new[] 创建数组 new type[size]
delete 删除对象 delete expr
delete[] 删除数组 delete[] expr
noexcept 能否抛出异常 noexcept(expr)
->* 指向成员选择的指针 ptr->*ptr_to_member
.* 指向成员选择的指针 obj.*ptr_to_member
* 乘法 expr * expr
/ 除法 expr / expr
% 取模(取余数) expr % expr
+ 加法 expr + expr
- 减法 expr - expr
« 向左移位 expr « expr
» 向右移位 expr » expr
< 小于 expr < expr
<= 小于等于 expr <= expr
> 大于 expr > expr
>= 大于等于 expr >= expr
== 相等 expr == expr
!= 不相等 expr != expr
& 位与 expr & expr
^ 位异或 expr ^ expr
| 位或 expr | expr
&& 逻辑与 expr && expr
|| 逻辑或 expr || expr
?: 条件 expr ? expr : expr
=, *=, /=, %=, +=, -=, «=, »=, &=, |=, ^= 赋值与复合赋值 lvalue = expr等
throw 抛出异常 throw expr
, 逗号 expr, expr

Comments