第三课目录

  • 运算符与表达式
    • 算术运算符
    • 关系运算符
    • 逻辑运算符
      • 德摩根律
      • 使用断言assert
    • 位运算符
      • 补码
    • 补码与位运算
    • 赋值运算符
    • 杂项运算符

运算符与表达式

数学是科技发展的基础,数学公式的意义十分重要,比如改变世界的欧拉方程:
eπi+1=0e^{\pi i}+1=0eπi+1=0
为了用机器创造世界,就需要机器能够表达公式;
运算符
运算符是编译器可识别并执行特定数学或逻辑操作的符号,C++内置丰富的运算符,并提供了以下类型的运算符:
1.算术运算符;
2.关系运算符;
3.逻辑运算符;
4.位运算符;
5.赋值运算符;
6.杂项运算符;
表达式
在程序中,运算符用于操作数据,数据被称为操作数,使用运算符将操作数连接而成的式子称为表达式;
表达式具有以下特点:
1.常量与变量都是表达式,比如,常量3.14,变量i;
2.运算符的类型对应表达式的类型,比如算术运算符对应算术表达式;
3.就像python中的表达式,每个C++表达式都有返回值,即表达式有运算结果;

算术运算符

有两个变量A和B:

int A=10;
int B=20;

有以下操作:

需要两个操作数才能完成:
+ 两个数相加
/ 分子除以分母,两个整型数得到整型,若含有浮点型,则结果为浮点型
% 取余数,两个数必须为整型只要一个操作数就能完成(单目运算):
++ 自增运算,整数值增加1,++可以在操作数的前或者后
-- 自减运算,整数值减少1,--可以在操作数的前或者后
但++和--位于操作数前后会有微小的差异,差异放到后期的面向对象阐述
//前置
std::cout << ++A << endl;
输出11
//后置
std::cout << A++ << endl;
输出10

关系运算符

关系运算符如下:

== 检查两个操作数的值是否相等,相等则为true,返回1,否则false返回0
!= 检查两个操作数的值是否不等,不等则为true
> 检查左操作数是否大于右操作数
< 检查左操作数是否小于右操作数同理还有
>= 和 <=

C++与python相比,关系运算的写法也更加严格,关系运算的表达式需要用括号包围,示例:

std::cout << (A == B) << endl;
运算结果为false,false是一个表达式,值为0,所以输出0

逻辑运算符

假设变量A的值为1,变量B的值为0,(用布尔型存储可以节省空间),存在以下逻辑运算:

&& 逻辑与,如果两个操作数都非0,则条件为真,返回1
|| 逻辑或,如果有一个操作数非0,则条件为真,返回1//单目运算,可以不用括号包围
! 逻辑非,条件为真的逻辑表达式会转为假

在圣经中,有一句话:
To be or not to be,that’s a question
现在可以通过机器获得答案:

(A==true||A!=true)

德摩根律

A∪B‾=A‾∩B‾,A∩B‾=A‾∪B‾\overline{A\cup B}=\overline{A}\cap \overline{B},\overline{A\cap B}=\overline{A}\cup \overline{B}A∪B=A∩B,A∩B=A∪B
通过C++可以描述为:

(!(A||B)==(!A&&!B)) //1
(!(A&&B)==(!A||!B)) //1

使用断言assert

在开发测试环节,常常会用到断言assert判断样例是否能被正确处理:

#include <assert.h>int main()
{bool A = true;//值为1bool B = false;//值为0//使用断言验证 德摩根律//断言1assert( (!(A || B) == (!A && !B)) );//断言2assert( ((A || B) == (!A && !B)) );return 0;
}

断言1是德摩根律,断言2不是德摩根律,在执行到断言1时,不会出错,执行到断言2会报错:

只有当断言assert()内的表达式为true(值为1)才会顺利执行,否则(表达式返回false,值为0)就会报错

位运算符

位运算符作用于位,并逐位执行操作,真值表如下:

p  q  p&q  p|q  p^q (异或)
0  0   0    0    0
0  1   0    1    1
1  0   0    1    1
1  1   1    1    0

位运算是用于操作数的bit逐位运算,逻辑运算的操作对象是布尔类型;
位运算符还有:

~ 取反
<< 左移
>> 右移

