C语言表达式

目录

4.1 算术运算符

4.1.1 运算符的优先级和结合性

4.1.2 运算符的优先级与结合性顺序表

4.2 赋值运算符

4.2.1 简单赋值

4.2.2 左值

4.2.3 复合赋值

4.3 自增运算符和自减运算符

4.4 表达式求值

4.4.1 子表达式的求值顺序

4.4.2 未定义的行为(小拓展)

4.5 表达式语句


4.1 算术运算符

一元运算符(单操作数):+、-

二元运算符(双操作数):+、-、*、/、%

注意事项:

1、操作数即运算符两端数值的个数

2、一元运算符+什么都不做,主要用于强调某数值常量是正的

3、除法运算符“/”的得到的结果是商,求余运算符“%”的得到的结果是余数

4、当两个操作数都是整数时,/运算符会丢掉分数部分(1/2的结果是0而非0.5)

5、%运算符的两个操作数需要是整数,否则程序无法编译通过

6、0不能作为/或%运算符的右操作数

7、除%以外,二元运算符即允许操作数是整数也允许操作数是浮点数,也可以是两者的混合,当int型操作数和float型操作数混合在一起时,运算结果是float型

        当运算符/和运算符%用于负操作数时,其结果难以确定。 根据 C89 标准 ,如果两个操作数中有一个为负数,那么除法和取余的结果既可以向上舍入也可以向下舍入 ( -9/7 的结果既可以是-1 也可以是-2, -9%7 的值可能是-2 或者 5) 但是 在 C99 标准中 ,除法的结果总是 趋零截尾 (-9/7 的结果是-1), i%j 的值的符号与 i 的相同 (-9%7 的值是-2)

4.1.1 运算符的优先级和结合性

        C 语言采用运算符优先级规则来解决在运算过程中可能出现的二义性问题 (编译器是把表达式 i+j*k 解释为(i+j)*k 还是 i+(j*k))

        当两个或更多个运算符出现在同一个表达式中时,可以按运算符优先级从高到低的次序重

复给子表达式添加圆括号,由此来确定编译器解释表达式的方法,即i + j * k  =>  i + (j * k)
        当表达式包含两个或更多个相同优先级的运算符时,仅有运算符优先级规则是不够的。此
时,运算符的结合性规则开始发挥作用。若运算符是从左向右结合的,则称这种运算符是左结合
的。二元算术运算符(*、/、%、+和-)都是左结合的,即i - j - k =>  (i – j ) – k

4.1.2 运算符的优先级与结合性顺序表

优先级

运算符

名称或含义

使用形式

结合方向

说明

1

[]

数组下标

数组名[常量表达式]

左到右

()

圆括号

(表达式)
函数名(形参表)

.

成员选择(对象)

对象.成员名

->

成员选择(指针)

对象指针->成员名

2

-

负号运算符

-表达式

右到左

单目运算符

(类型)

强制类型转换

(数据类型)表达式

++

自增运算符

++变量名
变量名++

单目运算符

--

自减运算符

--变量名
变量名--

单目运算符

*

取值运算符

*指针变量

单目运算符

&

取地址运算符

&变量名

单目运算符

!

逻辑非运算符

!表达式

单目运算符

~

按位取反运算符

~表达式

单目运算符

sizeof

长度运算符

sizeof(表达式)

3

/

表达式 / 表达式

左到右

双目运算符

*

表达式*表达式

双目运算符

%

余数(取模)

整型表达式%整型表达式

双目运算符

4

+

表达式+表达式

左到右

双目运算符

-

表达式-表达式

双目运算符

5

<<

左移

变量<<表达式

左到右

双目运算符

>>

右移

变量>>表达式

双目运算符

6

>

大于

表达式>表达式

左到右

双目运算符

>=

大于等于

表达式>=表达式

双目运算符

<

小于

表达式<表达式

双目运算符

<=

小于等于

表达式<=表达式

