4 表达式

4.1 算数运算符

一元运算符

运算符 说明 备注
+ 一元正号运算符 经典c中不存在这种运算符,它主要用于强调某数值常量是正数
- 一元负号号运算符

二元运算符

注意:

  1. %外,二元运算符允许操作数既可以是整数也可以是浮点数,或者是两者混合
  2. int类型的操作数和float型操作数混合在一起时,运算结果为float
  3. /%用于负操作数时,其结果与具体实现有关

由实现定义(implementtion-defined):软件在特定的平台上编译、链接和执行,行为可能会稍有差异。这是c语言为了和硬件平台匹配追求高效率的结果。

运算符 说明 备注
+ 加法运算符
- 减法运算符
* 乘法运算符
/ 除法运算符 当两个操作数都是整数时,运算结果会向下取整
% 取余运算符 操作数要求都是整数,否则无法编译通过

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

注意:很少有程序员会费心记住优先级和结合性规则,而是在需要时参考运算符表或添加足够多的()
运算符优先级:

优先级 运算符
最高优先级 +(负) -(正) * / %
最低优先级 +(加) -(减)

结合性:

  • 一元运算符都是右结合(right assoiative)的,即从右向左结合
1
-+i;//-(+i)
  • 二元运算符都是左结合(left assoiative)的,即从左向右结合
1
2
i-j-k;//(i-j)-k
+i+jk/;//(+i)+(j/k)

4.1.2 程序:计算通用产品代码的校验位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Compaer a Universal Prouct Code check digit
*/


# include <stdio.h>
int main(){
int d, i1, i2, i3, i4, i5, j1, j2, j3, j4, j5, first_sum, second_sum, total;
/*输入左边第一位数字*/
printf("Enter the first (single) digit:");
scanf("%d", &d);
/*输入2-6位数字*/
printf("Enter first groun of value digits:");
scanf("%1d%1d%1d%1d%1d", &i1, &i2, &i3, &i4, &i5);
/*输入7-11位数字*/
printf("Enter second group of five digits:");
scanf("%1d%1d%1d%1d%1d", &j1, &j2, &j3, &j4, &j5);
first_sum = d + i2 + i4 + j1 + j3 + j5;
second_sum = i1 + i3 + i5 + j2 + j4;
total = 3 * first_sum + second_sum;

printf("Check digit:%d\n", 9 - ((total - 1) % 10));

return 0;

}
1
2
3
4
5
$ ./upc 
Enter the first (single) digit:2
Enter first groun of value digits:23456
Enter second group of five digits:78954
Check digit:5

4.2 赋值运算符

注意:c语言的=不是语句,而是运算符。赋值表达式v=e的结果是v的值。

4.2.1 简单赋值

语法:v=ee可以是常量、变量或表达式。
副作用:简单赋值运算符是已知的第一个有副作用的运算符,因为它会改变操作数的值。

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

4.2.2 左值

定义:Q&A左值表示存储在计算机内存中的对象,而不是常量和计算结果。例如变量。
注意:赋值运算符要求左边的操作数必须是左值。

4.2.3 复合赋值

用途:简化利用变量原有值计算出新值,并重新赋值给这个变量的操作。
列举:共10种

+= -= *= /= %=

注意:

  1. v += e并不完全等同于v = v + e(优先级问题)

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

i+=1i-=1的区别

  1. ++--既可以是前缀运算符,也可以是后缀运算符
  2. 和赋值运算符一样,++--也有副作用:它会改变操作数的值

前缀使用和后缀使用的区别:

方式 语法 执行时机 优先级 备注
前缀方式 ++i--i 立即执行 一元正负号优先级相同,右结合方式
后缀方式 i++++i 稍后再执行 高于一元正负号,左结合方式 Q&A没有给出明确的时间,当会是下一条语句执行之前

4.4 表达式求值

4.4.1 部分c语言运算符表

Alt text

4.4.2 为表达式添加圆括号

过程:检查表达式,找到最高优先级的运算符后,用圆括号把运算符合相应的操作数括起来,圆括号的内容将被看成一个单独的操作数。然后重复此类操作指导表达式被完全加上圆括号。

1
2
3
4
5
6
7
8
9
s=>start: start
e=>end: end
op1=>operation: a = b += c++ - d + --e / -f
op2=>operation: a = b += (c++) - d + (--e) / (-f)
op3=>operation: a = b += (c++) - d + ((--e) / (-f))
op4=>operation: a = b += ((c++) - d + (((--e) / (-f))))
op5=>operation: (a = b += ((c++) - d + (((--e) / (-f)))))

s->op1->op2->op3->op4->op5->e

4.4.3 子表达式的求值顺序

注意:c语言并没有定义子表达式的求值顺序(&& || 条件运算符 逗号运算符除外)

1
2
a = 5;
c = (b = a + 2) - (a = 1)//c可能是6也可能是2

建议:不在表达式种使用赋值运算符,而是采用一串分离的赋值表达式。

4.5 表达式语句

表达式转换为语句:任何表达式都可以通过添加分号转换为语句。
注意:除非表达式有副作用,否则将表达式用作语句并没有任何意义。

1
2
3
4
int i = 1;
i --;//因为副作用的关系起到了作用

i * j - 1;//因为没有副作用,将该表达式作为语句使用没有任何意义