第4章 表达式

什么是表达式?我可以简单地认为是需要运算的式子,官方的解释为表达式由一个或多个运算对象组成,对表达式求值将得到一个结果,字面值和变量是最简单的表达式,其结果就是字面值和变量的值。

基础

我们不必钻牛角尖,我们要通过实践向前看,通过实践一步步向前,当我们对整体有了解之后然后再回头来钻牛角尖、研究背后的道理。

基本概念

C++定义了一元运算符、二元运算符、三元运算符

//example1.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
//运算符举例
int i = 3;
int *ip = &i; //一元运算符&
int j = i * 3; //二元运算符
bool flag = false;
i = flag ? 23 : 43; //因为falg为false则右边表达式计算结果为43
cout << i << endl; // 43
/*
state?return if state is true:return if state if false
*/
return 0;
}

来其他运算,下面列举的有些我们已经在前面学过了,有些没有学习过

运算对象转换

也就是从一种类型转换到另一种类型,我们现在知道的有自动转换与强制转换,如果记得不太清楚可以翻到前面的章节进行回顾。

重载运算符

我们使用的cout<<中的<<就是对<<运算符的一种重载,但是我们现在不进行讨论,不然初学者或者小白肯定会懵逼的哦,后面我们会慢慢接触到的。

左值和右值

表达式可以分为左值和右值,左值可以位于赋值语句的左侧,右值不能。

优先级与结合律

复合表达式是指含有两个或者多个运算符的表达式,表达式通过运算方式连接在一起

//example2.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int i = 6 + 3 * 4 / 2 + 2;
cout << i << endl; // 14
//程序上是怎样处理的呢
int j = (6 + ((3 * 4) / 2) + 2);
//这是上面复合表达式中运算符中的运算优先级
cout << j << endl; // 14

//我们可以自己使用括号来达到我们特定的效果
int k = 1 + 2 * 3;
cout << k << endl; // 7
k = (1 + 2) * 3;
cout << k << endl; // 9

//应用举例
int arr[] = {9, 1, 2, 3, 4};
cout << *(arr + 4) << endl; // 4
cout << *arr + 4 << endl; // 13
//在*arr+4中 *先于arr结合使用然后得到结果在参与+的运算
return 0;
}

求值顺序

运算符的优先级规定了运算对象的组合方式,但还有一点就是一个复合表达式中它们的计算顺序是怎样的

对于

f()+g()*h()+j()

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

C++没有明确指明大多数二元运算符左右表达式的运算顺序,但是在大多数编译器中都是从左到右的

举例代码

//example3.cpp
#include <iostream>
using namespace std;
int f1();
int f2();

int global_int = 1;

int main(int argc, char **argv)
{
int i = f1() + f2(); // 3 + 6
//在初始化i是右边的表达式f1()+f2()它们的执行顺序是怎样的呢
cout << i << endl;
// f1 excute gloal->2
// f2 excute gloal->4
// 9
//可见是从左到右执行,当f1执行得到结果后才会向后计算

cout << (f1() * 3 + (f2() * 3)) << endl;
// f1 excute gloal->5
// f2 excute gloal->7
// 45
//为什么?因为在此表达式中f1()*3的优先级与(f2()*3)相同
//二者会按照谁在前谁先执行

cout << f1() + f2() * 3 << endl;
// f1 excute gloal->8
// f2 excute gloal->10
// 45
//同理f1()与f2()*3同优先级,按照现后顺序执行
cout << f2() * 3 + f1() << endl;
// f2 excute gloal->12
// f1 excute gloal->13
// 56
//但是C++没有明确指明大多数二元运算符左右表达式的运算顺序,但是在大多数编译器中都是从左到右的
//也就是从C++的规定上,我们不知道f2()*3先执行还是f1()先执行
return 0;
}
int f1()
{
global_int += 1;
cout << "f1 excute gloal->" << global_int << endl;
return 1 + global_int;
}
int f2()
{
global_int += 2;
cout << "f2 excute gloal->" << global_int << endl;
return 2 + global_int;
}

算数运算符

算术运算符有

+   一元正号     + expr
- 一元负号 - expr
* 乘法 expr * expr
/ 除号 expr / expr
% 求余 expr % expr
+ 加号 expr + expr
- 减法 expr - expr

关于它们的优先级,一元运算符优先级最高,其次是乘法和除法,优先级最低的为加减法,算术运算符满足结合律,当优先级相同时按照从左向右的顺序进行组合

//example4.cpp
#include <iostream>
#include <climits>
using namespace std;
int main(int argc, char **argv)
{
int num1 = -199;
bool num2 = NULL; // NULL等价于0,false等价于0
int num3 = -num2;
cout << num1 << " " << num2 << " " << num3 << endl; //-199 0 0

//我们要注意在算数运算时要注意防止数据溢出的情况
int num4 = INT_MAX;
cout << num4 << endl; // 2147483647
num4 = num4 + 1;
cout << num4 << endl; //-2147483648
return 0;
}

