绪论
书接上文,不知道数组的两个小游戏是否让你头昏眼花了,但请你保持良好的自信心,百炼自成钢,在小游戏里面犯的错,最终都将转化成你的经验!!本章将写道 操作符 该章理论偏多,但是关于二进制的一些问题会稍微有点麻烦。
所以安全带系好,发车啦(建议电脑观看)。
思维导图:
要XMind思维导图的话可以私信哈
目录
1.算数操作符
2.位移操作符
3.位操作符
4.赋值操作符
5.单目操作符
6.关系操作符
7.逻辑操作符
8.条件操作符
9.逗号表达式
10.下标引用,函数调用和结构体成员
11.表达式求值
11.1隐式类型转换
11.2算术转换
11.3操作符属性
1.算数操作符
知识点:
算数操作符:+ ,- ,* , / , %
其加减乘除大概就是和数学中的一样的进行计算
而最后的 % :他叫取模,其实也并不陌生就是除法的余数:3 % 2 == 1
细节(注意点):
除%操作符之外的其他加减乘除都可用作到浮点数的计算,也就是 %操作符的两端只能是整数,返回的也是整除之后余的。
对于 / 来说当他的两个操作数(除法的两端)都为整数时执行整数除法,而只要其中有浮点数就将回执行浮点数除法 : 3 / 2.0 = 1.500000
2.位移操作符
知识点:
位移操作符:>> , <<
他们两个是用来移动二进制位的,且位移操作符的操作符只能是整数
练习:
细节:
先了解一下二进制位:
在计算机中所有的运算都是通过转化成二进制后才能再进行计算,所以无法转化成二进制的东西计算机无法运算
一个int类型的数他是占四个字节的而1个字节有是8bit所以四个字节就是32个bit
所以2可以表示成:
00000000000000000000000000000010
== (0 * 2 ^ 0 ) +( 1 * 2 ^ 1 )+ (0 * 10 ^ 3 )+ .... == 2 ( ^ 表示次方)
如十进制中的12 ==( 2 * 10 ^ 0 ) + ( 1 * 10 ^ 1)
其次我们要了解,原码,反码,补码
原码:就是一个数直接写出来的二进制形式
2:00000000000000000000000000000010
-2:10000000000000000000000000000010
(-2的二进制会在最前面加上1代表负数的 符号位 )
反码:原码按位取反的结果,也就是除符号0变成1,1变成0。
补码:反码+1的结果
所以他们的转换方式:原码按位取反得到反码再+1得到补码
所以补码返回成原码的方法就是 先-1,在按位取法;
但神奇的是补码进行原码的转化方法也能返回成原码也就是:补码先按位取法再加1,也会变回原码
注意:在内存中的二进制都是以补码的形式存储的,而我们看到的是原码,
正数的原,反,补相同,故负数则不同
下面以一个二进制-2来展示刚刚的原反补关系
原码:10000000000000000000000000000010
反码:111111111111111111111111111111111101
补码:111111111111111111111111111111111110
1.倒回去-1,取反
反码:111111111111111111111111111111111101
原码:10000000000000000000000000000010
2.以原码转化的方法
补码:111111111111111111111111111111111110
反码按位取反:
10000000000000000000000000000001
在加1:
10000000000000000000000000000010
附:二进制的加减也和十进制一样,不够减就向前面借(借的是2),加多了就向前增(到2就进)
如:1+1:
00000000000000000000000000000001
00000000000000000000000000000001
00000000000000000000000000000010
2-1:
00000000000000000000000000000010
00000000000000000000000000000001
00000000000000000000000000000001
左移操作符(<<):左抛弃,右边补0
右移操作符(>>):算术右移(右边抛弃,左边补符号位),逻辑右移(右边抛弃,左边补0),对于大部分编译器都是算术右移,所以下面例子以算术来看
通过对二进制的一定了解,现在再看左右位移操作符,也就是将2进制位整体挪一下位置
以-2来向左和右各移一位来看:也就是 -2 << 1 , -2 >> 1
没有移负数这种操作哈(3 >> -2 ?)
3.位操作符
知识点:
位操作符:& , | ,^
进行改变二进制的一种操作符
&:按位与:将两个二进制按位与比较每一位数有0就与成0,都为1才与成1;一假则假
| :按位或:将两个二进制按位或比较每一位数有1就或成1,都为0才或成0;一真则真
^:按位异或:每位二进制里比较各个位的相同就为0,不同就为
细节:
操作数必须为整数,他是在内存中计算的所以他们在计算过程中是补码,最终还要转化成原码出来
练习:
下面通过练习再来更加深刻的了解上面的位操作符
#include<stdio.h> int main() { int num1 = 1; int num2 = 2; num1 & num2; //00000000000000000000000000000001 //00000000000000000000000000000010 //1 & 2 = 0;按位与一假则假 //00000000000000000000000000000000 num1| num2; //00000000000000000000000000000001 //00000000000000000000000000000010 //1 | 2 = 3;按位或一真则真 //00000000000000000000000000000011 num1 ^ num2; //00000000000000000000000000000001 //00000000000000000000000000000010 //1 & 2 = 3;按位异或,两同为0,相异为1 //00000000000000000000000000000011 return 0; }
不创建临时变量的前提转交换两个数
求一个二进制中有几个1234
4.赋值操作符
知识点:
赋值操作符:=
附:+= , -= , *= , /= , %= , >>= , <<= , &= , ^= , |=
例如 += : a = a + 2 : a += 2 ,这两个效果一样,其他同理
>>= : a = a >> 3 ; a >>= 3
5.单目操作符
知识点:
单目操作符:! , - , + , & , sizeof , ~ , -- , ++ , * , ()
! :逻辑反操作符(将真变成假(0))
- :(负值)和中数学一样
+:(正值)没什么用并不能将负数变成正数
&:(取地址)只要是变量都会有一个地址,一般用在指针处
sizeof :求操作数的类型大小(如int类型就是4byte)
~ :对二进制进行按位取反
++ , -- :前,后置减减,前置是表示在使用前减减,后置则是表示在使用后在减减
++ -- 在使用过程中会改变值,即a-- : a在使用后 a = a -1
* : 解应用操作符,一般用在指针处
():强制类型转换,如 3.14原本是一个浮点型的,
!:
while(1)//此括号处因为为非0使用为真可以进入循环 { 。。。; } while(!1)//虽然他是非0但加上!就变成了(0)假 { .......; } while(!0)//此处本为假但是通过反操作符使他变成(1)真 { ...; }
细节:
附:
unsigned:
无符号的,如-1看成无符号时,就会将他在内存中的补码直接看成原码
-1 原码:10000000000000000000000000000001
反码:111111111111111111111111111111111110
补码: 111111111111111111111111111111111111
所以从上面来看如果unsigned int = -1 将会变一个很大的值
sizeof:
int a = 2 ; sizeof(a) == sizeof(int) == sizeof a != sizeof int(数据类型必须加括号)
所以sizeof并不是函数,而是操作符
当sizeof在函数内部使用时要注意参数类型是否是指针,如果是指针其大小将会是4/8
并且注意数组作为形参时其本质也是指针,如下
#include <stdio.h> void test1(int arr[]) { printf("%d\n", sizeof(arr)); } void test2(char ch[]) { printf("%d\n", sizeof(ch)); } int main() { int arr[10] = {0}; char ch[10] = {0}; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(ch)); test1(arr); test2(ch); return 0; }
上面这串代码最终将会打印
40
10
4
4
40因为int为4个字节,而数组大小有10个最终计算的大小就是4*10=40
同理10是因为char类型是1个字节,有10个元素10*1 = 10
而后面两个均为4是因为在形参处的数组其本质是指针他也可以写成(char*ch,int * arr)并且指针的大小都是4个字节,与其的类型无关(其类型关系我会在指针章在细说),所以sizeof就会算出4来
6.关系操作符
知识点:
关系操作符: < , > , >= , <= , == , !=
关系操作符和数学中的一样,
其中== 是比较性的 等号,注意与 赋值操作符 = 区别
!= 比较是否 不等
7.逻辑操作符
知识点:
逻辑操作符:&& , ||
&& : 就是与 , || 就是或
&&:判断其两个操作数是否为真(非0就为真),同时为真才为真(即一假则假)
|| :判断其两个操作数是否有一个为真,一个为真就为真(即一真则真)
其实就和数学中的或(∨)与(∧)一样
细节:
当连续使用时,从左往右一次进行
短路:
&&操作符,他是先判断左边,若左边已经为假,他再看右边的了
||操作符同理,他是也是先判断左边,但当左边为真是他就不会再看右边是否为真了
练习:
360笔试题
8.条件操作符(三目操作符)
知识点:
条件操作符:exp1 ? exp2 : exp3
当条件1(exp1)满足条件时就会进行条件2(exp2)中 , 若exp1不满足则进入exp3中
细节:
他就好比是if语句
if(exp1) { exp2; } else { exp3; }
9.逗号表达式
知识点:
逗号表达式: exp1 ,exp2 , exp3 ,.....
从左往右进入表达式,其中可能各个表达式会相互影响,也可能不影响,但最终结果由最后一个来定。
下面通过代码来更好的了解其
int main() { int a = 10; int b = 20; int c = (a ,b -= 10, a += 10 , b += a); //逗号表达式从左往右以此计算表达式,最终取最后的表达式 // c == 30; }
10.下标引用,函数调用和结构体成员
知识点:
下标引用,函数调用和结构体成员:[ ] , ( ) , . , ->
[ ]下标引用操作符:就是对数组的内容进行引用如arr[2]
( )函数调用操作符:就是在函数时所用的括号,其有两个操作数一个是函数名,另一个是就是传递给函数的参数
. 和 -> 都是用来访问结构体成员的其用法是 结构体名.结构体成员名 ; 结构体指针 -> 成员名
#include <stdio.h>
#include <stdio.h> struct Stu { char name[10]; int age; char sex[5]; double score; }; void set_age2(struct Stu* pStu) { (*pStu).age = 18;//其简易方法如下 pStu->age = 18;//结构成员访问 } int main() { struct Stu stu; struct Stu* pStu = &stu;//结构成员访问 pStu->age = 20;//结构成员访问 stu.age = 20;//结构成员访问 set_age2(&stu); return 0; }
11.表达式求值
表达式求值有一定的顺序,一部分由操作符的优先级和结合性决定
而有些操作数在求值过程中可能要转化成其他类型
11.1隐式类型转换
我们所需要知道的知识点:
在整数运算中总是至少以整形类型的精度进行的,所以为了这个精度一些小于整形大小的如短整形(short)字符(char)都会先被转换成整形再进行计算,这种转换被称作整形提升
整形提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长 度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令 中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。
细节:
如何进行整形提升:
如char a = 1;其的二进制位因为是char类型(1个字节)所以只有8bit即 00000001
整形提升时,高位补其的符号位,即00000000000000000000000000000001
若char b = 2;同理其最后的整形提升成00000000000000000000000000000011
char c = a + b ;他们在计算过程中回先整形提升后再进行计算出后再进行截断8bit的字符型
即:00000000000000000000000000000100 -> 00000100
隐式的整形提升
int main() { char a = 0xb6; short b = 0xb600; int c = 0xb6000000; if(a==0xb6)//此处本应该相等进入,但是因为有操作符== ,就导致其会进行整形提升 printf("a"); if(b==0xb600)//同上 printf("b"); if(c==0xb6000000) printf("c"); return 0; } int main() { char c = 1; printf("%u\n", sizeof(c));//而此处没有操作符就不会提升 printf("%u\n", sizeof(+c));//同上因为有操作符(+)就导致其会整形提升最终求出的就是4字节 printf("%u\n", sizeof(-c)); return 0; }
当有操作符时就会进行整形提升
11.2算术转换
知识点:
当不同类型的操作数进行计算时,必须转换成相同类型的才能继续继续计算。
他的转换方法时将低的转换到高的
long double 高
double
float
unsigned long int
long int
unsigned int
int 低
如
int a = 3;
float b = 3.5f;
a + f 时就要把int类型的转换成float
11.3操作符属性
知识点:
对复杂的表达式,影响其求值的有三个因素,1.操作符的优先级,2.操作符的结合性,3.是否控制求值所需
细节:
尽管我们了解了各种的操作符的优先级,结合性。但仍然不可能将所有代码问题都解决
如:int i = 1;
int a = (i++) + (i++) + (i++) 像这种我们仍然无法不断到底那个i先++
本章完。预知后事如何,暂听下回分说。