1.操作符
1.1算术操作符
C提供了所有常用的算数操作符
+ - * / %
除了%操作符以外,其他都适用于整型和浮点型,当/操作符的两个操作数都是整数时,它执行整除运算,在其他情况下执行浮点数除法。
%操作符时取模操作符,接受两个整型操作数,把左操作数除以右操作数,返回值是余数。
1.2移位操作符
移位操作符只是简单的把一个值的位向左或者向右移动。
在左移过程中,最左边的几个值会被抛弃,右边的多出来的空位则由0补齐;
在右移过程中,当左边移入新位时,有两种选择:
逻辑移位,左边的由0补全
算数移位,左边移入的值由该数的正负决定,符号位为1,则移入的值全为1,符号位为0,则移入的值为0,这样能保持原数的正负不变。
算数左移和逻辑左移是相同的,只在右移的时候才会不同,而且只有这里写代码片
当操作数是负数的的时候才会不同。
左移位操作符为 << ,右移位操作符为 >>,
左操作数的值将会移动右操作数指定的位数,两个操作数必须是整数类型。
注:标准说明无符号值执行的所有移位操作都是逻辑移位,但是对于有符号值,到底采用逻辑移位还是算数移位取决于编译器。
**警告:****a << -5,类似这种操作是没有定义的,并不是左移-5即右移5位。
1.3位操作符
位操作符对他们的操作数的各个位执行AND, OR ,XOR等逻辑操作;
当两个位进行AND操作时,如果两个位都是1,结果为1,否则为0;
当两个位进行OR操作时,如果两个位有一个是1,结果为1,否则为0;
当两个位进行XOR操作时,如果两个位不同,结果为1,相同则为0。
位操作符有:
& | ^
它们分别执行AND,OR,XOR操作。它们的操作数是整型类型,每次对左右操作数的各一位进行操作。
一些简单的位的操纵:
//把指定位设置为1:
value = value | 1 << bit_number;
//把指定位清0
value = value & ~ 1 << bit_number;
1.4赋值
赋值操作符,用一个等号表示,赋值是表达式的一种,所以只要是允许表达式存在的地方都允许赋值
x = y + 3;
首先进行加发运算,=的操作数十表达式y+3的值,赋值操作符把右操作数的值存储于左操作数指定的位置
复合赋值符
上面所介绍的操作符都具有复合赋值符:
+= -= *= /= %=
<<= >>= &= ^= |=
例:
a += experssion;
这个操作,读作“把experssion加到a”,功能相当于下面的表达式:
a = a + (experssion);
不同的是+=操作符的左操作数(像a这样的)只求值一次。
注:+=操作符最重要的优点是它使代码更容易阅读和书写,尤其是相加的表达式很复杂的时候
1.5单目操作符
C语言中存在一些单目操作符,只接受一个操作数的操作符:
! ++ - & sizeof
~ – + * (类型)
!操作符对操作数执行逻辑反操作,如果操作数为真,则结果为假,让一个操作数为假,结果则为真
~操作符对整型类型的操作数进行求补操作。操作数所有原先为1的位变成0,所有为0的数变成1
-操作符产生操作数的负值
+操作符产生操作数的值(和-凑对用)
&操作符产生它的操作数的地址
**操作符是间接访问操作符,它的操作数是指针,用于访问指针所指向的值(解引用操作)
sizeof操作符判断它的操作数的类型长度,以字节为单位表示,操作数既可以是个表达式,也可以是两边加上括号的变量名
(类型)操作符被称为强制类型转换,可以把表达式的值转换为另外的类型,具有很高的优先级
++和–操作符:
前缀形式出现于操作数的前面,操作数的值被增加,而表达式的值就是操作数增加后的值
后缀形式出现的于操作数的后面,操作数的值仍然被增加,但是表达式的值仍是操作数增加前的值
注:前缀和后缀形式的增值操作符都复制一份变量值的拷贝
1.6关系操作符
这类操作符用于测试操作数之间的各种关系
>= < <= != ==
这类操作符所产生的结果都是一个整型值,如果两端的操作数符合操作符指定的关系,表达式的结果是1,否则结果是0.通常用于if或while语句中,作为测值表达式。
注:==操作符和=操作符经常容易被混淆,是新手极易犯的错误之一,偏偏编译器又检测不出错误,很容易浪费大量的调试时间
1.7逻辑操作符
逻辑操作符有&&和||,这两个操作符看上去像位操作符,但是却不是
&&操作符:
experssion1 && experssion2
如果过experssion1和experssion2的值都为真,那么表达式为真,如果两个表达式中任意一个为假,那么表达式为假。
这个操作符可以控制表达式求值的顺序,例:
a > 5 && a < 10;
&&操作符的优先级比<和>操作符优先级都要低,但是它仍然对两个关系表达式进行控制,原理如下:
&&操作符的左操作数进行求值如果它的值为真,然后在对右操作数进行求值,如果左操作数的值为假,那么右操作数便不再进行求值。||操作符也具有相同的特点。
这种行为成为“短路求值”。
1.8条件操作符
条件操作符接受三个操作数,它也会控制字表达式的求值顺序,用法如下:
experssion1 ? experssion2 : experssion3;
条件操作符的优先级非常低,所以各个操作数不加括号一般也不会有问题
首先计算experssion1,如果它的值为真(非0),那么整个表达式的值就是experssion2,experssion3不会求值,但是如果experssion1的值为假,那么表达式的值就是experssio3, experssion2不会求值。
1.9逗号操作符
逗号操作符用法如下:
experssion1, experssion2, experssion3,...,experssionN;
逗号操作符将两个或者多个表达式分隔开来,表达式自左向右逐个进行求值,整个逗号表达式的值就是最后那个表达式的值.
1.10下标引用,函数调用和结构成员
下标引用操作符接受两个操作符,一个数组名和一个索引值。
C语言的下标引用从0开始 ,并且不会对下标值进行有效性检查
array[下标];
*(array + 下标);
函数调用操作符接受一个或多个操作数,它的第一个操作数是调用的函数名,剩余的操作数就是传递给函数的参数。
.和->操作符用于访问一个结构的成员。
2.布尔值
C不具备显式的布尔类型,所以使用整数来代替,规则是:
零是假,任何非零值皆为真
3.左值和右值
左值就是能出现在赋值符号左边的东西,右值是可以出现在赋值符号右边的东西
4.表达式求值
4.1隐式类型转换
C的整型算数运算总是至少以缺省整型类型的精度来进行的,运算时,表达式中的字符型和短整型操作数在使用前被转换为普通整型,这种转换成为整型提升
4.2算数转换
如果某个操作符的各个操作数属于不同的类型,那么除非一个操作数转换为另一个操作数的类型,否则操作就无法进行,下面的层次体系称为寻常算数转换
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作符的类型在上面的列表中排名较低,那么它首先将转换为另一个操作数的类型然后执行操作。
4.3操作符的属性
操作符优先级表
4.4优先级和求值的顺序
C的每个操作符都具有优先级,但是有时候仅凭优先级还不能确定求值的顺序,规则如下:
两个相邻操作符的执行顺序由他们的优先级决定,如果他们的优先级相同,他们的执行顺序由他们的结合行决定,除此之外,编译器可以自由决定使用任何顺序对表达式进行求值米之遥它不违背逗号,&&,||和?:操作符所施加的限制.
一些警告
1.有符号值得右移位操作是不可移植的
2.移位操作的位数十个负值
3.连续赋值中各个变量的长度不一
4.误用=而不是==进行比较
5.误用|替代||,误用&替代&&
6.在不同的用于布尔值的非零值之间进行比较
7.表达式赋值的位置并不决定表达式计算的精度
8.编写结果依赖于求值顺序的表达式