limits头文件

可见我们上面有用到一个新的头文件,climits在C语言中为limits.h,其内有宏定义各个基本类型的标识范围,不要恐惧,这些玩意根本不是让人记忆的,我们要知道有这么回事,在由于语法提示的编辑器内写代码,一敲不就出来了吗,但是我们要知道它们是做什么的,随着我们对知识的掌握,我们在回过头研究它们的表示范围为什么是这样,这就是计算机编码的范畴了同样也属于计算机组成原理内的重要知识

//example5.cpp
#include <iostream>
#include <climits>
using namespace std;
int main(int argc, char **argv)
{
// char
cout << CHAR_MIN << endl; // -128
cout << CHAR_MAX << endl; // 127

// signed char
cout << SCHAR_MIN << endl; // -128
cout << SCHAR_MAX << endl; // 127

// unsigned char
cout << UCHAR_MAX << endl; // 255

// short
cout << SHRT_MIN << endl; // -32768
cout << SHRT_MAX << endl; // 32767

// unsigned short
cout << USHRT_MAX << endl; // 65535

// int
cout << INT_MAX << endl; // 2147483647
cout << INT_MIN << endl; //-2147483648

// unsigned int
cout << UINT_MAX << endl;

// long
cout << LONG_MAX << endl; // 2147483647
cout << LONG_MIN << endl; //-2147483648

// unsigned long
cout << ULONG_MAX << endl; // 4294967295

// unsigned long long
cout << ULONG_LONG_MAX << endl; // 18446744073709551615

// longlong
cout << LONG_LONG_MAX << endl; // 9223372036854775807
cout << LONG_LONG_MIN << endl; //-9223372036854775808

// float
cout << __FLT_MANT_DIG__ << endl; // 24 尾数
cout << __FLT_DIG__ << endl; // 6 最少有效数字位数
cout << __FLT_MIN_10_EXP__ << endl; //-37 带有全部有效数的float类型的负指数的最小值(以10为底)
cout << __FLT_MAX_10_EXP__ << endl; // 38 float类型的正指数的最大值(以10为底)
cout << __FLT_MIN__ << endl; // 1.17549e-038 保留全部精度的float类型正数最小值
cout << __FLT_MAX__ << endl; // 3.40282e+038 float类型正数最大值
return 0;
}

除法与求余

下面我们在看一看我们不太熟悉的算术运算符

在注意的一点是求余运算不支持左右表达式为浮点型,也就是只支持整数求余算数运算

//example6.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
//对于加减法在此不再举例,我们在上面小结已经有了了解
//我们来看两个比较特殊的算数 除法与求余
int num1 = 21 % 5;
int num2 = 3 % 2;
int num3 = 9 % 3;
cout << num1 << " " << num2 << " " << num3 << endl; // 1 1 0
//明白怎么回事了吧,就是我们计算整数除法,那个余数啊,不会怕这都不知道这是小学的只是啊
//没关系我们把它学会的就好了

//还有特殊的地方为整数的除法
float num4 = 4 / 3;
cout << num4 << endl; // 1
cout << (float)4 / 3 << endl; // 1.33333
cout << 4.f / 3 << endl; // 1.33333
//可见在整数除法中,除到不可除就不再计算了,也就是得到了整数部分 剩下的余数进行了舍弃
//解决办法就是将expr / expr 上下的数值至少一个为浮点型,然后用浮点型变量存储起来
return 0;
}

负数求余

被除数与除数有负数时进行求余运算时,结果是怎样的呢?

  • m%(-n) 等于 m%n
  • (-m)%n 等于 -(m%n)
  • (-m)%(-n)=-(m%n)

简单可以记住,求余的结果的正负号有%符号左边的正负号决定

代码举例

//example7.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
cout << -4 % 3 << endl; //-1
cout << 4 % -3 << endl; // 1
cout << -4 % -3 << endl; //-1
//这都是怎么回事
//计算规则是这样的
// m%(-n) 等于 m%n
//(-m)%n 等于 -(m%n)
//对于(-m)%(-n)怎么解释呢
//(-m)%(-n) 等于 -(m%(-n))
//又(m%(-n))等于m%n
//则(-m)%(-n)=-(m%n)
return 0;
}

逻辑和关系运算符

填坑中

赋值运算符

填坑中

递增和递减运算符

填坑中

成员访问运算符

填坑中

条件运算符

填坑中

位运算符

填坑中

sizeof运算符

填坑中

逗号运算符

填坑中

类型转换

填坑中

运算符优先级表

填坑中