【C语言】什么是表达式?手把手教你表达式


前言

C语言的特点就是它更多地强调表达式而不是语句,表达式是表示如何计算值的公式。而最简单的表达式是变量常量。变量表示程序运行时需要计算的值,常量表示不变的值,更加复杂的表达式把运算符用于操作数(操作数自身就是表达式)。在表达式 a + (a * b) 中运算符 + 操作于操作数 a(a * b),而这两者自身又都是表达式。在这里插入图片描述

一、算数运算符

算数运算符是包括C语言在内的许多编程语言中都广泛应用的一种运算符,这类运算符可以执行加法,减法和除法。

一元运算符

只需要一个操作数的运算符叫做一元运算符" + " 称为一元正号运算符,而 " - " 称为一元负号运算符。例如:


i=+1;	//一元运算符 + 什么也不做。表示正 1
j=-1;	//一元运算符 - 什么也不做。表示负 1

一元运算符什么也不做,它主要用于强调某数值常量的正与负值。

二元运算符

需要两个操作数的运算符称为二元运算符" + " 加法运算符,“-” 减法运算符," * " 乘法运算符, " / " 除法运算符," % " 求余运算符。二元运算符或许看上去很熟悉,只有求余运算符 “ % ” 可能例外,i % j 的值是 i 除以 j 后的余数。例如,10 % 3 的值是1,而12 % 4 的值是0


#include<stdio.h>

