【《C和指针》阅读笔记】Chapter5 Operators and Expressions

本文详细介绍了C语言中的各种操作符,包括算数操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号操作符以及表达式求值的相关规则,深入理解这些操作符对于C语言编程至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

操作符

算数操作符

  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个因素决定的:操作符的优先级操作符的结合性以及操作符是否控制执行的顺序
  操作符优先级如下表所示:

<![if supportMisalignedColumns]> <![endif]>
操作符描述用法示例结果类型结合性是否控制求值顺序
( )聚组(表达式)与表达式同N/A
( )函数调用rexp(rexp,…,rexp)rexpL-R
[ ]下标引用rexp[rexp]lexpL-R
.访问结构成员lexp.member_namelexpL-R
->访问结构指针成员rexp->member_namelexpL-R
++后缀自增lexp++lexpL-R
--后缀自减lexp--rexpL-R
!逻辑反!rexprexpR-L
~按位取反~rexprexpR-L
+单目,表示正值+rexprexpR-L
-单目,表示负值-rexprexpR-L
++前缀自增++lexprexpR-L
--前缀自减--lexprexpR-L
*间接访问*rexplexpR-L
&取地址&lexprexpR-L
sizeof取其长度,以字节表示sizeof rexp / sizeof(类型)rexpR-L
(类型)类型转换(类型)rexprexpR-L
*乘法rexp * rexprexpL-R
/除法rexp / rexprexpL-R
%整数取余rexp % rexprexpL-R
+加法rexp + rexprexpL-R
-减法rexp - rexprexpL-R
<<左移位rexp << rexprexpL-R
>>右移位rexp >> rexprexpL-R
>大于rexp > rexprexpL-R
>=大于等于lexp >= rexprexpL-R
<小于rexp < rexprexpL-R
<=小于等于lexp <= rexprexpL-R
==等于rexp == rexprexpL-R
!=不等于lexp != rexprexpL-R
&位与rexp & rexprexpL-R
^位异或rexp ^ rexprexpL-R
|位或rexp | rexprexpL-R
&&逻辑与rexp && rexprexpL-R
||逻辑或rexp || rexprexpL-R
?:条件操作符rexp ?: rexprexpN/A
=赋值lexp = rexprexpR-L
+=以…加lexp += rexprexpR-L
-=以…减lexp -= rexprexpR-L
*=以…乘lexp *= rexprexpR-L
/=以…除lexp /= rexprexpR-L
%=以…取模lexp %= rexprexpR-L
<<=以…左移lexp <<= rexprexpR-L
>>=以…右移lexp >>= rexprexpR-L
&=以…与lexp &= rexprexpR-L
^=以…异或lexp ^= rexprexpR-L
|=以…或lexp |= rexprexpR-L
,逗号rexp , rexprexpL-R

优先级和求值的顺序

  如果表达式中的操作符超过一个,仅凭优先级还不能确定求值的顺序。下面是它的规则:

两个相邻操作符的执行顺序由它们的优先级决定。如果它们的优先级相同,它们的执行顺序由它们的结合性决定。除此之外,编译器可以自由决定使用任何顺序对表达式进行求值,只要它不违背逗号、&&、||和?:操作符所施加的限制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值