文章目录
操作符
算数操作符
C提供了所有常用的算数操作符:
+ - * / %
- 除了%操作符,其余几个操作符都是既适用于浮点类型又适用于整数类型。
- 当/操作符的两个操作数都是整数时,它执行整除运算,在其他情况下则执行浮点数除法。
- %为取模操作符,它接受两个整型操作数,把左操作数除以右操作数,但它返回的值时余数而不是商。
移位操作符
移位操作知识简单地把一个值的位向左或向右移动。
- 在左移位中,值最左边的几位被丢弃,右边多出来的几个空位则由0补齐。
- 右移位操作存在一个左移位操作不曾面临的问题:从左边移入新位时,可以选择两种方案。一种是逻辑移位,左边移入的位用0填充;另一种是算数移位,左边移入的位由原先该值的符号位决定,符号位为1则移入的位均为1,符号位为0则移入的位均为0,这样能够保持原数的正负形式不变。
- 算数左移和逻辑左移是相同的,它们只在右移时不同,而且只有当操作数是负值时才不一样。
- 左移位操作符为
<<
,右移位操作符为>>
。
位操作符
位操作符对它们的操作数的各个位执行AND、OR和XOR(异或)等逻辑操作。
位操作符有:
& | ^
- 它们分别执行AND、OR和XOR操作。它们要求操作数为整数类型,它们对操作数对应的位进行指定的操作,每次对左右操作数的各一位进行操作。
赋值
赋值用一个等号表示。
- 赋值操作符的结合性(求值的顺序)是从右到左,所以下面这三个表达式是等价的:
a = x = y + 3;
a = ( x = y + 3);
x = y + 3; a = x;
复合赋值符
到目前为止所介绍的操作符都还有一种复合赋值的形式:
+= -= *= /= %=
<<= >>= &= ^= |=
以+=
操作符为例,其用法如下:
a += expression
它的功能相当于下面的表达式:
a = a +expression
单目操作符
C具有一些单目操作符,也就是只接受一个操作数的操作符。它们是
! ++ - & sizeof
~ -- + * (类型)
- !操作符对它的操作数执行逻辑反操作。
- ~操作符对整型类型的操作数进行求补操作,操作数中所有原先为1的位变为0,所有原先为0的为变为1。
- -操作符产生操作数的负值。
- +操作符产生操作数的值:换句话说,它什么也不干。
- &操作符产生它的操作数的地址。
- ==*操作符是间接访问操作符,它与指针一起使用,用于访问指针所指向的值。==如:
int a, *b
...
b = &a;
这个例子中,表达式b的值时变量a的地址,但表达式*b的值则是变量a的值。
- sizeof操作符判断它的操作数的字节数,其结果自然取决于你所使用的环境。
- (类型)操作符被称为强制类型转换(cast),它用于显式地把表达式的值转换为另外的类型。
- 增值操作符++和减值操作符–如下例所示:
int a, b, c, d;
...
a = b = 10; //a和b得到值10
c = ++a; //a增加至11,c得到的值为11
d = b++; //b增加至11,但d得到的值仍为10
关系操作符
这类操作符用于测试操作数之间的各种关系。这些操作符是:
> >= < <= != ==
- !=用于测试“不相等”。
- ==操作符用于测试“相等”。
关系操作符的结果是整型值,所以它可以赋值给整型变量,但通常它们用于if或while语句中,作为测值表达式。
逻辑操作符
逻辑操作符有&&和||,它们用于对表达式求值,测试它们的值是真还是假。
先来看一下&&操作符:
expression1 && expression2
- &&会控制子表达式求值的顺序。例如,下面这个表达式:
( a > 5 ) && ( b < 10 )
- 尽管&&操作符的优先级较低,但它仍然会对两个关系表达式施加控制。下面是它的工作原理:&&操作符的左操作数总是首先进行求值,如果它的值为真,然后就紧接着对右操作数进行求值。如果左操作数的值为假,那么右操作数便不再进行求值,因为整个表达式的值肯定是假的,右操作数的值已无关紧要。||操作符也类似。
条件操作符
条件操作符接受三个操作数。它也会控制子表达式的求值顺序。下面是它的用法:
expression1 ? expression2 : expression3
- 条件操作符的优先级非常低,所以它的各个操作数即使不加括号,一般也不会有问题。
- 首先计算的是expression1,如果它的值为真(非零值),那么整个表达式的值就是expression2的值,expression3不会进行求值。但是,如果expression1的值是假(零值),那么整个条件语句的值就是expression3的值,expression2不会进行求值。
例如:
a > 5 ? b - 6 : c / 2;
- 上例可解读为:a是不是大于5?如果是,就执行b-6,否则执行c/2。
适用场合,如:
if( a > 5 )
b[ 2 *c + d( e / 5 ) ] = 3;
else
b[ 2 *c + d( e / 5 ) ] = -20;
上例可改写成:
b[ 2 *c + d( e / 5 ) ] = a > 5 ? 3 : -20;
逗号操作符
逗号操作符的用法如下:
expression1, expression2, ... , expressionN
逗号操作符将两个或多个表达式分隔开来。这些表达式自左向右逐个进行求值,整个逗号表达式的值就是最后那个表达式的值。
例如:
a = get_value();
count_value(a);
while( a > 0){
...
a = get_value();
count_value(a);
}
- 在这个while循环的前面,有两条独立的语句,它们用于获得在循环表达式中进行测试的值。这样,在循环开始之前和循环体的最后必须各有一份这两条语句的拷贝。但是,如果使用逗号操作符,你可以把这个循环改写为:
while( a = get_value(), count_value(a), a > 0 ){
...
}
- 也可以使用内嵌的赋值形式,如下所示:
while( count_value( a = get_value() ), a > 0 ){
...
}
下标引用、函数调用和结构成员
下标引用操作符是一对方括号。下标引用操作符接受两个操作数:一个数组名和一个索引值。
C的下标值总是从零开始,并且不会对下标值进行有效性检查。
除了优先级不同之外,下标引用操作和间接访问表达式是等价的。这里是它们的映像关系:
array[ 下标 ]
*( array + ( 下标 ) )
函数调用操作符接受一个或多个操作数。它的第一个操作数是你希望调用的函数名,剩余的操作数就是传递给函数的参数。
.和->操作符用于访问一个结构的成员。如果s是个结构变量,那么s.a就访问s中名叫a的成员。当你拥有一个指向结构的指针而不是结构本身,且欲访问它的成员时,就需要使用->操作符而不是.操作符。
布尔值
C不具备显式的布尔类型,所以使用整数来代替。其规则是:
零是假,任何非零值皆为真。
左值和右值
左值就是那些能够出现在赋值符号左边的东西。右值就是那些可以出现在赋值符号右边的东西。 这里有个例子:
a = b + 25;
上例中左值和右值不能互换,因为b + 25未标识一个特定的位置,这样的赋值语句是非法的。
这里有另外一个例子:
int a, *pi;
...
pi = &a;
*pi = 20;
- 指针pi的值是内存中某个特定位置的地址,*操作符使及其指向那个位置。当它作为左值使用时,这个表达式指定需要进行修改的位置。当它作为右值使用时,它就提取当前存储于这个位置的值。
表达式求值
隐式类型转换
C的整型算数总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符型和短整型操作数在使用之前被转换为普通整型,这种转换成为*整型提升(integral Prootion)**。 例如,在下面表达式的求值中:
char a, b, c;
...
a = b + c;
- b和c的值被提升为普通整型,然后再执行加法运算。加法运算的结果将被截短,然后再存储于a中。
算数转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另外一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换(usual arithmetic conversion)。
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名较低,那么它首先将转换为另外一个操作数的类型然后执行操作。
操作符的属性
复杂表达式的求值顺序是由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++ | lexp | 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 | lexp | 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 | 否 |
>= | 大于等于 | lexp >= rexp | rexp | L-R | 否 |
< | 小于 | rexp < rexp | rexp | L-R | 否 |
<= | 小于等于 | lexp <= rexp | rexp | L-R | 否 |
== | 等于 | rexp == rexp | rexp | L-R | 否 |
!= | 不等于 | lexp != 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 | N/A | 是 |
= | 赋值 | 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 | 否 |
<<= | 以…左移 | 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 | 是 |
优先级和求值的顺序
如果表达式中的操作符超过一个,仅凭优先级还不能确定求值的顺序。下面是它的规则:
两个相邻操作符的执行顺序由它们的优先级决定。如果它们的优先级相同,它们的执行顺序由它们的结合性决定。除此之外,编译器可以自由决定使用任何顺序对表达式进行求值,只要它不违背逗号、&&、||和?:操作符所施加的限制。