C 语言运算符的优先级和结合性
运算符的结合性和优先级
优先级 | 运算符 | 描述 | 结合性 |
---|---|---|---|
1 | () [] -> . | 函数调用、数组下标、结构体 / 联合体成员通过指针访问、结构体 / 联合体成员访问 | 从左到右 |
2 | ! ~ ++ (前缀)-- (前缀)+ (一元)- (一元)* (间接寻址)& (取地址)sizeof (type) | 逻辑非、按位取反、前缀自增、前缀自减、一元正号、一元负号、间接寻址、取地址、获取操作数的大小、类型转换 | 从右到左 |
3 | * / % | 乘法、除法、取模 | 从左到右 |
4 | + - | 加法、减法 | 从左到右 |
5 | << >> | 左移、右移 | 从左到右 |
6 | < <= > >= | 小于、小于等于、大于、大于等于 | 从左到右 |
7 | == != | 相等、不相等 | 从左到右 |
8 | & | 按位与 | 从左到右 |
9 | ^ | 按位异或 | 从左到右 |
10 | <code>|</code> | 按位或 | 从左到右 |
11 | && | 逻辑与 | 从左到右 |
12 | <code>||</code> | 逻辑或 | 从左到右 |
13 | ? : | 条件运算符(三元运算符) | 从右到左 |
14 | = += -= *= /= %= <<= >>= &= ^= <code>|=</code> | 赋值、复合赋值运算符 | 从右到左 |
15 | , | 逗号运算符 | 从左到右 |
上手demo
#include <stdio.h>
int main() {
int a = 2, b = 3, c = 4;
// 乘法优先级高于加法
int result1 = a + b * c; // 先计算 b * c,结果为 12,再加上 a,最终结果为 14
printf("result1: %d\n", result1);
// 使用括号改变优先级
int result2 = (a + b) * c; // 先计算 a + b,结果为 5,再乘以 c,最终结果为 20
printf("result2: %d\n", result2);
// 后缀自增和前缀自增
int d = 5;
int result3 = d++ + ++d; // 先使用 d 的值 5 进行计算,然后 d 自增,再将自增后的 d(此时为 7)加 1,最终结果为 12
printf("result3: %d\n", result3);
// 逻辑运算符
int result4 = (a < b) && (b < c); // 先计算 a < b 和 b < c,结果都为真,最终结果为 1
printf("result4: %d\n", result4);
// 条件运算符
int result5 = (a > b)? a : b; // a 不大于 b,所以结果为 b 的值,即 3
printf("result5: %d\n", result5);
// 复合赋值运算符
a += b; // 等价于 a = a + b,a 的结果为 5
printf("a after +=: %d\n", a);
return 0;
}
括号运算符 ()
用于改变运算的优先级,也用于函数调用和类型转换,其在表达式中,括号内的运算总是优先进行。
#include <stdio.h>
int main() {
int a = 5, b = 3, c = 2;
// 改变运算顺序
int result1 = (a + b) * c; // 先计算 a + b,结果为 8,再乘以 c,最终结果为 16
printf("result1: %d\n", result1);
// 函数调用
int sum(int x, int y) {
return x + y;
}
int result2 = sum(a, b); // 调用 sum 函数,将 a 和 b 的值传递给函数,结果为 8
printf("result2: %d\n", result2);
// 类型转换
float f = (float)a; // 将整型 a 转换为浮点型,结果为 5.0
printf("result3: %f\n", f);
return 0;
}
数组下标运算符 []
用于访问数组中的元,其下标必须是整数,可以是常量或变量。
*越界访问数组(使用超出数组长度的下标)会导致未定义行为!!!
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int index = 2;
int element = arr[index]; // 访问数组 arr 中索引为 2 的元素,结果为 30
printf("element: %d\n", element);
return 0;
}
结构体和联合体成员访问运算符 .
和 ->
.
用于直接访问结构体或联合体的成员。->
用于通过指针访问结构体或联合体的成员。
#include <stdio.h>
#include <stdlib.h>
struct Point {
int x;
int y;
};
int main() {
struct Point p = {10, 20};
// 直接访问成员
int x = p.x; // 访问结构体 p 的成员 x,结果为 10
printf("x: %d\n", x);
struct Point *ptr = (struct Point *)malloc(sizeof(struct Point));
ptr->x = 30; // 通过指针访问成员 x
ptr->y = 40; // 通过指针访问成员 y
printf("ptr->x: %d\n", ptr->x);
printf("ptr->y: %d\n", ptr->y);
free(ptr);
return 0;
}
一元运算符
逻辑非 !
对操作数取逻辑反,将非零值变为 0,当操作数 0 返回结果为 1。
#include <stdio.h>
int main() {
int a = 0;
int result =!a; // 结果为 1
printf("result: %d\n", result);
return 0;
}
按位取反 ~
对操作数的二进制表示按位取反,也包括符号位(0为正,1为负)。
#include <stdio.h>
int main() {
int a = 5; // 二进制: 0101
int result = ~a; // 结果为 -6(二进制: 1010)
printf("result: %d\n", result);
return 0;
}
前缀 / 后缀自增 ++
和前缀 / 后缀自减 --
- 前缀:先将操作数加 / 减 1,再使用结果。
- 后缀:先使用操作数的值,再将操作数加 / 减 1。
#include <stdio.h>
int main() {
int a = 5;
int b = ++a; // 前缀自增,a 先加 1 变为 6,b 为 6
int c = a++; // 后缀自增,使用 a 的值 6,然后 a 加 1 变为 7
printf("b: %d, c: %d, a: %d\n", b, c, a);
return 0;
}
一元正号 +
和一元负号 -
+
:不改变操作数的值,主要用于明确表示正数。-
:对操作数取负。
#include <stdio.h>
int main() {
int a = 5;
int b = -a; // 结果为 -5
int c = +a; // 结果为 5
printf("b: %d, c: %d\n", b, c);
return 0;
}
间接寻址 *
和取地址 &
*
:解引用指针,获取指针访问所指向的值。&
:获取变量的地址。
#include <stdio.h>
int main() {
int a = 10;
int *ptr = &a; // 获取 a 的地址存储在 ptr 中
int value = *ptr; // 通过 ptr 访问 a 的值,结果为 10
printf("value: %d\n", value);
return 0;
}
sizeof
运算符
获取操作数的大小(以字节为单位)。
#include <stdio.h>
int main() {
int a;
size_t size = sizeof(a); // 获取 int 类型的大小,通常为 4
printf("size: %zu\n", size);
return 0;
}
类型转换 (type)
将操作数转换为指定的数据类型。当是多精度转单精度类型时,可能导致数据丢失(如浮点数转整数)或截断。
#include <stdio.h>
int main() {
double d = 3.14;
int i = (int)d; // 将双精度浮点数转换为整数,结果为 3
printf("i: %d\n", i);
return 0;
}
算术运算符
乘法 *
和除法 /
*
:两个操作数相乘。/
:两个操作数相除,对于整数除法,结果会舍去小数部分;对于浮点数除法,使用float
或double
类型。
#include <stdio.h>
int main() {
int a = 10, b = 3;
int product = a * b; // 结果为 30
int quotient = a / b; // 结果为 3(整数除法)
printf("product: %d, quotient: %d\n", product, quotient);
return 0;
}
取模 %
计算两个整数相除的余数。操作数必须是整数,否则会导致编译错误!!!
#include <stdio.h>
int main() {
int a = 10, b = 3;
int remainder = a % b; // 结果为 1
printf("remainder: %d\n", remainder);
return 0;
}
加法 +
和减法 -
+
:两个操作数相加。-
:两个操作数相减。
#include <stdio.h>
int main() {
int a = 5, b = 3;
int sum = a + b; // 结果为 8
int difference = a - b; // 结果为 2
printf("sum: %d, difference: %d\n", sum, difference);
return 0;
}
移位运算符
左移 <<
将操作数的二进制表示向左移动指定的位数,右边补 0。
#include <stdio.h>
int main() {
int a = 5; // 二进制: 0101
int result = a << 2; // 左移 2 位,结果为 20(二进制: 10100)
printf("result: %d\n", result);
return 0;
}
右移 >>
将操作数的二进制表示向右移动指定的位数,对于无符号数左边补 0,对于有符号数根据编译器和系统可能补 0 或符号位。
#include <stdio.h>
int main() {
int a = 10; // 二进制: 1010
int result = a >> 1; // 右移 1 位,结果为 5(二进制: 0101)
printf("result: %d\n", result);
return 0;
}
关系运算符:<
(小于)、<=
(小于等于)、>
(大于)、>=
(大于等于)、==
(相等)、!=
(不相等)
用于比较两个操作数的大小或相等性,结果为 1(真)或 0(假)。
#include <stdio.h>
int main() {
int a = 5, b = 3;
int less_than = a < b; // 结果为 0(假)
int less_than_or_equal = a <= b; // 结果为 0(假)
int greater_than = a > b; // 结果为 1(真)
int greater_than_or_equal = a >= b; // 结果为 1(真)
int equal = a == b; // 结果为 0(假)
int not_equal = a!= b; // 结果为 1(真)
printf("less_than: %d, less_than_or_equal: %d, greater_than: %d, greater_than_or_equal: %d, equal: %d, not_equal: %d\n",
less_than, less_than_or_equal, greater_than, greater_than_or_equal, equal, not_equal);
return 0;
}
位运算符
- 位运算符用于对二进制位进行操作,常用于底层编程、硬件控制和性能优化。
- 按位与
&
常用于清除某些位或检查特定位是否置位。 - 按位或
|
常用于设置某些位。 - 按位异或
^
常用于反转某些位或交换两个数的值(不使用临时变量)。
按位与 &
对操作数的每一位进行逻辑与操作,都为 1 时结果为 1,否则为 0。
#include <stdio.h>
int main() {
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
int result = a & b; // 结果为 1(二进制: 0001)
printf("result: %d\n", result);
return 0;
}
按位异或 ^
对操作数的每一位进行逻辑异或操作,不同为 1,相同为 0。
#include <stdio.h>
int main() {
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
int result = a ^ b; // 结果为 6(二进制: 0110)
printf("result: %d\n", result);
return 0;
}
按位或 |
对操作数的每一位进行逻辑或操作,有一个为 1 时结果为 1,否则为 0。
#include <stdio.h>
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
int result = a | b; // 结果为 7(二进制: 0111)
printf("result: %d\n", result);
return 0;
}
逻辑运算符
逻辑与 &&
和逻辑或 ||
具有短路特性,即对于 &&
,如果第一个操作数为假,则不计算第二个操作数;对于 ||
,如果第一个操作数为真,则不计算第二个操作数。
逻辑与 &&
当且仅当两个操作数都为真(非零)时,结果为真。
#include <stdio.h>
int main() {
int a = 5, b = 0;
int result = (a > 0) && (b > 0); // 结果为 0(假)
printf("result: %d\n", result);
return 0;
}
逻辑或 ||
当两个操作数中至少有一个为真时,结果为真。
#include <stdio.h>
int main() {
int a = 5, b = 0;
int result = (a > 0) || (b > 0); // 结果为 1(真)
printf("result: %d\n", result);
return 0;
}
条件运算符(三元运算符)
根据条件表达式的结果选择两个表达式中的一个,condition? expression1 : expression2
,如果 condition
为真,结果为 expression1
,否则为 expression2
。
#include <stdio.h>
int main() {
int a = 5, b = 3;
int max = (a > b)? a : b; // 结果为 5
printf("max: %d\n", max);
return 0;
}
赋值运算符:=
,+=
,-=
,*=
,/=
,%=
,<<=
,>>=
,&=
,^=
,|=
将右侧表达式的值赋给左侧的变量。赋值表达式的值就是被赋的值。例如,int b = (a = 5);
中,先将 5 赋给 a
,然后将 a
的值赋给 b
。
#include <stdio.h>
int main() {
int a;
a = 10; // 将 10 赋值给 a
printf("a: %d\n", a);
return 0;
}
复合赋值运算符
结合了算术、移位或位运算与赋值操作,提高代码的简洁性,先进行算术、移位或位运算,然后将结果赋给左侧变量。
#include <stdio.h>
int main() {
int a = 5;
a += 3; // 等价于 a = a + 3,结果为 8
a -= 2; // 等价于 a = a - 2,结果为 6
a *= 4; // 等价于 a = a * 4,结果为 24
a /= 3; // 等价于 a = a / 3,结果为 8
a %= 5; // 等价于 a = a % 5,结果为 3
a <<= 1; // 等价于 a = a << 1,结果为 6(二进制:110)
a >>= 1; // 等价于 a = a >> 1,结果为 3(二进制:011)
a &= 2; // 等价于 a = a & 2,结果为 2(二进制:010)
a ^= 1; // 等价于 a = a ^ 1,结果为 3(二进制:011)
a |= 4; // 等价于 a = a | 4,结果为 7(二进制:111)
printf("a: %d\n", a);
return 0;
}
逗号运算符 ,
从左到右依次计算操作数,并返回最后一个操作数的值。
#include <stdio.h>
int main() {
int a = (1, 2, 3); // 结果为 3
int b = 5, c = 10;
int d = (b++, c++, b + c); // 先执行 b++ 和 c++,然后计算 b + c,结果为 17
printf("a: %d, d: %d\n", a, d);
return 0;
}
常用于在 for
循环的初始化或更新部分中执行多个操作
#include <stdio.h>
int main() {
for (int i = 0, j = 10; i < 5; i++, j--) {
printf("i: %d, j: %d\n", i, j);
}
return 0;
}
运算符优先级和结合性
- 优先级:
- 运算符的优先级决定了表达式中运算的顺序,在复杂表达式中可能导致混淆。例如,在
a + b * c
中,先计算乘法b * c
,再计算加法。为避免混淆,可使用括号明确运算顺序,如(a + b) * c
。
- 运算符的优先级决定了表达式中运算的顺序,在复杂表达式中可能导致混淆。例如,在
- 结合性:
- 从左到右结合性:大多数二元运算符是从左到右结合的,如
a + b + c
先计算a + b
,再加上c
。 - 从右到左结合性:一元运算符和赋值运算符通常是从右到左结合的,如
a = b = c
先将c
赋给b
,再将b
的值赋给a
。
- 从左到右结合性:大多数二元运算符是从左到右结合的,如