C++_Chapter 4 表达式基础与详述

Chapter 4 表达式基础与详述

4-1 表达式基础:引入

int main()
{
	int x;
	x = 3 // 表达式,可求值
    3 + 2.3 // 传入两个不同类型-->隐式产生类型转换
        
}

表达式由一到多个操作数组成,可以求值,并通常会返回求值结果

  • 最基本的表达式:变量、字面值

  • 表达式可嵌套

  • 通常来说,表达式会包含操作符(运算符)

  • 操作符的几个特性

    • 接收几个操作数:一元、二元、三院
    • 操作数的类型——类型转换
    • 操作数是左值还是右值
      • 有些情况下只能接收左值; // 3 = 5
    • 结果的类型
    • 结果是左值还是右值
    • 优先级和结合性(2 + 3 * 5),可以通过小括号来改变运算顺序
    • 操作符的重载——不改变接收操作数的个数、优先级和结合性
  • 操作数求值顺序的不确定性

    // 函数重载 ---> 读写依赖关系避免乱序
    void fun(int p1, int p2)
    {
    	std::cout << p1 << '\n' << p2 << '\n';
    }
    
    int main()
    {
        int x = 0;
        fun(x = x + 1, x = x + 1);
    }
    

4-2 表达式基础:左值与右值

// C语言
int main()
{
	int x;
	x = 3; // x可以放在等号左边,称为左值;3不能放在等号左边,称为右值
}
// 在C++当中,左值也不一定能放在等号左边,而右值也可能放在等号左边
// 所有的划分都是针对表达式的,不是针对对象或数值

glvalue (generalized left-value):泛左值是一个表达式—>获取到x所关联的内存,确定一个对象、位域或者函数

prvalue (Pure right-value):用于初始化某对象,不开辟内存

struct Str
{

}
int main()
{
	int{}
	Str{}
}

xvalue:代表其资源能够被重新使用

# include <iostream>
# include <vector>

void fun(std::vector<int>&& par)
{
	
}
int main()
{
	std::vector<int> x;
	fun(std::move(x)); // 把x变为将亡值
}
// 左值不一定能放在等号左边
const int x = 3;
x = 4;// 会报错
// 右值可能可以放在等号左边
struct Str {};

int main()
{
	Str() = Str(); // 编译可通过
}
  • 左值可以转换为右值(lvalue to rvalue conversion);
  • 临时具体化(Temporary Materialization)
// 右值可能可以放在等号左边
struct Str {
	int x;
};

int main()
{
	Str().x // 从Str中取出特定部分,从纯右值转换为将亡值
}
  • 再谈decltype

    int x;
    decltype(x);
    // 将亡值--->type&&
    // 左值--->type&
    // 纯右值--->type
    

4-3 表达式基础:类型转换

  • 隐式类型转换

    例:3 + 0.5

    • 自动发生
    • 实际上是一个有限长度的转型序列
      • (cpp-reference)数值提升:整型提升/浮点提升
      • 数值转换:如int—>double,可能会更改值,有 潜在的精度损失
  • 显式类型转换

    例:static_cast(3) + 0.5,程序以可见形式进行转换

    int x = 3;
    int y = 4;
    std::cout << (x / static_cast<double>(y)) << std::endl;
    
    • static_cast<新类型> (表达式) // 静态期转换,不影响运行期

    • dynamic_cast<新类型>(表达式)

    • const_cast<新类型> (表达式)

      const int* ptr;
      const_cast<int*>(ptr);// 去除常量
      
      // 需要小心
      int x = 3;
      
      const int& ref = x;
      int& ref2 = const_cast<int&>(ref);
      ref2 = 4;
      
      std::cout << x << std::endl;// 输出4
      
      // 但如果定义const int x = 3,上述代码可能输出3而不是4,与编译器有关,转换指针时,确保是关联到一个变量而不是常量
      
    • reinterpret_cast

      int main()
      {
      	int x = 3;
      	double y = reinterpret_cast<double>(x); // 对内存重新解释,但针对指针,该行代码报错
          int* ptr = &x;
          float* ptr2 = reinterpret_cast<float*>(ptr);// 该行代码正常
      }
      
    • C类型转换(在程序中避免大量使用)

      (double)3; // 不建议
      

4-4 表达式详述:算数操作符

+,- // 优先级1 一元
*,/,% // 优先级2
+,- // 优先级3
  • 均为左结合的
  • 通常来说,操作数与结果均为算数类型的右值;但加减法与一元+可接收指针
int main()
{
	int a[3] = {1,2,3};
    int* ptr = a;
    ptr = ptr + 1;
    ptr = ptr - 1;
    
    std::cend(a) - std::cbegin(a);
}
  • 一元+操作符会产生integral promotion
  • 整数相除会产生整数,向0取整;
  • 求余只能接受整数类型操作数,结果符号与第一个操作数相同
  • (m / n) * n + m % n = m

4-5 表达式详述:逻辑与关系操作符

逻辑操作符:!, ||, &&

