运算符与表达式
5. 运算符与表达式
运算符是 C 语言中用于执行各种操作的符号或关键字。运算符与操作数结合形成表达式,表达式可以计算出一个值。理解各种运算符及其优先级与结合性,是编写正确和高效 C 程序的基础。
5.1 算术运算符
算术运算符用于执行基本的数学计算,包括加、减、乘、除和取模(余数)等操作。
常见算术运算符
| 运算符 | 描述 | 示例 | 解释 |
|---|---|---|---|
+ | 加法 | a + b | 将两个操作数相加 |
- | 减法 | a - b | 从第一个操作数中减去第二个操作数 |
* | 乘法 | a * b | 将两个操作数相乘 |
/ | 除法 | a / b | 将第一个操作数除以第二个操作数 |
% | 取模(余数) | a % b | 计算第一个操作数除以第二个操作数的余数 |
示例与详细解释
#include <stdio.h>
int main() {
int a = 15;
int b = 4;
int sum, difference, product, quotient, remainder;
// 加法
sum = a + b; // 15 + 4 = 19
printf("a + b = %d\n", sum);
// 减法
difference = a - b; // 15 - 4 = 11
printf("a - b = %d\n", difference);
// 乘法
product = a * b; // 15 * 4 = 60
printf("a * b = %d\n", product);
// 除法
quotient = a / b; // 15 / 4 = 3 (整数除法,舍去小数部分)
printf("a / b = %d\n", quotient);
// 取模
remainder = a % b; // 15 % 4 = 3
printf("a %% b = %d\n", remainder);
return 0;
}
输出:
a + b = 19
a - b = 11
a * b = 60
a / b = 3
a % b = 3
注意事项
- 整数除法:当两个整数相除时,结果也是一个整数,任何小数部分都会被舍去。
- 取模运算:取模运算仅适用于整数类型,计算两个整数相除后的余数。
更多示例
#include <stdio.h>
int main() {
float x = 5.5;
float y = 2.2;
float result;
// 加法
result = x + y; // 5.5 + 2.2 = 7.7
printf("x + y = %.1f\n", result);
// 减法
result = x - y; // 5.5 - 2.2 = 3.3
printf("x - y = %.1f\n", result);
// 乘法
result = x * y; // 5.5 * 2.2 = 12.1
printf("x * y = %.1f\n", result);
// 除法
result = x / y; // 5.5 / 2.2 = 2.5
printf("x / y = %.1f\n", result);
return 0;
}
输出:
x + y = 7.7
x - y = 3.3
x * y = 12.1
x / y = 2.5
5.2 关系运算符
关系运算符用于比较两个值,结果是一个布尔值(真或假)。这些运算符常用于条件判断和循环控制。
常见关系运算符
| 运算符 | 描述 | 示例 | 解释 |
|---|---|---|---|
== | 等于 | a == b | 如果 a 等于 b,结果为真(1) |
!= | 不等于 | a != b | 如果 a 不等于 b,结果为真(1) |
> | 大于 | a > b | 如果 a 大于 b,结果为真(1) |
< | 小于 | a < b | 如果 a 小于 b,结果为真(1) |
>= | 大于等于 | a >= b | 如果 a 大于等于 b,结果为真(1) |
<= | 小于等于 | a <= b | 如果 a 小于等于 b,结果为真(1) |
示例与详细解释
#include <stdio.h>
int main() {
int a = 10;
int b = 20;
// 等于
if (a == b) {
printf("a 等于 b\n");
} else {
printf("a 不等于 b\n");
}
// 不等于
if (a != b) {
printf("a 不等于 b\n");
}
// 大于
if (a > b) {
printf("a 大于 b\n");
} else {
printf("a 不大于 b\n");
}
// 小于
if (a < b) {
printf("a 小于 b\n");
}
// 大于等于
if (a >= 10) {
printf("a 大于等于 10\n");
}
// 小于等于
if (b <= 20) {
printf("b 小于等于 20\n");
}
return 0;
}
输出:
a 不等于 b
a 不大于 b
a 小于 b
a 大于等于 10
b 小于等于 20
更多示例
#include <stdio.h>
int main() {
int x = 5;
int y = 5;
int z = 10;
// 使用关系运算符进行比较
printf("x == y: %d\n", x == y); // 1(真)
printf("x != z: %d\n", x != z); // 1(真)
printf("z > y: %d\n", z > y); // 1(真)
printf("x < z: %d\n", x < z); // 1(真)
printf("y >= x: %d\n", y >= x); // 1(真)
printf("z <= 10: %d\n", z <= 10); // 1(真)
return 0;
}
输出:
x == y: 1
x != z: 1
z > y: 1
x < z: 1
y >= x: 1
z <= 10: 1
注意事项
- 布尔值:C 语言中,真用
1表示,假用0表示。 - 类型兼容性:在比较时,操作数应尽量类型一致,避免隐式类型转换带来的问题。
5.3 逻辑运算符
逻辑运算符用于连接两个或多个条件表达式,产生一个布尔值结果。这些运算符在条件判断和控制结构中尤为重要。
常见逻辑运算符
| 运算符 | 描述 | 示例 | 解释 |
|---|---|---|---|
&& | 逻辑与(AND) | a && b | 如果 a 和 b 都为真,结果为真(1) |
| ` | ` | 逻辑或(OR) | |
! | 逻辑非(NOT) | !a | 如果 a 为假,结果为真(1);如果 a 为真,结果为假(0) |
示例与详细解释
#include <stdio.h>
int main() {
int a = 10;
int b = 20;
int c = 30;
// 逻辑与运算
if (a < b && b < c) {
printf("a < b 且 b < c\n");
}
// 逻辑或运算
if (a > b || b < c) {
printf("a > b 或 b < c\n");
}
// 逻辑非运算
if (!(a > b)) {
printf("!(a > b) 即 a <= b\n");
}
return 0;
}
输出:
a < b 且 b < c
a > b 或 b < c
!(a > b) 即 a <= b
更多示例
#include <stdio.h>
int main() {
int x = 5;
int y = 10;
// 逻辑与
if (x > 0 && y > 0) {
printf("x 和 y 都是正数\n");
}
// 逻辑或
if (x < 0 || y > 0) {
printf("x 是负数 或 y 是正数\n");
}
// 逻辑非
if (!(x == y)) {
printf("x 不等于 y\n");
}
return 0;
}
输出:
x 和 y 都是正数
x 是负数 或 y 是正数
x 不等于 y
注意事项
- 短路评估:逻辑与和逻辑或运算符具有短路特性。例如,
a && b,如果 a 为假,b 将不再被计算。 - 优先级:逻辑运算符的优先级低于关系运算符,高于赋值运算符。使用括号可以明确运算顺序。
5.4 位运算符
位运算符用于对整数类型的位(0 和 1)进行操作。这些运算符在底层编程、硬件控制和高效算法实现中非常有用。
常见位运算符
| 运算符 | 描述 | 示例 | 解释 |
|---|---|---|---|
& | 按位与 | a & b | 对应位都为 1 时结果为 1,否则为 0 |
| ` | ` | 按位或 | `a |
^ | 按位异或 | a ^ b | 对应位不同则为 1,相同则为 0 |
~ | 按位取反 | ~a | 对每一位取反,即 0 变 1,1 变 0 |
<< | 左移 | a << 2 | 将位向左移动指定的位数 |
>> | 右移 | a >> 2 | 将位向右移动指定的位数 |
示例与详细解释
#include <stdio.h>
int main() {
unsigned char a = 5; // 二进制:00000101
unsigned char b = 9; // 二进制:00001001
unsigned char result;
// 按位与
result = a & b; // 00000101 & 00001001 = 00000001
printf("a & b = %d\n", result); // 输出 1
// 按位或
result = a | b; // 00000101 | 00001001 = 00001101
printf("a | b = %d\n", result); // 输出 13
// 按位异或
result = a ^ b; // 00000101 ^ 00001001 = 00001100
printf("a ^ b = %d\n", result); // 输出 12
// 按位取反
result = ~a; // ~00000101 = 11111010
printf("~a = %d\n", result); // 输出 250(对于unsigned char)
// 左移
result = a << 2; // 00000101 << 2 = 00010100
printf("a << 2 = %d\n", result); // 输出 20
// 右移
result = b >> 2; // 00001001 >> 2 = 00000010
printf("b >> 2 = %d\n", result); // 输出 2
return 0;
}
输出:
a & b = 1
a | b = 13
a ^ b = 12
~a = 250
a << 2 = 20
b >> 2 = 2
更多示例
#include <stdio.h>
int main() {
unsigned int x = 12; // 二进制:00001100
unsigned int y = 5; // 二进制:00000101
unsigned int z;
// 按位与
z = x & y; // 00001100 & 00000101 = 00000100 (4)
printf("x & y = %u\n", z);
// 按位或
z = x | y; // 00001100 | 00000101 = 00001101 (13)
printf("x | y = %u\n", z);
// 按位异或
z = x ^ y; // 00001100 ^ 00000101 = 00001001 (9)
printf("x ^ y = %u\n", z);
// 按位取反
z = ~x; // ~00001100 = 11110011... (对于unsigned int, 结果依系统而定)
printf("~x = %u\n", z);
// 左移
z = x << 1; // 00001100 << 1 = 00011000 (24)
printf("x << 1 = %u\n", z);
// 右移
z = y >> 1; // 00000101 >> 1 = 00000010 (2)
printf("y >> 1 = %u\n", z);
return 0;
}
输出:
x & y = 4
x | y = 13
x ^ y = 9
~x = 4294967283
x << 1 = 24
y >> 1 = 2
应用场景
-
位掩码:使用按位与操作提取特定位的数据。
#include <stdio.h> int main() { unsigned char flags = 0b10101100; // 假设这是一个标志位 unsigned char mask = 0b00001000; // 掩码,提取第四位 unsigned char result = flags & mask; if (result) { printf("第四位是1\n"); } else { printf("第四位是0\n"); } return 0; }输出:
第四位是1 -
设置、清除和切换位:
#include <stdio.h> int main() { unsigned char flags = 0b00000000; // 初始状态 // 设置第三位 flags |= 0b00000100; // 00000000 | 00000100 = 00000100 printf("设置第三位后: %u\n", flags); // 输出 4 // 清除第三位 flags &= ~0b00000100; // 00000100 & 11111011 = 00000000 printf("清除第三位后: %u\n", flags); // 输出 0 // 切换第二位 flags ^= 0b00000010; // 00000000 ^ 00000010 = 00000010 printf("切换第二位后: %u\n", flags); // 输出 2 // 再次切换第二位 flags ^= 0b00000010; // 00000010 ^ 00000010 = 00000000 printf("再次切换第二位后: %u\n", flags); // 输出 0 return 0; }输出:
设置第三位后: 4 清除第三位后: 0 切换第二位后: 2 再次切换第二位后: 0
注意事项
- 优先级:位运算符的优先级低于算术运算符,高于赋值运算符。使用括号可以明确运算顺序。
- 类型:位运算符仅适用于整数类型(
int、char、long等)。
5.5 赋值运算符
赋值运算符用于将右侧的值赋给左侧的变量。C 语言提供了多种赋值运算符,以简化变量的赋值操作。
常见赋值运算符
| 运算符 | 描述 | 示例 | 解释 |
|---|---|---|---|
= | 简单赋值 | a = b | 将 b 的值赋给 a |
+= | 加后赋值 | a += b | 等价于 a = a + b |
-= | 减后赋值 | a -= b | 等价于 a = a - b |
*= | 乘后赋值 | a *= b | 等价于 a = a * b |
/= | 除后赋值 | a /= b | 等价于 a = a / b |
%= | 取模后赋值 | a %= b | 等价于 a = a % b |
<<= | 左移后赋值 | a <<= 2 | 等价于 a = a << 2 |
>>= | 右移后赋值 | a >>= 2 | 等价于 a = a >> 2 |
&= | 按位与后赋值 | a &= b | 等价于 a = a & b |
| ` | =` | 按位或后赋值 | `a |
^= | 按位异或后赋值 | a ^= b | 等价于 a = a ^ b |
示例与详细解释
#include <stdio.h>
int main() {
int a = 10;
int b = 5;
// 简单赋值
printf("初始值: a = %d, b = %d\n", a, b);
// 加后赋值
a += b; // a = a + b => 10 + 5 = 15
printf("a += b 后: a = %d\n", a);
// 减后赋值
a -= b; // a = a - b => 15 - 5 = 10
printf("a -= b 后: a = %d\n", a);
// 乘后赋值
a *= b; // a = a * b => 10 * 5 = 50
printf("a *= b 后: a = %d\n", a);
// 除后赋值
a /= b; // a = a / b => 50 / 5 = 10
printf("a /= b 后: a = %d\n", a);
// 取模后赋值
a %= 3; // a = a % 3 => 10 % 3 = 1
printf("a %%= 3 后: a = %d\n", a);
// 左移后赋值
a <<= 2; // a = a << 2 => 1 << 2 = 4
printf("a <<= 2 后: a = %d\n", a);
// 右移后赋值
a >>= 1; // a = a >> 1 => 4 >> 1 = 2
printf("a >>= 1 后: a = %d\n", a);
// 按位与后赋值
a &= b; // a = a & b => 2 & 5 = 0
printf("a &= b 后: a = %d\n", a);
// 按位或后赋值
a |= b; // a = a | b => 0 | 5 = 5
printf("a |= b 后: a = %d\n", a);
// 按位异或后赋值
a ^= b; // a = a ^ b => 5 ^ 5 = 0
printf("a ^= b 后: a = %d\n", a);
return 0;
}
输出:
初始值: a = 10, b = 5
a += b 后: a = 15
a -= b 后: a = 10
a *= b 后: a = 50
a /= b 后: a = 10
a %= 3 后: a = 1
a <<= 2 后: a = 4
a >>= 1 后: a = 2
a &= b 后: a = 0
a |= b 后: a = 5
a ^= b 后: a = 0
注意事项
- 操作顺序:赋值运算符具有从右到左的结合性。
- 优先级:赋值运算符的优先级较低,通常与括号结合使用以确保正确的运算顺序。
5.6 条件运算符
条件运算符(又称三元运算符)是一种简洁的条件判断方法,语法形式为条件 ? 表达式1 : 表达式2。根据条件的真假,选择执行表达式 1 或表达式 2。
条件运算符的语法
条件 ? 表达式1 : 表达式2
- 条件:一个布尔表达式,如果为真(非零),则执行表达式 1;否则执行表达式 2。
- 表达式 1 和 表达式 2:可以是任何类型的表达式。
示例与详细解释
#include <stdio.h>
int main() {
int a = 10;
int b = 20;
int max;
// 使用条件运算符找出较大值
max = (a > b) ? a : b; // 如果a > b为真,则max = a;否则max = b
printf("较大的数是: %d\n", max);
// 另一个示例
int num = 15;
char *result;
// 判断num是否为偶数
result = (num % 2 == 0) ? "偶数" : "奇数";
printf("num 是 %s\n", result);
return 0;
}
输出:
较大的数是: 20
num 是 奇数
更多示例
#include <stdio.h>
int main() {
int x = 5;
int y = 10;
int z;
// 条件运算符嵌套
z = (x > y) ? x : (y > 0 ? y : -1);
printf("z = %d\n", z); // 输出 10
// 使用条件运算符简化赋值
int age = 18;
char *status;
status = (age >= 18) ? "成年人" : "未成年人";
printf("年龄 %d 是 %s\n", age, status);
// 结合算术运算符使用
int a = 7;
int b = 3;
int result;
result = (a > b) ? (a - b) : (b - a);
printf("差值: %d\n", result); // 输出 4
return 0;
}
输出:
z = 10
年龄 18 是 成年人
差值: 4
注意事项
- 可读性:虽然条件运算符可以简化代码,但过度使用或嵌套使用可能会降低代码的可读性。应在保持简洁的同时,确保代码清晰易懂。
- 类型一致性:条件运算符的两个表达式(表达式 1 和表达式 2)应尽量类型一致,以避免不必要的类型转换。
5.7 运算符优先级与结合性
在 C 语言中,运算符优先级决定了表达式中不同运算符的计算顺序,而结合性决定了当具有相同优先级的运算符出现在表达式中时,运算的方向。
运算符优先级
不同运算符具有不同的优先级。优先级高的运算符会先被计算。以下是常见运算符的优先级列表(从高到低)。
| 优先级 | 运算符 | 描述 |
|---|---|---|
| 1 | () [] -> . | 函数调用、数组下标、成员访问 |
| 2 | ! ~ ++ -- - + * & | 单目运算符 |
| 3 | * / % | 乘法、除法、取模 |
| 4 | + - | 加法、减法 |
| 5 | << >> | 左移、右移 |
| 6 | < <= > >= | 关系运算符 |
| 7 | == != | 相等运算符 |
| 8 | & | 按位与 |
| 9 | ^ | 按位异或 |
| 10 | ` | ` |
| 11 | && | 逻辑与 |
| 12 | ` | |
| 13 | ?: | 条件运算符(三元运算符) |
| 14 | = += -= *= /= %= <<= >>= &= ^= ` | =` |
| 15 | , | 逗号运算符 |
运算符结合性
结合性决定了当表达式中存在多个相同优先级的运算符时,运算的顺序。
| 结合性 | 运算符 |
|---|---|
| 从左到右 | () [] -> .、算术运算符、关系运算符、逻辑运算符等 |
| 从右到左 | 赋值运算符、条件运算符(三元运算符)等 |
示例与详细解释
示例 1:优先级高的运算符先计算
#include <stdio.h>
int main() {
int a = 5;
int b = 10;
int c;
// 运算符优先级示例
c = a + b * 2; // 先计算 b * 2,再加 a
printf("a + b * 2 = %d\n", c); // 输出 25
c = (a + b) * 2; // 使用括号改变优先级,先计算 a + b,再乘以2
printf("(a + b) * 2 = %d\n", c); // 输出 30
return 0;
}
输出:
a + b * 2 = 25
(a + b) * 2 = 30
示例 2:结合性示例
#include <stdio.h>
int main() {
int x = 1;
int y = 2;
int z = 3;
int a, b;
// 从左到右结合性
a = x = y + z; // y + z = 5, x = 5, a = 5
printf("a = %d, x = %d\n", a, x); // 输出 a = 5, x = 5
// 赋值运算符的右到左结合性
b = a = x = 10; // x = 10, a = 10, b = 10
printf("a = %d, b = %d, x = %d\n", a, b, x); // 输出 a = 10, b = 10, x = 10
return 0;
}
输出:
a = 5, x = 5
a = 10, b = 10, x = 10
示例 3:条件运算符的优先级与结合性
#include <stdio.h>
int main() {
int a = 5, b = 10, c = 15;
int result;
// 条件运算符的优先级较低,需要使用括号确保运算顺序
result = a > b ? a : b > c ? b : c;
// 等价于: result = (a > b) ? a : (b > c ? b : c)
printf("Result: %d\n", result); // 输出 15
return 0;
}
输出:
Result: 15
运算符优先级表
以下是 C 语言中常见运算符的优先级和结合性列表,便于查阅和参考。
| 优先级 | 运算符 | 描述 | 结合性 |
|---|---|---|---|
| 1 | () [] -> . | 函数调用、数组下标、成员访问 | 从左到右 |
| 2 | ! ~ ++ -- - + * & | 单目运算符 | 从右到左 |
| 3 | * / % | 乘法、除法、取模 | 从左到右 |
| 4 | + - | 加法、减法 | 从左到右 |
| 5 | << >> | 左移、右移 | 从左到右 |
| 6 | < <= > >= | 关系运算符 | 从左到右 |
| 7 | == != | 相等运算符 | 从左到右 |
| 8 | & | 按位与 | 从左到右 |
| 9 | ^ | 按位异或 | 从左到右 |
| 10 | ` | ` | 按位或 |
| 11 | && | 逻辑与 | 从左到右 |
| 12 | ` | ` | |
| 13 | ?: | 条件运算符(三元运算符) | 从右到左 |
| 14 | = += -= *= /= %= <<= >>= &= ^= ` | =` | 赋值运算符 |
| 15 | , | 逗号运算符 | 从左到右 |
5.8 总结
运算符与表达式是 C 语言编程中的核心概念。通过理解不同运算符的功能、优先级和结合性,能够编写出逻辑正确且高效的代码。以下是本节的关键点:
- 算术运算符:用于基本的数学计算,如加、减、乘、除和取模。
- 关系运算符:用于比较两个值,返回布尔值(真或假)。
- 逻辑运算符:用于连接多个条件表达式,产生布尔值结果。
- 位运算符:用于对整数类型的位进行操作,适用于底层编程和高效算法。
- 赋值运算符:用于将值赋给变量,提供多种简化赋值的方式。
- 条件运算符:一种简洁的条件判断方法,通常用于简化
if-else语句。 - 运算符优先级与结合性:决定表达式中运算的顺序和方向,确保代码按预期执行。