运算符是一种特殊的符号,用以用于数据的运算、赋值和比较等。
表达式指的是一组运算数、运算符的组合,表达式一定具有值,一个变量或一个常量就是一个表达式,变量、常量与运算符也可以组成复杂一些的表达式。
一:运算符分类
(1)按照操作数个数分类:
- 一元运算符(一目运算符)
- 二元运算符(二目运算符)
- 三元运算符(三目运算符)
(2) 按照功能分类:
- 算术运算符
- 赋值运算符
- 关系运算符
- 逻辑运算符
- 位运算符
二:算术运算符
算术运算符是对数值类型的变量进行运算的,在C程序中使用的非常多。
运算符 | 描述 | 操作数个数 | 组成的表达式的值 | 副作用 |
+ | 正号 | 1 | 操作数本身 | 无 |
- | 负号 | 1 | 操作数符号取反 | 无 |
+ | 加号 | 2 | 两个操作数之和 | 无 |
- | 减号 | 2 | 两个操作数之差 | 无 |
* | 乘号 | 2 | 两个操作数之积 | 无 |
/ | 除号 | 2 | 两个操作数之商 | 无 |
% | 取模(取余) | 2 | 两个操作数相除的余数 | 无 |
++ | 自增 | 1 | 操作数自增前或自增后的值 | 有 |
- - | 自减 | 1 | 操作数自减前或自减后的值 | 有 |
//正号和负号
#include <stdio.h>
int main()
{
int x = 12;
int x1 = -x, x2 = +x;
int y = -67;
int y1 = -y, y2 = +y;
printf("x1=%d, x2=%d \n", x1, x2);
printf("y1=%d, y2=%d \n", y1, y2);
return 0;
}
//加、减、乘、除
#include <stdio.h>
int main()
{
int a = 5 + 2.5;
printf("%d\n", a * a);
double b = 6 / 4;
printf("%f\n", b); // 输出 1.000000
double c = 6.0 / 4;
printf("%f\n", c); // 输出 1.500000
return 0;
}
注意:整数之间做除法时,结果只保留整数部分而舍弃小数部分。
//取模(取余)
#include <stdio.h>
int main()
{
int res1 = 10 % 3;
printf("%d\n", res1);
int res2 = -10 % 3;
printf("%d\n", res2);
int res3 = 10 % -3;
printf("%d\n", res3);
int res4 = -10 % -3;
printf("%d\n", res4);
return 0;
}
注意:运算结果的符号与被模数也就是第一个操作数相同。
自增和自减
- 自增、自减运算符可以写在操作数的前面也可以写在操作数后面,不论前面还是后面,对操作数的副作用是一致的。
- 自增、自减运算符在前在后,对于表达式的值是不同的。 如果运算符在前,表达式的值是操作数自增、自减之后的值;如果运算符在后,表达式的值是操作数自增、自减之前的值。
//自增和自减
#include <stdio.h>
int main()
{
int i1 = 10, i2 = 20;
int i = i1++;
printf("%d\n", i); // 10
printf("%d\n", i1); // 11
i = ++i1;
printf("%d\n", i); // 12
printf("%d\n", i1); // 12
i = i2--;
printf("%d\n", i); // 20
printf("%d\n", i2); // 19
i = --i2;
printf("%d\n", i); // 18
printf("%d\n", i2); // 18
return 0;
}
三:关系运算符(比较运算符)
运算符 | 描述 | 操作数个数 | 表达式的值 | 副作用 |
== | 相等 | 2 | 0 或 1 | 无 |
!= | 不等 | 2 | 0 或 1 | 无 |
< | 小于 | 2 | 0 或 1 | 无 |
> | 大于 | 2 | 0 或 1 | 无 |
<= | 小于等于 | 2 | 0 或 1 | 无 |
>= | 大于等于 | 2 | 0 或 1 | 无 |
#include <stdio.h>
int main()
{
int a = 8;
int b = 7;
printf("a>b的值:%d \n", a > b);
printf("a>=b的值:%d \n", a >= b);
printf("a<b的值:%d \n", a < b);
printf("a<=b的值:%d \n", a <= b);
printf("a==b的值:%d \n", a == b);
printf("a!=b的值:%d \n", a != b);
return 0;;
}
四:逻辑运算符
运算符 | 描述 | 操作数个数 | 表达式的值 | 副作用 |
&& | 逻辑与 | 2 | 0 或 1 | 无 |
|| | 逻辑或 | 2 | 0 或 1 | 无 |
! | 逻辑非 | 1 | 0 或 1 | 无 |
(1):逻辑与 &&
如果两个操作数都为真(非零),那么表达式的值为真,否则为假。
如果第一个操作数为假,第二个操作数没有计算的必要了,这种现象称为短路现象。
#include <stdio.h>
int main()
{
double score = 70; // 成绩
if (score >= 60 && score <= 80)
{
printf("ok1 \n");
}
else
{
printf("ok2 \n");
}
int a = 10, b = 99;
// 短路现象
if (a < 2 && ++b > 99)
{
printf("ok100");
}
printf("b=%d\n", b);
return 0;
}
(2):逻辑或 ||
只要有一个操作数为真,表达式的值就为真;两个操作数都为假,表达式的值为假。
如果第一个操作数为真,第二个操作数没有计算的必要了,这种现象称为短路现象。
#include <stdio.h>
int main()
{
double score = 70;
if (score >= 70 || score <= 80)
{
printf("ok1 \n");
}
else
{
printf("ok2 \n");
}
int a = 10, b = 99;
// 短路现象
if (a > 5 || b++ > 100)
{ //
printf("ok100 \n");
}
printf("b=%d\n", b);
return 0;
}
(3):逻辑非
操作数状态取反作为表达式的值。
#include <stdio.h>
int main()
{
int score = 100;
int res = score > 99;
if (res)
{
printf("hello, tom \n");
}
if (!res)
{
printf("hello,jack \n");
}
return 0;
}
五:赋值运算符
运算符 | 描述 | 操作数个数 | 表达式的值 | 副作用 |
= | 赋值 | 2 | 左边操作数的值 | 有 |
+= | 相加赋值 | 2 | 左边操作数的值 | 有 |
-= | 相减赋值 | 2 | 左边操作数的值 | 有 |
*= | 相乘赋值 | 2 | 左边操作数的值 | 有 |
/= | 相除赋值 | 2 | 左边操作数的值 | 有 |
%= | 取余赋值 | 2 | 左边操作数的值 | 有 |
<<= | 左移赋值 | 2 | 左边操作数的值 | 有 |
>>= | 右移赋值 | 2 | 左边操作数的值 | 有 |
&= | 按位与赋值 | 2 | 左边操作数的值 | 有 |
^= | 按位异或赋值 | 2 | 左边操作数的值 | 有 |
|= | 按位或赋值 | 2 | 左边操作数的值 | 有 |
- 赋值运算符的第一个操作数(左值)必须是变量的形式,第二个操作数可以是任何形式的表达式。
- 赋值运算符的副作用针对第一个操作数。
#include <stdio.h>
int main()
{
int a = 10, b = 20, c = 30;
c += 3; // 等价于 c = c + 3; 计算后c的值变为33
c += b; // 等价于 c = c + b; 计算后c的值变为53
a += 1.7; // 等价于 a = a + 1.7 计算后a的值变为11
printf("a=%d b=%d c=%d", a, b, c);
return 0;
}
六:位运算符
运算符 | 描述 | 操作数个数 | 副作用 |
& | 按位与 | 2 | 无 |
| | 按位或 | 2 | 无 |
^ | 按位异或 | 2 | 无 |
~ | 按位取反 | 1 | 无 |
<< | 按位左移 | 2 | 无 |
>> | 按位右移 | 2 | 无 |
注意:操作数进行位运算的时候,以它的补码形式进行运算。
(1):按位与、按位或、按位异或
#include <stdio.h>
int main()
{
int a = 17;
int b = -12;
printf("a&b=%d\n", a & b); // a&b=16
printf("a|b=%d\n", a | b); // a|b=-11
printf("a^b=%d\n", a ^ b); // a^b=-27
return 0;
}
(2):按位取反
#include <stdio.h>
int main()
{
int a = 17;
int b = -12;
// 按位非
printf("~a=%d\n", ~a);
printf("~b=%d\n", ~b);
return 0;
}
(3):按位左移、按位右移
#include <stdio.h>
int main()
{
int a = 17;
int b = -12;
// 按位左移
printf("a<<2=%d\n", a << 2); // a<<2=68
printf("b<<2=%d\n", b << 2); // b<<2=-48
// 按位右移
printf("a>>3=%d\n", a >> 3); // a>>3=2
printf("b>>3=%d\n", b >> 3); // b>>3=-2
return 0;
}
七:三元运算符
基本语法:
条件表达式? 表达式1: 表达式2;
如果条件表达式为非0(真),整个表达式的值是表达式1;
如果条件表达式为0(假),整个表达式的值是表达式2;
#include <stdio.h>
int main()
{
int a = 10;
int b = 99;
int res = a > b ? a++ : b--;
int n1 = a > b ? 1.1 : 1.2;
printf("a=%d \n", a);
printf("b=%d \n", b);
printf("res=%d \n", res);
return 0;
}
- 计算两个数的最大值(a , b)
#include <stdio.h>
int main()
{
int a = 10;
int b = 100;
int max = a > b ? a : b;
printf("a和b中最大的数字:%d", max);
return 0;
}
2.计算三个数的最大值
#include <stdio.h>
int main()
{
int a = 10;
int b = 100;
int c = 199;
// int max1 = a>b ? a : b;
// int max2 = max1>c ? max1:c;
int max = (a > b ? a : b) > c ? (a > b ? a : b) : c;
printf("a、b、c中最大的数字:%d", max);
return 0;
}
八:运算符优先级
优先级 | 运算符 | 名称或含义 | 结合方向 |
1 | [] | 数组下标 | 左到右 |
() | 圆括号 | ||
. | 成员选择(对象) | ||
-> | 成员选择(指针) | ||
2 | - | 负号运算符 | 右到左 |
(类型) | 强制类型转换 | ||
++ | 自增运算符 | ||
-- | 自减运算符 | ||
* | 取值运算符 | ||
& | 取地址运算符 | ||
! | 逻辑非运算符 | ||
~ | 按位取反运算符 | ||
sizeof | 长度运算符 | ||
3 | / | 除 | 左到右 |
* | 乘 | ||
% | 余数(取模) | ||
4 | + | 加 | 左到右 |
- | 减 | ||
5 | << | 左移 | 左到右 |
>> | 右移 | ||
6 | > | 大于 | 左到右 |
>= | 大于等于 | ||
< | 小于 | ||
<= | 小于等于 | ||
7 | == | 等于 | 左到右 |
!= | 不等于 | ||
8 | & | 按位与 | 左到右 |
9 | ^ | 按位异或 | 左到右 |
10 | | | 按位或 | 左到右 |
11 | && | 逻辑与 | 左到右 |
12 | || | 逻辑或 | 左到右 |
13 | ?: | 条件运算符 | 右到左 |
14 | = | 赋值运算符 | 右到左 |
/= | 除后赋值 | ||
*= | 乘后赋值 | ||
%= | 取模后赋值 | ||
+= | 加后赋值 | ||
-= | 减后赋值 | ||
<<= | 左移后赋值 | ||
>>= | 右移后赋值 | ||
&= | 按位与后赋值 | ||
^= | 按位异或后赋值 | ||
|= | 按位或后赋值 | ||
15 | , | 逗号运算符 | 左到右 |
总结:
- 不要过多的依赖运算的优先级来控制表达式的执行顺序,这样可读性太差,尽量使用小括号来控制表达式的执行顺序。
- 不要把一个表达式写得过于复杂,如果一个表达式过于复杂,则把它分成几步来完成。
- 运算符优先级不用刻意地去记忆,总体上:一元运算符 > 算术运算符 > 关系运算符 > 逻辑运算符 > 三元运算符 > 赋值运算符。
本章的内容就到这里。
关注我一起成为嵌入式大佬。