C++中的表达式

文章目录

C++中的表达式由一个或多个操作数通过操作符组合而成。最简单的表达式仅包含一个字面值常量或变量。每个表达式都会产生一个结果。如果表达式中没有操作符,则其计算结果就是操作数本身。除了特殊的用法外,表达式的结果是 右值,可以读取该结果值,但是不允许对它进行赋值。

算数操作符

一元操作符优先级最高,其次时乘除,最后是加减。其中求模操作中值得注意得是:

cpp 复制代码
-21 % -8; //-5,当两个数都是负数,求模(取余)操作的为负数或0
21 % -5; //1或-4,当两个操作数只有一个负数时,结果值得符号可依据分母或分子得符号而定,这两种操作得结果取决于机器
cpp 复制代码
expr1 && expr2;
expr3 || expr4;

逻辑与和逻辑或总是先计算左操作数,只有在左操作数无法确定逻辑表达式得结果时,才会计算右操作数。这种求值策略被称为"短路求值"。

位操作符

位操作符操作的整数类型可以是有符号的也可以是无符号的。如果操作数为负数,则位操作符如何处理操作数的符号位依赖于机器。由于系统不能确保如何处理操作数的符号位,所以建议使用unsigned整型操作数。

bitset对象或整型值的使用

cpp 复制代码
bitset<30> bitset_quizl;
unsigned long int_quizl = 0;

//操作第27位
bitset_quizl.set(27); // set置为1,reset置为0
int_quizl |= 1UL << 27; // 置1
int_quizl &= ~(1UL << 27); // 置0

一般而言,标准库提供的bitset操作更加直接、更容易阅读和书写、正确的使用的可能性更高。而且bitset对象的大小不受unsigned的位数限制。

将位移操作符用作IO

输入输出标准库分别重载了位操作符>>和<<用于输入和输出。重载的操作符和内置版本有相同的优先级和结合性。优先级比算数操作符低,但比关系操作符、赋值操作符和条件操作符高。

cpp 复制代码
cout << 42 + 10;
cout << (10 < 42);
cout << 10 < 42; // ❌

赋值操作符

赋值操作符的右结合性

cpp 复制代码
int ival, jval; 
ival = jval = 0;

值得注意的是,操作对象必须具有相同的类型,或者具有可转换位同一类型的数据类型。

赋值操作具有低优先级

cpp 复制代码
int i = get_value();
while(i != 42)
{
	// do something();
	i = get_value();
}
// 更简洁的写法
while((i = get_value()) != 42)
{
	// do something;
}

第二种写法更加简洁和清晰。值得注意的是在赋值操作中的圆括号是必须的,因为赋值操作符的优先级低于不等操作符。

自增和自减操作符

  • 后置操作符返回未加1的值
cpp 复制代码
int i = 0, j;
j = ++i; // i = 1, j = 2;
j = i++; // i = 2, j = 1;

前置操作返回自增之后的结果,后置操作返回自增之前的结果。只有在必要时才使用后置操作。因为其在实现的过程中回产生一个临时对象,从而增加时间开销。

  • 在单个表达式中组合使用解引用和自增操作
cpp 复制代码
vector<int>::iterator iter = ivec.begin();
while(iter != ivec.end())
{
	cout << *iter++ << endl;
}

自增操作的优先级高于解引用,因此先自增,在解引用。后置自增返回自增之前的值,因此解引用可以从0开始。

条件操作符

  • 嵌套条件操作符
cpp 复制代码
int i = 10, j = 20, k = 40;
int max = i > j? i > k? i : k
		: j > k? j : k;
  • 在输出表达式中使用条件操作符
cpp 复制代码
cout << (i < j ? i : j);
cout << i < j ? i : j; // ❌

条件表达式的优先级很低,想要不出错,最好用圆括号把表达式括起来。

sizeof操作符

sizeof表达式的结果是编译时常量,通常有以下三种语法形式:

  • sizeof(type name);
  • sizeof(expr);
  • sizeof expr;
cpp 复制代码
// 以下三种得到都是Sales_item的大小
Sales_item item, *p;
sizeof(Sales_item);
sizeof item;
sizeof *p; 

优先级


上表按照优先级顺序列出了C++的全部操作符。该表不同优先级的操作符划分到不同段中,每段内各个操作符优先级相同,且都高于后面各段中的操作符。值得注意的后自增操作优先级比前自增操作的优先级更高。