双目运算符

7

==

等于

表达式==表达式

左到右

双目运算符

!=

不等于

表达式!= 表达式

双目运算符

8

&

按位与

表达式&表达式

左到右

双目运算符

9

^

按位异或

表达式^表达式

左到右

双目运算符

10

|

按位或

表达式|表达式

左到右

双目运算符

11

&&

逻辑与

表达式&&表达式

左到右

双目运算符

12

||

逻辑或

表达式||表达式

左到右

双目运算符

13

?:

条件运算符

表达式1? 表达式2: 表达式3

右到左

三目运算符

14

=

赋值运算符

变量=表达式

右到左

/=

除后赋值

变量/=表达式

*=

乘后赋值

变量*=表达式

%=

取模后赋值

变量%=表达式

+=

加后赋值

变量+=表达式

-=

减后赋值

变量-=表达式

<<=

左移后赋值

变量<<=表达式

>>=

右移后赋值

变量>>=表达式

&=

按位与后赋值

变量&=表达式

^=

按位异或后赋值

变量^=表达式

|=

按位或后赋值

变量|=表达式

15

,

逗号运算符

表达式,表达式,…

左到右

 

4.2 赋值运算符

4.2.1 简单赋值

        表达式 v = e 的赋值效果是求出表达式 e 的值,并把此值复制到 ve 可以是常量、变量或更
为复杂的表达式:
i = 5; /* i is now 5 */
j = i; /* j is now 5 */
k = 10 * i + j; /* k is now 55 */
         如果 v e 的类型不同,那么赋值运算发生时会把 e 的值转换为 v 的类型:
int i; 
float f; 
i = 72.99f; /* i is now 72 */ 
f = 136; /* f is now 136.0 */

        副作用:通常我们不希望运算符修改它们的操作数,简单赋值运算符是一个有副作用的运算符,它改变了运算符的左操作数(表达式 i = 0 求值产生的结果为 0,并(作为副作用)把 0 赋值给 i)

