第四章 表达式
-
如果m% != 0, 那么其结果的符号和m相同
-
关于使用 ++ 和 – 时,建议将其作为前置运算符(++i),因为作为后置运算符时(i++)需要额外的空间存储修改前的值,所以只有当我们需要修改前的值作为输出或赋值时才使用后置。
条件运算符
cond? expr1 : expr2
#include<string>
int grade;
string final_grade = (grade < 60) ? "fail" : "pass";
// 嵌套条件运算符
string final_grade = (grade < 60) ?
"fail" : (grade < 90) ? "pass" : "good";
// 条件运算符的嵌套会导致可读性降低,最好不要超过两层
// 条件运算符的优先级非常低,所以和其他除 = 一起使用时最好在两端添加括号
cout<<((grade < 60) ? "fail" : "pass"); // 输出fail/pass
cout<<(grade < 60) ? "fail" : "pass"; // 输出 0/1
// 上面的表达式等价于
cout<<(grade < 60);
cout ? "fail" : "pass";
位运算符
- 由于位移时对于符号位没有明确的规定,所以除了无符号整数外,需要特别注意其他类型的符号位的改变。
运算符 | 作用 |
---|---|
- | 位求反 |
<<, >> | 左移, 右移 |
& | 未与 |
^ | 位异或 |
| | 位或 |
- ^=; &=; |= 和 += 的使用规则相同
void display_binary(int n){ //输出二进制
int a = n % 2;
n = n >> 1;
if(n) display_binary(n);
cout<<a; }
unsigned char bit1 = 0233;
unsigned char bit2 = 0146;
cout<<sizeof(bit1)<<endl; //1
display_binary(bit1); //10011011
display_binary(bit2); //01100110
cout<<endl<<sizeof(bit1<<8)<<endl; //4
display_binary(bit1<<8); //1001101100000000
cout<<endl<<sizeof(bit1>>2)<<endl; //4
display_binary(bit1>>2); //00100110
display_binary(bit1^bit2); //11111101 异或
display_binary(bit1|bit2); //11111111 或
display_binary(bit1&bit2); //00000010 且
sizeof运算符
sizeof(type)
sizeof expr
- sizeof 返回一个常量,可用于定义数组长度。
- sizeof 对于指针返回其长度,对于解引用才返回指向对象的长度。
- sizeof 处理数组时会返回整个数组的长度,而不会把数组看作指针进行处理。
- sizeof 处理string和vector对象的时候,只返回该类型固定部分的大小, 不会计算对象中元素占用了多少空间, string对象固定返回8,和sizeof(string), 如果要返回string对象的长度,需要使用string.size()或string.length()函数。vector对象返回24。
int i, *p, arr[10];
cout<< sizeof(int) <<endl; //4
cout<< sizeof i <<endl; //4
cout<< sizeof *p <<endl; //4
cout<< sizeof p <<endl; //8,指针长度,主要看编译环境而改变
cout<< sizeof arr <<endl; //40
类型转换
-
隐式转换: 算术类型中,隐式转换被设计得尽量去避免精度的损失,如int型和float型相加时,结果会转化成float型。
- 不同形式的算术类型进行算数运算。
- 数组转换成指针。当数组被用作decltype, 取地址符&, sizeof 和 typeid 等运算符时不会发生转换。
- 指针的转换:
- 0和nullptr可以转换成任意类型的指针。
- 指向非常量的指针能转换成void*。
- 指向任意对象的指针能转换成const void*。
- 转换成布尔类型,0转化为false,非0转化为True。
-
算术转换:隐式转换的一种,是把一个算数类型转换成另一种算术类型,算数转换的规则定义了一套类型转换的层次,其中运算符的运算对象将转换为最宽的类型。
-
整形提升,算术转换的一种,负责把小整数类型转化为较大的整数类型。 在算术表达式中,如果一个运算类型是无符号,另一个是有符号类型,且无符号类型不小于有符号类型,那么转化为无符号类型,如果此时有符号类型是负数,则结果可能会发生错误, 反之,有符号类型大于无符号类型,则转换为有符号类型,如unsigned int 和long long 。
int x = -255;
unsigned y = 75;
auto z = x + y; // 4294967116
int z = x + y; // -180
小整数类型 | 大整数类型 | 备注 |
---|---|---|
bool char signed char unsigned char short unsigned short | int unsigned int | 只要前者所有有可能的值都存在int中,就会转化成int型,否则转化为unsigned int型。 |
wchar_t char16_t char32_t | int unsigned int long unsigned long long long unsigned long long | 前者会转化成后这种最小的一种类型,前提是转换后的类型能够容纳前者所有的值。 |
-
显式转换:(强制类型转换)
- cast-name(expression): type是转换的目标类型,cast-name指定了执行的是某种转换,是static_cast, dynamic_cast(支持运行时类型识别), const_cast, reinterpret_cast中的一种。
- (type) expression: 旧强制类型转换。
类型 | 备注 |
---|---|
static_cast | 任何具有明确定义的类型转换,只要不包含底层的const(大概是常量指针?)就可以使用。 |
dynamic_cast | 支持运行时类型识别,用于将基类的指针或引用安全的转化成派生类的指针或引用(P730)。 |
const_cast | 只能改变运算对象的底层const,将底层const转换成非常量类型,或者反过来。只有const_cast能够改变表达式的常量属性。 |
reinterpret_cast | 通常为运算对象的位模式提供较低层次上的重新解释,如将指针类型改变,感觉没啥用,而且如果将int*转化为char*,进行字符串处理等操作,编译器无法识别该指针指向的其实是int型。 |
int i = 24, j = 5;
double slope = i/j; //4, 损失运算精度
double slope = (double)i/j; //4.8
// static_cast
double slope = static_cast<double>(i)/j; //4.8, i要加上括号!
void *p = &slope;
double *dp = static_cast<double*> p; //将指向double对象的void*转化为double*
// const_cast
const char c = 'a';
const char *cp = &c;
char *p = const_cast<char*>(cp); //将常量对象指针转化为非常量对象的指针,去掉了const性质,如果cp指向的对象是一个可修改的对象,则可以对其进行修改,但是若cp指向的是一个常量,则可能发生未定义的错误。
static_cast<string>(cp); //正确,字符串字面值转化成string类型
const_cast<string>(cp); //错误,const_cast只改变常量属性。
//reinterpret_cast
int * ip;
char *cp = reinterpret_cast<char*> (ip); //没有实质意义
char *cp = (char *)ip; //等同意义
string str(cp); //把实质为int当作字符串字面值传递给字符串str。
运算符优先表(从高到低排序)
运算符 | 功能 | 用法 |
---|---|---|
:: | 全局作用域 | ::name |
:: | 类作用域 | class::name |
:: | 命名空间作用域 | namespace::name |
. -> | 成员选择 | object.member object->member |
[] | 下标 | expr[idx] |
() | 函数调用 | function_name(args) |
() | 类型构造 | type(args) |
++ – | 后置递增、递减操作 | value++ value– |
typeid | (运行时)类型id | typeid(type)、typeid(expr) |
explicit cast | 类型转换 | cast_name (expr) |
++ – | 前置递增、递减运算 | ++value –value |
~ | 位求反 | ~expr |
! | 逻辑非 | !expr |
- + | 一元正负号 | -expr +expr |
* | 解引用 | *p |
& | 取地址 | &expr |
() | 类型转换 | (type)expr |
sizeof | 对象大小 | sizeof expr |
sizeof | 类型大小 | sizeof (expr) |
sizeof… | 参数包大小 | sizeof…(name) |
new | 创建对象 | new type |
new[] | 创建数组 | new type(size) |
delete | 释放对象 | delete expr |
delete[] | 释放数组 | delete[] expr |
noexpect | 能否抛出异常 | noexpect(expr) |
.* ->* | 指向成员选择的指针 | expr.*ptr_to_member ptr->*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 |
? : | 条件语句 | cond? a:b |
= | 赋值 | a = n |
*=, /=, %= +=, -= <<=,>>= &=, |=, ^= | 复合运算 | expr += 1 |
throw | 抛出异常 | throw expr |
, | 逗号 | expr, expr |