int main(void){

	printf("10%3=%d\n",10%3);	
	printf("12%4=%d\n",12%4);

	return 0;
	

在这里插入图片描述
除了 % 运算符意外,其他二元运算符即允许操作数是整数也允许操作数是浮点数,两者混合也是可以的。当把 int 型操作数和 float 型操作数混合在一起时,运算结果是 float 型的。因此,9+2.5f 的值为11.5,而6.7f / 2的值为3.35.。
运算符 / 和运算符 % 要注意以下几点:

  1. 运算符 / 可能会产生意外的结果。比如,当两个操作数都是整数的时候,运算符 / 会丢掉分数部分来 “截取” 结果。因此下面,1 / 2 的结果是 0 不是 0.5
  2. 运算符 % 必须要求操作数是整数。如果两个操作数有一个不是整数,程序将无法编译通过。
  3. 把零做 /% 右操作数会导致未定义的行为。
  4. 当运算符 /% 用于负操作数时,其结果难以确定。C89 规定如果两个操作数中一个为负数时,那么除法的结果既可以向上取整也可以向下取整。(例如,-9 / 7 的结果既可以是 -1 也可以是 -2 。)在C89中,如果 i 或者 j 都是负数,i % j 的符号与具体实现有关。(例如,-9 % 7 的值可能是 -2 或者 5 。)但是C99中,除法结果是总是向零截取的(因此 -9 / 7 的结果是 -1 ),i % j 的值符号与 i 的相同(因此 -9 % 7 的值是 -2 )。

运算符的优先级和结合性

当表达式中包含多种运算符时,其含义可能不是一目了然的。例如,表达式 i + j * k“ i 加上 j ,然后结果再乘以 k” ,还是 “ j 乘以 k ,然后加上 i ” 呢?解决这个问题一种方法就是添加圆括号,写为 ( i + j ) * k 。C语言允许在所有表达式中用圆括号进行分组
可是在不使用圆括号的情况下,还是不知道编译器会把表达式 i + j * k 解释为 ( i + j ) * k 还是 i + ( j * k ) ? 和其他编程语言一样采用运算符优先级规则来解决这种隐含的二义性问题。

最高优先级+ - (一元运算符)
* / %(乘法,除法,取余)
最低优先级+ -(二元运算符)

当两个或更多个运算符出现在同一个表达式中时,可以通过运算符优先级从高到低的次序重复给子表达式添加圆括号来确定编译器解释表示式的方法。如下例子:
i + j * k 等价于 i + ( j * k )
-i * -j 等价于 ( -i ) * ( -j )
+i + j / k 等价于 ( +i) + ( j / k)
当表达式包含两个或更多个相同优先级的运算符时,仅有运算符优先规则是不够用的。这种情况下,运算符的结合性开始发挥作用。如果运算符是从左向右结合的,那么称这种运算符是左结合的。二元算数运算符(即 * ,%,/,和 +,-)都是左结合的,例如:
i - j - k 等价于 ( i - j ) - k
i * j / k 等价于 ( i * j ) / k
如果运算符是从右向左结合的,那么称这种运算符是右结合的。一元算数运算符 ( + 和 - ) 都是右结合的,所以
- + i 等价于 - ( + i)
在C语言中运算符的优先级结合性规则都是非常重要的应该多加留心。

二、赋值运算符

在求出表达式的值以后常常需要将其存储到变量中,以便将来使用。C语言中的 = ( 简单赋值 )运算符用于此目的。为了更新已经存储在变量中的值,C语言还提供了一种复合赋值运算符。

简单赋值

表达式 v = e 的赋值效果是求出表达式 e 的值,并把此值复制给 v 。如下面的例子所示,e 可以是常量,变量或更为复杂的表达式:


i = 5;	//把5赋给i
j = i;	//把i的值赋给j
k = 10 * i +j;	//把表达式 10*i*j 的计算结果赋给k

如果和 ve 的类型不同,那么赋值运算发生时会把 e 的值转换为 v 的类型:


int i;
float f;

i = 72.99f;
f = 136; 

在这里插入图片描述
既然赋值运算符,那么多 个赋值串联在一起:


i = j = k = 0

赋值运算符 = 是右结合的,所以上述赋值表达式等价于


i = (j = (k = 0));

作用是先把 0 赋值给k,再赋值给j,最后再赋值给i
注意由于存在类型转换,串联在一起的赋值运算的最终结果可能不是预期的结果:


int i;
float f;

f = i = 72.99f;	

在这里插入图片描述
首先把数值72赋值给变量i,然后把72.0(而不是预期的72.99)赋值给变量f
通常情况下,可以使用 v 类型值的地方都可以进行形如 v=e 的赋值。在下面的例子中,表达式 j=ii 的值复制给 j,然后 j 的新值加上 1,得到 k 的新值:


i = 1;
k = 1 + ( j = i );
printf("%d %d %d\n",i, j, k);

在这里插入图片描述
按照这种形式使用赋值运算符通常不是一个好主意,“嵌入式赋值”不便于程序的阅读。

左值

大多数C语言运算符允许他们的操作数是变量,常量或者包含其他运算符的表达式。然而,赋值运算符要求它的左操作数必须是左值。左值表示存储在计算机内存中的对象,而不是常量或计算的结果。变量是左值,而诸如 102 * 1 这样的表达式则不是左值。目前为止,变量是已知的唯一左值
既然赋值运算符要求左操作是左值,那么在赋值表达式的左侧放置任何其他类型的表达式都是不合法的:


12 = i;		//错误
i + j =0;	//错误
-i = j;		//错误

复合赋值

利用变量的原有值计算出新值并重新赋值给这个变量在C语言程序中是非常普遍的。例如,下面这条语句就是把变量 i 的值加上 2 后再赋值给它自己:


i = i + 2;

C语言的复合赋值运算符允许缩短这个语句以及类似的语句。使用 += 运算符,可以将上面的表达式简写为:


i += 2;		//和 i= i + 2; 是同一个效果

+= 运算符把右操作数的值加到左侧的变量中去。
另外还有9种复合赋值运算符,包括 -==/=* ,%= ,所有的复合赋值运算符的工作原理大体相同。

  1. v += e 表示 v 加上 e ,然后将结果存储到 v‘中。
  2. v -= e 表示 v 减去 e ,然后将结果存储到 v 中。
  3. v * = e 表示 v 乘以 e ,然后将结果存储到 v 中。
  4. v /= e 表示 v除以 e ,然后将结果存储到 v 中。
  5. v %= e 表示 v 除以 e 取余数,然后i将结果存储到 v 中。

注意事项在使用复合赋值运算符时,注意不要交换组成运算符的两个字符的位置。交换字符位置产生的表达也可以被编译器接受,例如把表达式 i += j 但写成 i =+ j,程序也能够编译,但最后的表达式i =+ j等价于表达式 i = ( +j),作用只是其简单地把 j 的值赋给 i
复合赋值运算符有着和 = 运算符一样的特性。特别是,它们都是右结合的,所以语句

i += j += k;

意味着

i += (j += k);

自增运算符和自减运算符

最常用于变量的两种运算符是 “自增” (加1)和 “自减” (减1)。也可以用以下方式完成这类操作:

i = i + 1;
j = j - 1;

复合赋值运算符可以将上述这些语句缩短一些:

i += 1;
j -= 1;

C语言允许用 + +自增)和 - -自减)运算符将这些语句缩短的更短一些。
++ 表示操作数加1,而 - - 表示操作数减1 。看似很简单实则是个误导,实际上自增和自减运算符的使用很复杂的。复杂的原因之一就是,++- - 运算符既可以作为前缀运算符(如 ++ i 和 - - i)使用也可以做为后缀运算符(如 i ++ 和 i - -)使用。
复杂的另一个原因是,和赋值运算符一样,++- - 也有副作用:它们会改变操作数的值,计算表达式 i++”后缀自增“)的结果是 i + 1,而副作用的效果是自增 i