注意事项:由于存在类型转换,串在一起的赋值运算的结果可能不是预期的结果(int i; float r; r = i = 33.3f,首先把数值33赋值给变量i,然后把33.0而不是预期的33.3赋值给变量f

4.2.2 左值

        大多数 C 语言运算符允许它们的操作数是变量、常量或者包含其他运算符的表达式。然而,
赋值运算符要求它的左操作数必须是左值。左值表示对象,而不是常量或计算的结果。变量是左
值的一种(10 或 2 * i 这样的表达式不是左值) 既然赋值运算符要求左操作数是左值,那么在赋值
表达式的左侧放置任何其他类型的表达式都是不合法的:
12 = i; /*** WRONG ***/ 
i + j = 0; /*** WRONG ***/ 
-i = j; /*** WRONG ***/

        编译器会检测出这种错误,并给出 “invalid lvalue in assignment”(赋值中的左值无效/表达式必须是可修改的左值)这样的出错消息

4.2.3 复合赋值

        C 语言的复合赋值运算符允许将类似于i = i + 2的语句缩短为i+=2的形式,符合赋值运算符包
括: -=、*、/=、%=

4.3 自增运算符和自减运算符

自增运算符:++

自减运算符:--

自增和自减运算符既可为前缀运算符(++i 和--i)也可为后缀运算符( i++和 i--)

前缀自增/减:先加再用

后缀自增/减:先用再加

i = 1; 
j = 2; 
k = i++ + j++;

i = 2、j = 3、k = 4

注意事项:后缀++和后缀--比一元的正号和负号优先级高,且均为左结合。前缀++和前缀--与一元

的正号和负号优先级相同,且均为右结合。

4.4 表达式求值

有圆括号显示表达式是如何由子表达式构成的,那么这个复杂的表达式将较容易理解

对于复杂表达式a = b += c++ - d + --e / -f 的求值过程分析:

1、后缀++具有最高优先级,因此在后缀++和相关操作数的周围加上圆括号

a = b += (c++) - d + --e / -f

2、前缀--和一元负号运算符的优先级都为2,因此为这两个运算符加上括号

a = b += (c++) - d + (--e) / (-f)

3、 另一个负号的左侧紧挨一个操作数,因此它一定是减法运算符,而不是一元负号运算符

4、/运算符的优先级为3,故将/两侧的操作数用括号包裹

a = b += (c++) - d + ((--e) / (-f))

5、+和-为两个优先级为4的运算符,当两个具有相同优先级的运算符和同一操作数相邻时,需要注

意运算的结合性,-和+运算符均为自左向右结合,因此先括减号再括加号

a = b += (((c++) – d) + ((--e) / (-f)))

6、最后剩下的=和+=运算符,这两个运算符与b相邻,因此必须考虑运算结合性,赋值运算符从右向左结合,因此括号先加在表达式+=周围,然后加再=周围

 (a = (b += (((c++) – d) + ((--e) / (-f)))))

4.4.1 子表达式的求值顺序

        有了运算符的优先级和结合性规则,我们就可以将任何 C 语言表达式划分成子表达式。但是
这些规则并不总是允许我们确定表达式的值,表达式的值可能依赖于子表达式的求值顺序。
        
         C 语言没有定义子表达式的求值顺序 (除了含有逻辑与运算符及逻辑或运算符、 条件运算符
以及逗号运算符的子表达式) 因此,在表达式(a + b) * (c - d)中,无法确定子表达式(a + b)是否在
子表达式(c – d)之前求值。
        不管子表达式的计算顺序如何,大多数表达式有相同的值。但是, 当子表达式改变了某个操
作数的值时,产生的值可能就不一致了:
a = 5; //子表达式的值
c = (b = a + 2) – (a = 1);
        第二条语句的执行结果是未定义的,C 标准没有规定。对大多数编译器而言,c 的值是
6 或者 2。如果先计算子表达式(b = a + 2),那么 b 的值为 7,c 的值为 6。但是,如果先计
子表达式(a = 1),那么 b 的值为 3,c 的值为 2
        除了赋值运算符,仅有自增和自减运算符可以改变操作数。使用它们时,要注意表 达式不要
依赖特定的计算顺序
i = 2; 
j = i * i++;
        人们很自然地就会认定 j 赋值为 4。但是,该语句的执行效果是未定义的,j 也可能赋值
为 6。该情况的执行步骤如下:
(1) 取出第二个操作数(i 的原始值),然后 i 自增
(2) 取出第一个操作数(i 的新值)
(3) i 的原始值和新值相乘,结果为 6
        “取出”变量意味着从内存中获取它的值。变量的后续变化不会影响已取出的值,因为已
取出的值通常存储在 CPU 中,称为寄存 器的一个特殊位置。

4.4.2 未定义的行为(小拓展)

        根据 C 标准,类似 c = (b = a + 2) – (a = 1);和 j = i * i++;这样的语句会导致“未定义的
为”,这跟由实现定义的行为是不同的。当程序中出现未定义的行为时,后果是不可预料
的。不同的编译器给出的编译结果可能是不同的,但这还不是唯一可能发生的事情:首先程
序可能无法通过编译,就算通过了编译也可能无法运行,就算可以运行也有可能崩溃、不稳
定或者产生无意义的结果。

4.5 表达式语句

        C 语言有一条不同寻常的规则:任何表达式都可以用作语句。即不论表达 式是什么类型、计
算什么结果,均可通过在后面添加分号将其转换成语句。
表达式++i 

语句++i;

        执行这条语句时,i 先进行自增,然后把新产生的 i 值取出(与放在表达式中的效果一样)

是,因为++i 不是更长的表达式的一部分,所以它的值会被丢弃,执行下一条语句(当然,对 i 的

改变是持久的)

~over~

  • 14
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值