关系操作符:<=>, <, <=, >, >=, ==, !=

都可以转换为布尔值的操作数

true && true;
3 && true; // 字面值可以转换为bool
int x = 3;
3 && x; // 只要可以转换为布尔值,逻辑操作符就是合法的
// 指针只要指向不是nullptr,转换为bool就是true
  • 操作数与结果均为右值

  • 除逻辑非之外,其他操作符都是左结合

  • 逻辑与和逻辑或具有短路特性

    a && b;
    // 执行过程:先判断a转换为bool是否为真,若为假,直接返回0
    int* ptr = nullptr;
    if (ptr && (*ptr == 3))
    {
        
    }
    
  • 逻辑与的优先级高于逻辑或

    a || (b && c) // 计算加括号会好很多,不会乱
    
  • 关系操作符不要串联

    std::cout << (c > b > a) << std::endl;
    // 计算会先计算出c > b--->true 1 > a---> false
    // 正确的实现应该是
    std::cout << ((c > b) && (b > a)) << std::endl;
    
  • 不要写出 val == true的代码

    int main()
    {
    	int a = 3;
    	if (a) // 不要写( a == true --->会转换为a == 1)
    	{
    		
    	}
    }
    
  • Spaceship operator:<=>(C++20支持)

    if (a > b)
    {
    
    }
    else if (a < b)
    {
    
    }
    else
    {
    
    }
    

    a <= > b—>会返回a与b的关系

    上式可转换为

    auto res = (a <=> b);
    if (res > 0)
    {
    
    }
    else if (res < 0)
    {
    
    }
    else
    {
    
    }
    

4-6 表达式详述:位操作符

~、&、|、^

  • 接收右值,进行位运算,返回右值

    signed char x = 3; // 00000011
    std::cout << ~x << std::endl; // 补码
    // 00000011------>11111100补码形式进行分析,最高位为1,代表为负数,最终结果为-4
    signed char y = 5; // 00000101
    std::cout << (x & y) << std::endl; // 两个只要有一个位对应为0,就是0,否则为1
    std::cout << (x | y) << std::endl; // 两个有一个位对应为1则为1,否则为0
    std::cout << (x ^ y) << std::endl; // 取值不同,返回为1,取值相同,返回为0
    
  • 除取反外,其他运算符均为左结合的

  • 注意计算过程中可能会涉及到integral promotion

  • 位操作符没有短路逻辑

  • 移位操作在一定程度上等价于乘(除)2的幂

    int x = 3;
    constexpr int y = 2;
    std::cout << (x * y) << std::endl;
    //std::cout << (x << 1) << std::endl;
    
  • 注意整数的符号与位操作符的相关影响

    • integral promotion 会根据整数的符号影响其结果
    unsigned char x = 0xff; //11111111
    // 0000..00011111111(24个0+8个1)
    // 按位取反:1111..11100000000--->-256
    auto y = ~x;
    std::cout << y << std::endl;
    signed char x = 0xff; // 上面y会输出为0 1(符号位)1111111
    // 1111...1111111111--->0000...0000000000
    
    • 右移保持符号,但左移不能保证
    int x = 0x80000000; // 10...0
    std::cout << x << std::endl;
    std::cout << (x << 1) << std::endl; // 数据溢出,输出为0
    std::cout << (x >> 1) << std::endl; // x/2
    

4-7 表达式详述:赋值操作符

  • 左操作数为可修改左值,右操作数为右值,可转换为左操作数的类型

  • 赋值操作是右结合的,求值结果为左操作数

    int x;
    int y;
    x = y = 3; // 合法,且先计算y = 3
    
  • 可以引用大括号(初始化列表)防止收缩转换

    short x;
    x = {0x80000000};
    
  • 小心区分= 与==

  • 复合赋值运算符

4-8 表达式详述:自增自减运算符

x += 1 == ++x;

  • a++/a-- // 后缀自增/自减运算符 --> y = x++/x–返回x的原始值

  • ++a/–a // 前缀自增/自减运算符

  • 分前缀与后缀两种情况

  • 操作数为左值;前缀时返回左值;后缀时返回右值

  • 建议使用前缀形式

4-9 表达式详述:其他操作符

struct Str
{
	int x;
}

int main()
{
	a = Str;
	a.x;
}
成员访问操作符:.与->
  • -> 等价于 (*).

  • . 的左操作数是左值(或右值),返回左值(或右值xvalue )

  • -> 的左操作数指针,返回左值

条件操作符
  • 唯一的三元操作符

    true ? 3 : 5
    
  • 接收一个可转换为bool 的表达式与两个类型相同的表达式,只有一个表达式会被求值

  • 如果表达式均是左值,那么就返回左值,否则返回右值

  • 右结合

逗号操作符
  • 保证操作数会被从左到右求值

  • 求值结果为右操作数

  • 左结合

sizeof操作符
  • 产生表达式的类型对象所表示的字符数

    int x;
    sizeof x; // 不建议使用
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值