C语言中操作符的分类和优先级
1.操作符的分类
总结
- 算术操作符:+ 、 - 、 *、 / 、 %
- 单目操作符:++、- -、!、&、*、+、-、sizeof、(类型)
- 赋值操作符:+=、-=、*=、/=、%=、=、>>=、<<=、&=、|=、^=
- 位操作符:>>、<<、&、|、^、~
- 逻辑操作符:&&、||
- 关系操作符:>、>=、<、<=、==、!=
- 条件操作符(三目操作符):? :
- 逗号表达式:,
- 索引访问操作符:[ ]
- 函数调用:()
- 成员访问操作符:. 、->
算术操作符
1.加法(+)
用于将两个操作数相加。例如int a = 3 + 5;,这里3和5是操作数,+是加法操作符,计算结果8被赋值给变量a。
2.减法(-)
是用于减法运算,如int b = 7- 2;,计算结果5赋值给b。
3.乘法(*)
用于两个操作数的乘法运算。例如int a= 4*6;,结果24赋给a。
4.除法(/)
当操作数是整数时,执行整数除法。例如int e = 10/3;,结果是3(因为整数除法会舍去小数部分)。如果操作数是浮点数,则执行浮点数除法,如float a = 10.0/3.0;,结果约为3.333。
5.取余(%)
用于计算两个整数相除的余数。例如int a= 10 % 3;,结果是1,因为10除以3商为3,余数为1。
单目操作符
1.逻辑非(!)
功能:对操作数进行逻辑取反。如果操作数为非零(在 C 语言中视为真),则结果为 0(假);如果操作数为 0(假),则结果为 1(真)。
示例:
int a = 5;
int b =!a;//先判断a的值为非零(真),然后取反,所以b的值为 0。
-----------------------------------------------------------------
//再如
int c = 0;
int d =!c;//因为c的值为 0(假),取反后d的值为 1。
2.负号(-)
功能:改变操作数的符号,将正数变为负数,负数变为正数。
示例:
int e = 3;
int f=-e;//此时f的值为 - 3。
-----------------------------------------------------------------
//又如
int g=-7;
int h = -g;//则h的值为 7。
3.正号(+)
功能:通常在操作数前面加上正号,对操作数的值没有实际改变,但在一些复杂的表达式中可以明确操作数的符号意图。
示例:
int i = +5;//这里i的值就是 5
int i = 5;//效果相同。
4.自增(++)
功能:有两种形式,前缀自增(++a)和后缀自增(a++)。前缀自增是先将操作数的值加 1,然后再使用操作数的值;后缀自增是先使用操作数的值,然后再将操作数的值加 1。
示例:
//前缀自增:
int j = 3;
int k = ++j;//先将j的值加 1 变为 4,然后将 4 赋值给k,所以k和j的值都为 4。
俗称先加后用
//后缀自增:
int m = 3;
int n = m++;//先将m的值(3)赋值给n,然后m的值再加 1 变为 4,所以n的值为 3,m的值为 4。
俗称先用后加
5.自减(–)
功能:和自增操作符类似,也有前缀自减(–a)和后缀自减(a–)两种形式。前缀自减是先将操作数的值减 1,然后再使用操作数的值;后缀自减是先使用操作数的值,然后再将操作数的值减 1。
示例:
//前缀自减:
int o = 5;
int p = --o;//先将o的值减 1 变为 4,然后将 4 赋值给p,所以p和o的值都为 4。
//后缀自减:
int q = 5;
int r = q--;//先将q的值(5)赋值给r,然后q的值再减 1 变为 4,所以r的值为 5,q的值为 4。
6.取地址(&)
功能:用于获取变量在内存中的地址。
示例:
int s = 10;
int *t = &s;//这里&s获取变量s的内存地址,并将这个地址赋值给指针变量t。指针变量t就指向了变量s。
7.间接访问(*)(在指针相关场景下作为单目操作符)
功能:用于访问指针所指向的变量的值。
示例:
基于上面取地址的例子
int s = 10; int *t = &s;
int u = *t;//这里*t就是访问指针t所指向的变量(也就是s)的值,所以u的值为 10。
8.类型转换操作符((类型))
功能:将操作数的值转换为指定的类型。
示例:
float v = 3.14;
int w=(int)v;//这里将浮点数v强制转换为整数,w的值为 3(会截断小数部分)。
9.sizeof 操作符
功能:用于计算操作数(可以是变量、类型等)占用的字节数。(注意:sizeof是操作符而非函数)
示例:
int x = 10;
sizeof(x)返回4(在 32 位系统中int通常占4个字节)
sizeof(int)也返回4。
赋值操作符
1.简单赋值操作符(=)
功能:
赋值操作符 “=” 的主要功能是将右边表达式的值赋给左边的变量。这个操作符会改变左边变量的值,使其等于右边表达式的计算结果。
示例:
int a;
a = 5;//这里先声明了一个整型变量a,然后通过赋值操作符将整数5赋给变量a,此时a的值就为5。
右边的表达式可以是一个常量,也可以是一个复杂的表达式。
例如int b; b=(3 + 4);,先计算括号内的表达式3 + 4得到7,然后将7赋值给变量b
2.加法赋值(+=)
功能:a += b等价于a = a + b。它先将变量a与变量b相加,然后将结果赋值给a。
示例:
int c = 3; c += 2;//等价于c = c + 2;
所以c的值最终为5。
3.减法赋值(-=)
功能:a -= b等价于a = a - b。先将变量a减去变量b,然后将结果赋值给a。
示例:
int d = 7; d -= 3;//等价于d = d - 3;
d的值变为4
4.乘法赋值(*=)
功能:a *= b等价于a = a * b。先将变量a与变量b相乘,然后将结果赋值给a。
示例:
int e = 2; e *= 4;//等价于e = e * 4;
e的值变为8。
5.除法赋值(/=)
功能:a /= b等价于a = a / b。先将变量a除以变量b,然后将结果赋值给a。
示例:
int f = 10; f /= 2;//等价于f = f / 2;
f的值变为5。
6.取余赋值(%=)
功能:a %= b等价于a = a % b。先将变量a除以变量b取余数,然后将余数赋值给a。
示例:
int g = 7; g %= 3;//等价于g = g % 3;
g的值变为1。
7.左移赋值(<<=)
功能:a <<= b等价于a = a << b。先将变量a的二进制位向左移动b位,然后将结果赋值给a。
示例:
int h = 1; h <<= 1;//等价于h = h << 1;
1的二进制是0001,左移一位后变为0010,所以h的值变为2。
8.右移赋值(>>=)
功能:a >>= b等价于a = a >> b。先将变量a的二进制位向右移动b位,然后将结果赋值给a。
示例:
int i = 4; i >>= 1;//等价于i = i >> 1;
4的二进制是0100,右移一位后变为0010,所以i的值变为2。
9.按位与赋值(&=)
功能:a &= b等价于a = a & b。先对变量a和变量b进行按位与运算,然后将结果赋值给a。
示例:
int j = 3; j &= 2;//等价于j = j & 2;
3的二进制是0011,2的二进制是0010,按位与后得到0010,所以j的值变为2。
10.按位或赋值(|=)
功能:a |= b等价于a = a | b。先对变量a和变量b进行按位或运算,然后将结果赋值给a。
示例:
int k = 1; k |= 3;//等价于k = k | 3;
1的二进制是0001,3的二进制是0011,按位或后得到0011,所以k的值变为3。
11.按位异或赋值(^=)
功能:a ^= b等价于a = a ^ b。先对变量a和变量b进行按位异或运算,然后将结果赋值给a。
示例:
int l = 3; l ^= 2;//等价于l = l ^ 2;
3的二进制是0011,2的二进制是0010,按位异或后得到0001,所以l的值变为1。
位操作符
- 原码
- 定义:原码是一种简单的机器数表示法。对于一个有符号整数,最高位为符号位(0表示正数,1表示负数),其余位表示数值的绝对值。
- 示例:
- 对于+5,若用8位二进制表示原码,则为00000101。其中最高位0表示正数,后面7位0000101表示数值5。
- 对于 - 5,其8位原码是10000101。最高位1代表负数,后面7位0000101是数值5的二进制表示。
- 反码
- 正数反码定义:正数的反码与原码相同。
- 负数反码定义:负数的反码是在原码的基础上,符号位不变,其余位按位取反。
- 示例:
- 对于+5,8位二进制反码和原码一样是00000101。
- 对于 - 5,原码是10000101,反码则是11111010。即符号位1不变,后面7位0000101按位取反得到11111010。
- 补码
- 正数补码定义:正数的补码与原码、反码相同。
- 负数补码定义:负数的补码是在反码的基础上加1。
- 示例:
- 对于+5,8位二进制补码为00000101。
- 对于 - 5,反码是11111010,补码则是11111010+1 = 11111011。
- 原码、反码、补码的用途及意义
- 计算机运算中的应用:
- 在计算机中,使用补码进行加减法运算可以简化硬件电路设计。例如,计算机进行减法运算时,实际上是通过加法来实现的。对于两个数 a a a和 b b b,计算 a − b a - b a−b,在补码系统中可以转换为 a + ( − b ) a+(-b) a+(−b)的补码运算。
- 假设用8位二进制数表示,计算 5 − 3 5-3 5−3。5的补码是00000101,-3的补码是11111101(3的原码10000011,反码11111100,补码11111101)。将它们相加: 00000101 + 11111101 = 00000010 00000101+11111101 = 00000010 00000101+11111101=00000010,结果为2,这正是 5 − 3 5 - 3 5−3的正确结果。
- 数据存储和表示方面的作用:
- 原码最直观地表示了数的正负和大小,但是在运算时存在一些不便,如涉及减法时需要单独处理符号位。反码主要是作为计算补码的中间步骤。而补码在计算机系统中被广泛用于整数的存储和运算,使得计算机可以统一用加法电路来实现加法和减法运算,提高了运算效率和硬件的简洁性。
- 计算机运算中的应用:
- 左移操作符(<<)与右移操作符(>>)
- 注:移位操作符的操作数只能是整数。
- 1.1 左移操作符
- 移位规则:左边抛弃,右边补0。
- 示例:
#include<stdio.h>
int main()
{
int num=10;
int n=num<<1;
printf("n=%d\n",n);
printf("num=%d\n",num);
return 0;
}
int num=10;
num的2进制 00000000000000000000000000001010
num<<1的结果,但num的值不变 0|00000000000000000000000000010100(左边去一位,右边补一位)
- 1.2 右移操作符
- 移位规则:有两种
- 逻辑右移:左边用0补充,右边去掉
- 算术右移:左边用原值的符号位补充,右边去掉(与编译器有关,大多数为第二种)
- 移位规则:有两种
#include<stdio.h>
int main()
{
int num=-1;
int n=num>>1;
printf("n=%d\n",n);
printf("num=%d\n",num);
return 0;
}
算术右移:int num=-1
num的2进制表示(内存中为补码)11111111111111111111111111111111
num>>1:11111111111111111111111111111111|1(左边补一符号位,右边去一位)
- 对于移位操作符,不能移动负数位,这个标准未定义
- 按位与(&)操作符
- 功能:
按位与操作符对两个操作数对应的二进制位进行与运算。只有当两个二进制位都为 1 时,结果位才为 1;否则为 0。 - 示例:
假设a = 5(二进制为0101),b = 3(二进制为0011)。
计算a & b时,将它们的二进制位逐位进行与运算:
从最左边开始,0(a的最高位)和0(b的最高位)进行与运算,结果为0。
接着,1(a的次高位)和0(b的次高位)进行与运算,结果为0。
然后,0(a的第三位)和1(b的第三位)进行与运算,结果为0。
最后,1(a的最低位)和1(b的最低位)进行与运算,结果为1。
所以a & b的结果是二进制0001,即十进制的1。 - 应用场景:
常用于屏蔽某些位。例如,要获取一个字节(8 位)数据的低 4 位,可以将这个字节与二进制00001111(十进制15)进行按位与运算。
- 按位或(|)操作符
- 功能:
按位或操作符对两个操作数对应的二进制位进行或运算。只要两个二进制位中有一个为 1,结果位就为 1;只有当两个二进制位都为 0 时,结果位才为 0。 - 示例:
同样假设a = 5(二进制为0101),b = 3(二进制为0011)。
计算a | b时,逐位进行或运算:
最高位0(a)和0(b)进行或运算,结果为0。
次高位1(a)和0(b)进行或运算,结果为1。
第三位0(a)和1(b)进行或运算,结果为1。
最低位1(a)和1(b)进行或运算,结果为1。
所以a | b的结果是二进制0111,即十进制的7。 - 应用场景:
可以用于设置某些位。例如,要将一个字节的低 4 位全部设置为 1,可以将这个字节与二进制00001111进行按位或运算。
- 按位异或(^)操作符
- 功能:
按位异或操作符对两个操作数对应的二进制位进行异或运算。当两个二进制位不同时,结果位为 1;相同时,结果位为 0。 - 示例:
还是假设a = 5(二进制为0101),b = 3(二进制为0011)。
计算a ^ b时,逐位进行异或运算:
最高位0(a)和0(b)进行异或运算,结果为0。
次高位1(a)和0(b)进行异或运算,结果为1。
第三位0(a)和1(b)进行异或运算,结果为1。
最低位1(a)和1(b)进行异或运算,结果为0。
所以a ^ b的结果是二进制0110,即十进制的6。 - 应用场景:
可以用于数据加密中的简单加密和解密。例如,用一个密钥与数据进行按位异或操作进行加密,再次用相同的密钥进行异或操作就可以解密。
逻辑操作符
- 逻辑与(&&)操作符
- 功能:
逻辑与操作符 && 用于对两个逻辑表达式进行逻辑与运算。当且仅当两个操作数都为真(在 C 语言中,非零值表示真)时,结果为真(值为 1);只要有一个操作数为假(值为 0),结果就为假(值为 0)。 - 示例:
int a = 5, b = 3, c = 0;
int result1 = (a > 2) && (b > 1); //这里 (a > 2) 为真(因为 5 > 2),(b > 1) 也为真(因为 3 > 1)
所以 result1 的值为 1。
int result2 = (a > 2) && (c > 1); //因为 (c > 1) 为假(因为 0 > 1 不成立),即使 (a > 2) 为真
整个表达式的结果 result2 为 0。
- 短路求值特性:
当使用 && 操作符时,如果第一个操作数为假,那么不会计算第二个操作数,因为无论第二个操作数的值如何,整个表达式的结果都为假。
- 逻辑或(||)操作符
- 功能:
逻辑或操作符 || 用于对两个逻辑表达式进行逻辑或运算。只要两个操作数中有一个为真,结果就为真;只有当两个操作数都为假时,结果才为假。 - 示例:
int e = 2, f = 0, g = 4;
int result4 = (e > 3) || (f > 1); //这里 (e > 3) 为假(因为 2 > 3 不成立),(f > 1) 也为假(因为 0 > 1 不成立),
所以 result4 的值为 0。
int result5 = (e > 1) || (f > 1); //因为 (e > 1) 为真(因为 2 > 1),
所以 result5 的值为 1,即使 (f > 1) 为假。
- 短路求值特性:
当使用 || 操作符时,如果第一个操作数为真,那么不会计算第二个操作数,因为无论第二个操作数的值如何,整个表达式的结果都为真。
关系操作符
- 大于(>)操作符
- 功能:
- 用于比较两个操作数的大小,判断左边的操作数是否大于右边的操作数。如果左边操作数大于右边操作数,结果为真(在 C 语言中用
1
表示),否则为假(用0
表示)。
- 用于比较两个操作数的大小,判断左边的操作数是否大于右边的操作数。如果左边操作数大于右边操作数,结果为真(在 C 语言中用
- 示例:
- int a = 5, b = 3;
- int result = a > b;,因为
5
大于3
,所以result
的值为1
。 - 对于不同类型的操作数,如
float c = 3.5, d = 2.1;
,int res = c > d;
,先将c
和d
的值比较,因为3.5
大于2.1
,所以res
的值为1
。
- 小于(<)操作符
- 功能:
- 与大于操作符相反,用于判断左边的操作数是否小于右边的操作数。若左边操作数小于右边操作数,结果为真(值为
1
),否则为假(值为0
)。
- 与大于操作符相反,用于判断左边的操作数是否小于右边的操作数。若左边操作数小于右边操作数,结果为真(值为
- 示例:
int e = 2, f = 4;
int res1 = e < f;
,因为2
小于4
,所以res1
的值为1
。- 对于混合类型,如
double g = 1.2, h = 1.5;
,int res2 = g < h;
,因为1.2
小于1.5
,所以res2
的值为1
。
- 功能:
- 大于等于(>=)操作符
- 功能:
- 判断左边的操作数是否大于或等于右边的操作数。如果满足该条件,结果为真(值为
1
),否则为假(值为0
)。
- 判断左边的操作数是否大于或等于右边的操作数。如果满足该条件,结果为真(值为
- 示例:
int i = 5, j = 5;
int res3 = i >= j;
,由于5
等于5
,所以res3
的值为1
。- 再如
int k = 7, l = 4;
,int res4 = k >= l;
,因为7
大于4
,所以res4
的值为1
。
- 功能:
- 小于等于(<=)操作符
- 功能:
- 检查左边的操作数是否小于或等于右边的操作数。满足此条件时结果为真(值为
1
),不满足则为假(值为0
)。
- 检查左边的操作数是否小于或等于右边的操作数。满足此条件时结果为真(值为
- 示例:
int m = 3, n = 3;
int res5 = m <= n;
,因为3
等于3
,所以res5
的值为1
。- 例如
int o = 2, p = 5;
,int res6 = o <= p;
,因为2
小于5
,所以res6
的值为1
。
- 功能:
- 等于(==)操作符
- 功能:
- 用来判断两个操作数是否相等。当两个操作数的值相等时,结果为真(值为
1
),不相等时为假(值为0
)。
- 用来判断两个操作数是否相等。当两个操作数的值相等时,结果为真(值为
- 示例:
int q = 4, r = 4;
int res7 = q == r;
,由于4
等于4
,所以res7
的值为1
。- 注意与赋值操作符
=
的区别,如int s = 3; s == 3;
只是判断s
的值是否为3
,而s = 3
是将3
赋值给s
。 - 对于不同类型,如
float t = 3.0, u = 3.0;
,int res8 = t == u;
,因为3.0
等于3.0
,所以res8
的值为1
。
- 功能:
- 不等于(!=)操作符
- 功能:
- 当两个操作数不相等时,结果为真(值为
1
),相等时为假(值为0
)。
- 当两个操作数不相等时,结果为真(值为
- 示例:
int v = 2, w = 3;
int res9 = v!= w;
,因为2
不等于3
,所以res9
的值为1
。- 对于不同类型,如
double x = 2.5, y = 2.0;
,int res10 = x!= y;
,因为2.5
不等于2.0
,所以res10
的值为1
。
- 功能:
三目操作符
- 基本语法:
- 三目操作符在 C 语言中的表示形式为
表达式1? 表达式2 : 表达式3
。
- 功能:
- 首先对
表达式1
进行求值,如果表达式1
的结果为真(非零),则整个三目操作符的结果为表达式2
的值;如果表达式1
的结果为假(零),则整个三目操作符的结果为表达式3
的值。
- 首先对
- 示例:
int a = 5, b = 3;
int max = (a > b)? a : b;
:- 首先计算
表达式1
,即a > b
,因为5 > 3
,所以a > b
的结果为真(值为 1)。 - 由于
表达式1
为真,整个三目操作符的结果为表达式2
的值,即a
的值,所以max
的值为5
。
- 首先计算
- 另一个例子:
int c = 2, d = 4;
int min = (c < d)? c : d;
:- 先计算
表达式1
,即c < d
,因为2 < 4
,所以c < d
的结果为真(值为 1)。 - 因此,整个三目操作符的结果为
表达式2
的值,即c
的值,所以min
的值为2
。
- 先计算
- 使用场景:
- 简化条件判断:
- 可以用来替代简单的
if-else
语句,使代码更加简洁。例如,以下是使用if-else
语句的代码:int num1 = 10, num2 = 20; int result; if (num1 > num2) { result = num1; } else { result = num2; }
- 可以使用三目操作符简化为:
int num1 = 10, num2 = 20; int result = (num1 > num2)? num1 : num2;
- 可以用来替代简单的
- 表达式中使用:
- 三目操作符可以直接用在表达式中,例如:
int x = 5, y = 3; int z = (x > y)? (x + 1) : (y - 1);
- 这里先判断
x > y
,因为5 > 3
为真,所以z
的值为x + 1
的结果,即6
。
- 三目操作符可以直接用在表达式中,例如:
- 简化条件判断:
- 注意事项:
- 虽然三目操作符可以使代码简洁,但如果表达式过于复杂,可能会降低代码的可读性,所以建议在表达式较为简单的情况下使用。
- 避免嵌套过多的三目操作符,如
a > b? c > d? e : f : g
,这种嵌套会使代码难以理解,通常可以使用if-else
语句替代。 - 三目操作符的三个表达式的类型最好一致,避免出现类型不匹配的问题,如
int m = (a > b)? 3.5 : 4;
可能会导致类型转换的潜在问题。
逗号表达式
- 基本语法:
逗号表达式的形式为 表达式1, 表达式2, 表达式3, …, 表达式n。 - 功能:
逗号表达式会从左到右依次计算各个表达式的值,整个逗号表达式的值是最后一个表达式(表达式 n)的值。
索引访问操作符
在 C 语言中,索引访问操作符是 [],主要用于数组元素的访问。
它允许通过数组的索引(也称为下标)来访问数组中特定位置的元素。
函数调用
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数是传递给函数的参数。
成员访问操作符
-
结构体成员访问操作符(.)
- 语法:结构体变量名.成员名。这个操作符用于访问结构体变量中的成员。
-
指向结构体的指针成员访问操作符(->)
- 语法:结构体指针->成员名。当有一个指向结构体的指针时,使用这个操作符来访问结构体指针所指向的结构体中的成员。
2.操作符的优先级