位运算符&,|,^属于双目运算符,其结合性都是从左到右,优先级高于逻辑运算符,低于关系运算符;
同一层的优先级:&>^>|;
优先级最好通过括号确定更不容易出错
实例:

int a=10;
int b=20;cout<<(a&b)<<endl; //01010&10100=00000=>0
cout<<(a|b)<<endl; //01010|10100=11110=>30
cout<<(a^b)<<endl; //01010^10100=11110=>30
//与补码相关
cout<<(~a)<<endl; //~ 0000 0000 0000 1010=1111 1111 1111 0101=>-11
cout<<(a<<2)<<endl; //00001010<<2=00101000=>40
cout<<(a>>2)<<endl; //00001010>>2=00000010=>2

在取反操作上,反而得到一个负数,这与补码相关,因为C++内部数值以补码表达;除此之外,移位运算也与有无符号数相关,所以还需要补充补码与有无符号数相关的内容;

补码

在电路设计上,机器只能做加法不能做减法,所以需要找到一种方法(补码)让机器的加法实现减法;
机器数
一个数在机器中以二进制表示,机器数是带符号的,在机器中,用一个数的最高位保存符号位,正数为0,负数为1:
以上的两个整型数,默认4个字节,即32位;
真值
真值是真正数学意义上的数值,由于机器数第一位是符号位,所以机器数的形式值不等于真值;
无符号数的补码
无符号数补码就是平时接触的二进制十进制转换:
b(1011)=1×23+0×22+1×21+1×20=11b(1011)=1\times 2^{3}+0\times 2^{2}+1\times 2^{1}+1\times 2^{0}=11b(1011)=1×23+0×22+1×21+1×20=11
有符号数的补码
对于长度为www的有符号数的补码,转为对应数值的计算为:
−xw−12w−1+∑i=0w−2xi2i-x_{w-1}2^{w-1}+\sum_{i=0}^{w-2}x_{i}2^{i}−xw−1​2w−1+i=0∑w−2​xi​2i
比如:
b(1011)=−1×23+0×22+1×21+1×20=−5b(1011)=-1\times 2^{3}+0\times 2^{2}+1\times 2^{1}+1\times 2^{0}=-5b(1011)=−1×23+0×22+1×21+1×20=−5


可以发现,无符号数的补码等于原码;
有符号数的正数,其补码等于原码;如果是负数,补码等于原码取反再加一;


因为正数的补码等于原码,负数的补码则不同,正数相加还是正数,正数加负数即为减法,得到结果还是补码,而这个补码正好对应差的真值,这就是机器只保存补码的原因
补码对应的数值范围如下:
U代表无符号,T代表有符号
补充:字节序
32位机器用32bit(32字长)即4byte作为一个字,8byte就称为双字,字是CPU一次性处理的单元,而一个字在内存中如何以byte存放?
假设现在有一个字的内容是整型数0x01234567,这个字的4个byte将被连续存于存储器的0x100,0x101,0x102和0x103的位置(存储器的单元是字节);
字节序即为多字节对象存储在内存中的字节顺序,有两种不同的存储方案:大端法和小端法;
1.大端法:最高有效字节在最前面的方式称为大端法,用于大多数IBM机器,internet传输
2.小端法:最低有效字节在最前面的方式成为小端法,用于Intel兼容的机器
观察机器数
观察以下机器数:

int i1=0;
int i2=-1;
int i3=-2147483648; //32字长的最小有符号整型数
int i4=2147483647; //32字长的最大有符号整型数unsigned int u1=0;
unsigned int u2=4294967295; //32字长的最大无符号整型数
unsigned int u3=2147483648;
unsigned int u4=2147483647;cout << &i3 << endl;

在调试阶段,根据i3地址(0x0077F9A4)找到i3:
i3=-2147483648,补码即0x8000 0000;
上图的visual studio设置为每行显示两字节,可以看出我的机器上字排序为小端法,因为我的机器是英特尔机器,英特尔机器都是小端法;
另外也反映了,机器内保存的数都是补码形式;

补码与位运算

验证真值
定义以下函数:

//二进制转无符号整型
unsigned int btou(unsigned int num)
{return (unsigned int)(num);
}
//二进制转有符号整型
int btot(int num)
{return (int)(num);
}