new和delete表达式

  • 初始化
cpp 复制代码
\\ 动态创建类类型,回自动调用默认构造函数
string *s = new string;
\\ 内置类型,不会自动初始化
int *pi = new int;
\\ 可通过在类型名后加(),进行值初始化
int *pi = new int();
  • new分配内存失败
    分配失败系统将抛出名为bad_alloc的异常。
  • 在delete之后,重设指针的值
cpp 复制代码
delete p;
p = nullptr;

在delete动态分配的之后,p变成没有意义。在很多机器之上,尽管p没有意义,但仍存放了它之前所指向地址的值,然后p所指向的内存已经被释放,因此p不在有效。

删除指针之后,该指针变成垂悬指针。为了防止出错,在删除指针之后,立即将其置为0。

类型转换

由编译器自动进行类型转换称之为隐式类型转换。

何时发生隐式转换

在下列情况下,将发生隐式类型转换:

  • 混合类型的表达式中,其操作数转化为相同的类型
cpp 复制代码
int ival;
double dval;
ival >= dval; // ival转化为double,一般向精度更大的类型进行转换
  • 用作条件表达式被转换为bool类型
cpp 复制代码
int ival;
if(ival)
while()
&&, ||, !, ?:等等
  • 用一表达式初始化或赋值某个变量,该表达式结果转化为该变量类型。

显示转换

显示转换也称为强制类型转换(cast),包括static_cast、dynamic_cast、const_cast和reinterpret_cast。

  • 何时需要强制类型转换
cpp 复制代码
double dval;
int ival;
ival *= dval; // ival * dval,ival先转double,ival = ival * dval,赋值操作结果转int
ival *= static_cast<int>(dval); // 有效减少隐式转换的次数
  • 使用场景
cpp 复制代码
// dynamic_cast,支持运行时识别指针或引用所指向的对象
// const_cast,添加移除const属性,const_cast<new_type>(expression)
// static_cast,编译器隐式转换的任何类型都可以由static_cast显示完成
// reinterpret_cast,通常为操作数的位模式提供低层次的重新编译。本质上依赖于机器。
//为了安全的使用,要求程序员完全理解所设计的类型,以及编译器实现强制类型转换的细节。
// 转换后的类型通常与原类型完全不相关,用于底层操作,转换为完全不相关的类型,如指针与整数之间的转换。
//这种转换是危险的,可能会导致未定义行为,特别是在内存布局不一致的情况下
int *ip;
char *pc = reinterpret_cast<char*>(ip); // 程序员必须永远记着pc所指向的真实对象是int型,而非字符数组。任何假设pc是字符指针的应用,可能带来未知的错误。

旧式强制类型转换

在引入命名转换操作符之前,显示强制类型转换用圆括号实现。

cpp 复制代码
// 以下两种写法都可
char *pc = (char*) ip;
char *pc = char*(ip);

旧式强制类型转换具有和static_cast、const_cast以及reinterpret_cast一样的行为。在合法使用前两者的场景中,提供了与各自对应命名强制转换一样的功能。如果这两种转换均不合法,则执行reinterpret_cast的功能。

支持旧式强制类型转换是为了对保持老程序的兼容性,一般建议使用命名强制转换。

相关推荐
就叫飞六吧25 分钟前
C语言中,`extern` 和 `#include`
c语言·c++·算法
涛ing1 小时前
12. C语言 数组与指针(深入理解)
linux·c语言·开发语言·数据结构·c++·算法·ubuntu
我命由我123452 小时前
Android Process 问题:NoSuchMethodError,No static method myProcessName()
android·java·java-ee·android studio·安卓·android-studio·安卓开发
黑果果的思考3 小时前
C++例程:使用I/O模拟IIC接口(6)
开发语言·c++
TroubleMaker3 小时前
OkHttp源码学习之Authenticator
android·java·okhttp
CharlesBlog4 小时前
Android 智能指针(2):RefBase 核心篇
c++
susu10830189114 小时前
android studio实现圆形图片
android·ide·android studio
十年一梦实验室4 小时前
C++ 如何将 gRPC集成到机器人系统中
开发语言·c++·机器人
不是AI4 小时前
【C语言】【C++】报错:[错误]‘vector‘ does not name a type
c语言·开发语言·c++