int i=1;
printf("i is %d\n",++i);	//  前缀++ 先进行++操作,然后将值赋给该变量
printf("i is %d\n,i);	// i的值为2

在这里插入图片描述
计算表达式 i++“后缀自增”)的结果是 i,但是会引发 i 随后进行自增:

int i=1;
printf("i is %d\n",i++);	//后缀++ 先使用变量原始值,然后进行自增操作
printf("i is %d\n,i);

在这里插入图片描述

第一个 printf 函数显示 i 自增前的原始值,第二个 printf 函数显示了 i 变化后的新值。正如这些例子说明的那样,i++ 意味着 “立即自增 i ”,而 i++ 则意味着 “现在先用 i 的原始值,稍后再自增 i ”。C语言标准没有给出精准的时间,但是可以放心地假设 i 将在下一条语句执行前进行自增。

- - 运算符算符也具有相似的特性:

int i=1;
printf("i is %d\n",--i);	// 前缀--  先进行自减操作然后将自减后的值赋给变量i
printf("i is %d\n",i);

在这里插入图片描述
后缀 “- -” 操作:

int i=1;
printf("i is %d\n",i--);
printf("i is %d\n,i);

在这里插入图片描述
在同一个表达式中多次使用 ++- - 运算符,结果往往会很难理解。思考下列语句:

int i=1;
int j=2;
K = ++i + j++;

在上述语句执行后,ijk 的值分别是多少呢?由于 i 是在值被使用前进行自增,而 j 是在值被使用后进行自增,所以在最后一个语句等价于:

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

所以最终 ijk 的值分别是234 。例如执行下列语句:

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

i ,jk 的值将分别是 233
需要记住的是,后缀 “++” 和后缀 “- -” 比一元的正好,负号优先级高,而且都是左结合的。前缀 ++ 和前缀 - - 与一元的正号,负号优先级相同,而且都是右结合。

表达式求值

下面这个表格的第一行显示了每种运算符相对于表中其他运算符的优先级(最高优先级为1,最低 优先级为5),最后一列显示了每种运算符的结合性。

优先级类型名称符号结合性
1(后缀)自增++左结合
(后缀)自减- -
2(前缀)自增++右结合
(前缀)自减- -
一元正号+
一元负号-
3乘法类* , %, /左结合
4加法类+, -左结合
5赋值= ,+=, -= ,*= ,/= ,%=右结合

假设读某人的程序时遇到类似这样的表达式:

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

如果有圆括号显示表达式是如何由子表达式构成的,那么这个复杂的表达式将比较容易理解。借助上述表格,为表达式添加括号是非常容易的:检查表达式,找到最高优先级的运算符后,用圆括号把运算符和相应的操作数括起来,这表明在此之后圆括号内的内容将被看成是一个单独的操作数。然后重复此类操作直到将表达式完全加上圆括号。
在此例中,用作后缀运算符的 ++ 具有最高优先级,所以在后缀 ++ 和相关操作数的周围加上圆括号:

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

注意,另一个负号的左侧紧挨着一个操作数,所以这个运算符一定是减法运算符,而不是一元负号运算符。
接下来,注意到运算符 / (优先级为3):

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

这个表达式包含两个优先级为 4 的运算符:减号和加号。当两个具有相同优先级的运算符和同一个操作数相邻时,需要注意结合性。在此例中,- 运算符和 + 运算符都和d相邻,所以应用结合性规则。- 运算符和 + 运算符都是自左向右结合,所以圆括号先括减号,然后再括加号:

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

最后剩下运算符 = 和运算符 += 。这两个运算符都和 b 相连,所以必须考虑结合性。赋值运算符从右向左结合。所以括号先加在表达式 += 周围,然后加在表达式 = 周围:

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

现在这个表达式完全加上了括号。

总结

运算符相对来说比较好理解,但是也有很多要注意的地方比如前缀和后缀之分。要是我们详细掌握该知识点使用运算符就不会出现任何问题,我也在努力的学习着这些知识点。希望大家也提出我的问题或者希望能给点学习上的帮助。谢谢!!在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值