验证真值,体现有无符号数的区别:

    cout << btou(0xFFFFFFFF) << endl;cout << btot(0xFFFFFFFF) << endl;

输出分别为:
另外补充一点,补码的编码与真实值的关系如下:

这是一个分段函数,每一段呈正比关系;

回顾之前的按位取反:

int a=10;
cout<<(~a)<<endl; //~ 0000 0000 0000 1010=1111 1111 1111 0101=> -11

现在进行探索:

int a = 10;
int b = ~a;cout << &a << endl;
cout << &b << endl;

根据a的地址0x003CFDA0找到a:

0x003CFDA0  0a 00  ..
0x003CFDA2  00 00  ..

由于机器是英特尔架构,所以实际这个int的4字节(1个字)值为:0000 000a,这是补码,但a是正数,补码等于原码,所以a的值确实是10;
b的地址是0x003CFD94,找到b:

0x003CFD94  f5 ff  ?.
0x003CFD96  ff ff  ..

b的补码为FFFF FFF5,二进制形式为:

1111 1111 1111 0101

容易看出b的补码确实是a的补码按位取反,通过验证真值部分实现的函数int btot(int num),得到这个补码对应的有符号值为-11;现在就能清晰解释为什么对10取反会得到-11这样的奇怪结果了;
移位运算补充
左移运算情况单一:
右移运算分两种情况,一个是逻辑右移,一个是算术右移;
逻辑右移:移走的位填充为0
算术右移:移走的位填充与符号位有关,负数填充1
对于有符号数,尽量不要使用右移运算,因为到底是逻辑右移还是算术右移完全取决于编译器的判断;

赋值运算符

赋值运算符有如下几种常用形式:

= 简单的赋值运算,右边操作数的值赋给左边操作数
+= 右边操作数的值加上左边操作数的值赋值给左边操作数
同样的还有:
-=
*=
/=
%= 把两操作数的余数赋给左边操作数

回想python,这类似in-place(当然,python数值对象没有in-place之分,但tensor或ndarray对象存在in-place)
结合位运算,延伸出赋值位运算:

<<= 左移赋值运算  c<<=2等价于c=c<<2
>>= 右移赋值运算
&=  按位与再赋值  c&=2等价于c=c&2
^=
|=

实例:

int c=a+b;
cout<<c<<endl;
c+=a
cout<<c<<endl;

杂项运算符

杂项运算符有以下:

sizeof 返回变量占用空间的字节数,可见sizeof不是函数而是运算符condition?x:y 条件运算符:C++内唯一一个三目运算符,如果condition为真,返回x,否则返回y, 逗号运算符,顺序执行运算,整个逗号表达式的值是以逗号分隔的最后一个表达式的值
即:表达式 a,b,c 的值为c的值.和-> 成员运算符,用于引用类,结构和共用体的成员Cast 强制转换运算符,比如int(2.2)会返回2& 指针运算符&可以返回变量的地址
* 指针运算符*指向一个变量,例如*var将指向变量var

实例:

int x = 10, y = 20;cout << sizeof(x) << endl;int c = x > y ? 1 : 0;int e = (x, y, c);
cout << e << endl;  //0float f = float(e);float* p = &f;
cout << *p << endl; //0

成员运算符举例:

typedef struct {short Sunday = 0;short Monday = 1;short Tuesday = 2;short Wednesday = 3;short Thursday = 4;short Friday = 5;short Saturday = 6;
}Week;Week w;
cout << w.Friday << endl; //5
cout << sizeof(w) << endl; //14,因为short占用2byte

关于运算符优先级,需要记住:
1.一般,单目运算符优先级高于双目运算符;
2.最好加括号确保优先级的正确性

