目录
1.各种操作符的介绍
1.操作符分类:
2.算术操作符
+ - * / %
这里有三个要注意的地方:
1.除了%操作符之外,其他的几个操作符都可以作用于整数和浮点数。
2.对于/操作符如果两个操作数都为整数,执行的是整数除法。而只要有一个是浮点数执行的就是浮点数除法。
3.%操作符的两个数必须为整数。返回的是整除之后的余数。
对于除法操作符来说,两边的操作数都是整数,执行的是整数除法
如果想计算出小数,除号两端至少有一个操作符是浮点数
如果先让浮点数保留一位小数可以在%前加个.1,根据需要情况保留几位自己定义
取模(取余),计算的是整除之后的余数,取模操作符的两边的操作数只能是整数
非法表达式
3.移位操作符
<<左移操作符
>>右移操作符
注:移位操作符的操作数只能是整数。
3.1<<左移操作符
在介绍操作符之前我们得了解一下二级制。
移位移的是什么位?移的是二进制位。移(2进制)位操作符
2进制
整数的二进制表示形式,其实有三种
原码、反码、补码
12 - 数值
2进制是:1100
8进制是:14
10进制是:12
十六进制是:C
正数的原码、反码、补码是相同的,简称原反补。
内存中存储的其实是二进制的补码
所以在参与移位的时候,移动后都是补码。
反过来就能得到原码,补码-1,得到反码,反码符号位不变,其他位按位取反得到原码。
另外一种方法:补码直接取反加一得到原码。
正数:
现在正式介绍什么是移位操作符
负数:
3.2>>右移操作符
移位:
首先右移运算符分为两种:
1.算术右移(平常见到的)
2.逻辑右移
<<右移操作符:
注:
对于移位运算符,不要移动负数位,这个是标准未定义的。
列如:
int num = 10;
num >> -1
这种写法是错误的。
3.3补码整形数值的意义
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理。
同时,加法和减法也可以统一处理,CPU只由加法器,此外,补码和原码相互转换,其运算过程是相同的
(1)解决了符号的表示的问题
(2)可以将减法运算转化为补码的加法运算来实现,克服了原码加减法运算繁杂的弊端,可有效简化运算器的设计
(3)在计算机中,利用电子器件的特点实现补码和真值、原码之间的相互转换,非常容易
(4)补码表示统一了符号位和数值位,使得符号位可以和数值位一起直接参与运算,这也为后面设计乘法器除法器等运算器件提供了极大的方便。总之,补码概念的引入和当时运算器设计的背景不无关系,从设计者角度,既要考虑表示的数的类型(小数、整数、实数和复数)、数值范围和精确度,又要考虑数据存储和处理所需要的硬件代价。因此,使用补码来表示机器数并得到广泛的应用,也就不难理解了。 - 来自百度
4.位操作符
位操作符有:
& 按位与
| 按位或
^ 按位异或
注:它们的操作符必须是整数。
这个三个操作符也是针对二进制位计算的!
1.&按位与
& - 按2进制位与
对应的二进制位有0,则为0,两个同时为1,才为1
口诀:全一为一
2.|按位或
| - 按2进制位或
对应的二进制位有1,则为1,两个同时为0才为0。
口诀:有一则一
3.^按位异或
^ - 按2进制位异或
对应的二进制位相异为1,两个相同才为0。
口诀:相异为一,相同为0
一道例子:
不能创建临时变量(第三个变量),实现两个数的交换。
第一种方法:
虽然这种也可以实现两个数的交换,但是当两个数非常大的时候两个数相加就会超过他本身这个类型的取值范围导致结果不同。
第二种方法:
异或虽然好但是也有缺点:
1.可读性差
2.效率也不如使用临时变量的方法快
3.异或只能支持对整除的交换。
所以交换变量的时候还是比较推荐下面的这种方法:
5.赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
int weight = 100; weight = 90;//使用赋值操作费 //赋值操作符可以连续使用,比如: int a = 10; int x = 0; int y = 20; a = x = y+1;//连续赋值 //拆分为 x = y + 1; a = x; //这样的写法更加清晰,易于调试,比较推荐这种写法。
复合赋值符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些运算符都可以写成复合的效果。
比如:
int x = 10; x = x + 10; x += 10;//复合赋值 //这两种写法意思是相同的 //其他运算符一样的道理,这样写更加简洁。
6.单目操作符
6.1单目操作符介绍
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
有单目操作符也有双目操作符甚至还有三目操作符。
比如 a + 1; +号,+操作符有两个操作数,双目操作符
单目操作符意思就是只有一个操作数。
6.1.1! 逻辑反操作
什么是逻辑反操作符?把真变为假,把假变为真。
或者我们直接输出在屏幕上
扩展:布尔类型
C99中引入的
布尔类型就是用来表示真假的类型
真:
假:
6.1.2 - 负值
用来表达负数时是单目操作符,用来两个数相减时是双目操作符。
6.1.3 + 正值
通常我们都会省略+正号,用来两个数相加时是双目操作符。
6.1.4 & 取地址
&和*通常都是配套使用的,取出的地址可以存放到指针里,pa就是一个指针,指针是用来存放地址的,通过*解引用就可以找到a的地址,以此来修a
6.1.5 seziof 操作数的类型长度(以字节为单位)
sizeof既是操作符也是关键字
函数调用的时候要写(),但是sizeof后边的括号可以省略,说明sizeof不是函数
sizeof既可以计算变量的大小,也可以计算类型的大小,还能计算数组的大小和指针的大小
指针在32位机器上是4个字节,64位机器是8个字节。
6.1.6 ~对一个数的二进制按位取反
0的补码是全零,按位取反之后是全一,补码减一,得到反码,反码符号位不变,其他位按位取反就得原码,打印出来是负一。
列子:
1.把a的二进制中的第五位改成1
思路:先把1向左移动四位,然后和a按位或。
那么又该怎么把它变回来?
还是先把1向左移动四位,然后~按位取反在和a按位与。
6.1.7 ++ 前置、后置++
后置++:先使用,后++,一次自增1
前置++:先++,后使用,一次自增1
前置++相当于a = a + 1;
6.1.8 -- 前置、后置--
前置--和后置--和上面的++是一样的
前置--,先--,后使用,一次自减1
后置--,先使用,后--,一次自减1
注:1.前置和后置++、--是会改变自身的值的,使用的时候要注意!
2.在表达式里要注意他们是先使用在自增,还是后使用在自增,否则会影响表达式的判断和传值。
test(a++);后面这个表达式使用完之后才++。
6.1.9 * 间接访问操作符(解引用操作符)
用来指针解引用拿出变量的地址。指针篇在详细介绍。
6.2.0 (类型) 强制类型转换
因为3.14是double类型的想要付给int类型就可以使用强制转换它的类型。
这样就不会有警告出现了,相当于取出了整形的部分,不需要小数。
6.2sizeof和数组
7.关系操作符
关系操作符:
>
>=
<
<=
!= 用于比较“不相等”
== 用于比较“相等”
注:在编程的过程中==和=不小心写错会导致错误的发生。
8.逻辑操作符
逻辑操作符有:
&& 逻辑与 相当于“并且”
|| 逻辑或 相当于“或者”
他们两个都是只关注真假
&& 两个为真时才为真,只要有一个假则为假。
||两个同时为假则为假,只要有一个为真则为真。
&&和||他们都具有短路规则
&&逻辑与,的短路规则是如果第一个为假则不执行后面的代码,直接返回假。
||逻辑或,的短路规则是如果第一个为真则不执行后面的代码,直接返回真。
9.条件操作符
exp1 ? exp2 : exp3
条件操作符又被称为三目操作符。
其实也可以写成条件表达式if和else
10.逗号表达
exp1,exp2,exp3,...expN
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
栗子1:
c是多少?
第一个表达式不影响后面的结果,第二个表达式a已经被改成了30,第三个表达式b = 31,根据逗号表达式,从左向右依次执行,整个表达式的结果是最后一个表达式的结果,所以c是31。
虽然整个表达式的结果是最后一个表达式的结果,但是根据前面的表达式会影响最后一个表达式的结果。
11.下标引用、函数调用和结构成员
1.[]下标引用操作符
操作符:一个数组名 + 一个索引值
2.()函数调用操作符
接受一个或者多个操作符:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
3.访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
结构体属于自定义类型,自定义类型还有枚举类型和联合体类型(也叫“共用体类型”)。
内置类型有:
char、int、short、long、long long、float、double等等。
为什么要有自定义类型?因为生活中有些对象要被描述的话,不能简单的只使用一个内置类型,这样无法全部描述出来,列如:
人:性别、名字、身高、体重、年龄等。
列子1:
创建一个人的类型结构体,里面定义的类型就是他的成员
前面是结构体类型后面是结构体变量名,初始化和数组一样需要用大括号{}阔起来。
传的是地址的话推荐使用->操作符,因为传的是地址函数使用的是指针变量来接收实参,在里面使用. 操作符使用起来就比较繁琐。
12.表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
12.1隐式转换
C的整形算术运算总是至少以缺省(默认)整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
整型提升的意义:
表达式的整型运算要在CPU的相应的运算器件内执行,CPU内整型提升(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度
通用CPU(general-purpose CPU)是难以直接实现两个8比特位字节直接相加运算(虽然机器指令中可能有这种字节相加命令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
比如这个代码,在执行运算的之前会把a和b提升为整型然后再执行加法运算。(表达式中的字符和短整型操作数在使用之前被转换为普通整型)
注:是类型小于整型的,比如char、short
加法运算完成之后,结果将被截断,然后再存储于a中。
整型提升是如何进行的呢?
整型提升是按照变量的数据类型的符号位来提升的。
负数的整型提升,高位补1
无符号整型提升,高位补0
char有符号的char取值范围是:-128~127,无符号的char的取值范围是0~255。
只要参与表达式运算或者表达式判断,就会发生整形提升。
列如:
因为+号和-号也是操作符所以发生了整形提升
其实if语句也会发生整型提升要注意()里面的表达式判断。
12.2算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数会转换为另一个操作数的类型否则操作就无法进行。下面的层次体系称为寻常算术转换。
long double
double
float
unsigned long int
long int
unsigned int
int
short
char
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
注:但是算是转换要合理,不然会出现一些潜在问题。
列如浮点数赋值给整形
float f = 3.14
int n = f;//隐式转换,会有精度丢失。
12.3操作符的属性
复杂表达式的求值有三个影响的因素。
1.操作符的优先级
2.操作符的结合性
3.是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
操作符优先级 :
操作符 | 描述 | 用法示例 | 结果类型 | 结合性 | 是否控制求值 顺序 |
() | 聚组 | (表达式) | 与表达式同 | N/A | 否 |
() | 函数调用 | rexp(rexp,...,rexp) | rexp | L-R | 否 |
[ ] | 下标引用 | rexp[rexp] | lexp | L-R | 否 |
. | 访问结构成员 | lexp.member_name | lexp | L-R | 否 |
-> | 访问结构指针成员 | rexp->member_name | lexp | L-R | 否 |
++ | 后缀自增 | lexp ++ | rexp | L-R | 否 |
-- | 后缀自减 | lexp -- | rexp | L-R | 否 |
! | 逻辑反 | ! rexp | rexp | R-L | 否 |
~ | 按位取反 | ~ rexp | rexp | R-L | 否 |
+ | 单目,表示正值 | +rexp | rexp | R-L | 否 |
- | 单目,表示正值 | -rexp | rexp | R-L | 否 |
++ | 前缀自增 | ++ lexp | rexp | R-L | 否 |
-- | 前缀自减 | -- lexp | rexp | R-L | 否 |
* | 间接访问 | * rexp | rexp | R-L | 否 |
& | 取地址 | & lexp | rexp | R-L | 否 |
sizeof | 取其长度,以字节 表示 | sizeof rexp sizeof(类型) | rexp | R-L | 否 |
(类型) | 类型转换 | 类型) rexp | rexp | R-L | 否 |
* | 乘法 | rexp * rexp | rexp | L-R | 否 |
/ | 除法 | rexp / rexp | rexp | L-R | 否 |
% | 整数取余 | rexp % rexp | rexp | L-R | 否 |
+ | 加法 | rexp + rexp | rexp | L-R | 否 |
- | 减法 | rexp - rexp | rexp | L-R | 否 |
<< | 左移位 | rexp << rexp | rexp | L-R | 否 |
>> | 右移位 | rexp >> rexp | rexp | L-R | 否 |
> | 大于 | rexp > rexp | rexp | L-R | 否 |
>= | 大于等于 | rexp >= rexp | rexp | L-R | 否 |
< | 小于 | rexp < rexp | rexp | L-R | 否 |
<= | 小于等于 | rexp <= rexp | rexp | L-R | 否 |
== | 等于 | rexp == rexp | rexp | L-R | 否 |
!= | 不等于 | rexp != rexp | rexp | L-R | 否 |
& | 位与 | rexp & rexp | rexp | L-R | 否 |
^ | 位异或 | rexp ^ rexp | rexp | L-R | 否 |
| | 位或 | rexp | rexp | rexp | L-R | 否 |
&& | 逻辑与 | rexp && rexp | rexp | L-R | 是 |
|| | 逻辑或 | rexp || rexp | rexp | L-R | 是 |
? : | 条件操作符 | rexp ? rexp : rexp | rexp | N/A | 是 |
= | 赋值 | lexp = rexp | rexp | R-L | 否 |
+= | 以...加 | lexp += rexp | rexp | R-L | 否 |
-= | 以...减 | lexp -= rexp | rexp | R-L | 否 |
*= | 以...乘 | lexp *= rexp | rexp | R-L | 否 |
/= | 以...除 | exp /= rexp | rexp | R-L | 否 |
%= | 以...取模 | lexp %= rexp | rexp | R-L | 否 |
<<= | 以...左移 | lexp <<= rexp | rexp | R-L | 否 |
>>= | 以...左移 | lexp >>= rexp | rexp | R-L | 否 |
&= | 以...与 | lexp &= rexp | rexp | R-L | 否 |
^= | 以...异或 | lexp ^= rexp | rexp | R-L | 否 |
|= | 以...或 | lexp |= rexp | rexp | R-L | 否 |
, | 逗号 | rexp,rexp | rexp | L-R | 是 |