第三课.运算符与表达式相关推荐

  1. Oracle PL/SQL 第三章--运算符与表达式

    Oracle PL/SQL 第三章--运算符与表达式 目录 Oracle PL/SQL 第三章--运算符与表达式 1.运算符分类 1.1.算术运算符 1.2.关系运算符 1.3.比较运算符 1.4.逻 ...

  2. 单片机C语言中的位运算符,单片机c语言教程第八课 运算符和表达式(位运算符)...

    学过汇编的朋友都知道汇编对位的处理能力是很强的,但是单片机C语言也能对运算对象进行按位操作,从而使单片机C语言也能具有一定的对硬件直接进行操作的能力.位运算符的作用是按位对变量进行运算,但是并不改变参 ...

  3. C++学习笔记——第三天运算符和表达式

    目标 掌握C++支持的各种运算符和应用 掌握C++支持的由各种运算符和常量变量构成的表达式,语句及其应用 运算符 C++中包含了C语言中的运算符和表达式,并且又增加了一些新的运算符. ::作用域运算符 ...

  4. Java篇第三回——运算符、表达式与语句(C不好的也快来)

    CONTENT 一.运算符.表达式 1.算七精 2.关六七 3.逻三布 4.赋右左 5.位四四 6.instanceof运算符 7.小结 二.语句 1.类型:方表复空控p 2.为啥会学不会的分支语句和 ...

  5. C语言学习(三)运算符、表达式和语句

    参考书:<C Primer Plus>第六版 while循环,程序清单1. 基本运算符有:赋值运算符=.加法运算符+.减法运算符-.乘法运算符*.除法运算符/,每个运算符都有自己的优先级. ...

  6. C语言基础应用(三)运算符与表达式

    一.运算符的分类 C语言中的运算符号分为10类: 算术运算符 . 关系运算符 . 逻辑运算符 . 位操作 运算符. 赋值运算符 . 条件运算符 . 逗号运算符 . 指针运算符 .求字节数运算符和特殊运 ...

  7. 神经网络与深度学习——TensorFlow2.0实战(笔记)(三)(python运算符和表达式)

    从程序中学习知识点 1.算术运算符 #运算符(Operator):完成不同类型的常量.变量之间的运算 #除法运算 / 结果是一个浮点型的精确数的值,与java等其他语言的不同之处 print(7/2, ...

  8. java 表达式2004的值_javaSE习题 第三章 运算符、表达式和语句

    问答: 1.下列System.out.printf的结果是什么? int a=100,x,y; x=++a; y=a--; System.out.printf("%d,%d,%d" ...

  9. Java第三章-运算符、表达式和语句课后作业

    文章目录: 一:判断题 二:单选题 三:挑错题 四:阅读程序题(给出[代码]注释标注的代码的输出结果) 五:编程题 1.编写一共应用程序,用for循环出俄文的"字母表" 2.编写一 ...

最新文章

  1. [Node.js] 2、利用node-git-server快速搭建git服务器
  2. python解释器哪一年_Python即Python解释器的发展史
  3. HDU-2149(博弈)
  4. 如何提高电脑运行速度_电脑运行速度慢的解决方法
  5. 完整机器学习项目的流程
  6. 详述MySQL事务及ACID特性的实现原理
  7. 消息中间件学习总结(7)——RocketMQ之万亿级数据洪峰下的分布式消息引擎
  8. 网页模板快速建站工具_自助建站相对传统建站有什么优势 - 建站极速通
  9. 资源丨用PyTorch实现Mask R-CNN
  10. 多数据点拟合曲线,最小二乘法,矩阵
  11. 关于我小孩的教育意见
  12. 北大生物信息学学习(2) 生物学及生物学信息学的发展
  13. 清理Windows.edb文件释放C盘空间(原创)
  14. 开放、创新、合作,共赢多样性计算新时代
  15. 学习java兴趣之作模仿原神抽卡模块。希望哥、姐指点一下以下是代码;
  16. 全球上线!ABB中国涡轮增压器分拆 – 数据清理阶段完成
  17. 笔记本电脑连接HDMI接口拓展显示器没有反应
  18. 不能上升到金钱的爱都不是真爱!
  19. java基础系列(四)UTF-8和GBK编码的区别
  20. office卸载后无法重装终极解决办法

热门文章

  1. 宇宙条一面:十道经典面试题解析
  2. 开发必备快速定位排查日志 9 大类命令详解
  3. Java新手,强烈不建议你用 a.equals(b) 判断对象相等!
  4. 面试官一个线程池问题把我问懵逼了。
  5. 手把手教你重构乱糟糟的代码
  6. 面试官让我手写一个生产者消费者模式?
  7. 从零到百亿级,揭秘科大讯飞广告平台架构演进之路
  8. 微服务架构下的测试之道
  9. Web网站搭建从零到一
  10. 8个让人相见恨晚的软件,每一款都十分良心