第03章 运算符
3.1 运算符分类
C语言的运算符按功能分类:
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 条件运算符
- 其他运算符
3.2 算术运算符
3.2.1 基本算术运算符
| 运算符 | 说明 | 示例 | 结果 |
|---|---|---|---|
| + | 加法 | 5 + 3 | 8 |
| - | 减法 | 5 - 3 | 2 |
| * | 乘法 | 5 * 3 | 15 |
| / | 除法 | 5 / 3 | 1 (整数除法) |
| % | 取模(余数) | 5 % 3 | 2 |
3.2.2 整数除法与浮点除法
#include <stdio.h>
int main()
{
int a = 5, b = 2;
float c = 5.0, d = 2.0;
printf("整数除法: %d\n", a / b); // 5 / 2 = 2
printf("浮点除法: %f\n", c / d); // 5.0 / 2.0 = 2.5
printf("混合除法: %f\n", (float)a / b); // 2.5
return 0;
}
3.2.3 自增自减运算符
#include <stdio.h>
int main()
{
int a = 10, b = 10;
printf("a++ = %d\n", a++); // 先使用后加: 输出10, a变11
printf("a = %d\n", a); // 11
printf("++b = %d\n", ++b); // 先加后使用: b变11,输出11
printf("b = %d\n", b); // 11
return 0;
}
3.3 关系运算符
| 运算符 | 说明 | 示例 | 结果 |
|---|---|---|---|
| == | 等于 | 5 == 3 | 0 (假) |
| != | 不等于 | 5 != 3 | 1 (真) |
| > | 大于 | 5 > 3 | 1 |
| < | 小于 | 5 < 3 | 0 |
| >= | 大于等于 | 5 >= 5 | 1 |
| <= | 小于等于 | 3 <= 5 | 1 |
#include <stdio.h>
int main()
{
int a = 5, b = 3;
printf("a == b: %d\n", a == b); // 0
printf("a != b: %d\n", a != b); // 1
printf("a > b: %d\n", a > b); // 1
printf("a < b: %d\n", a < b); // 0
return 0;
}
⚠️ 常见错误: 混淆=(赋值)和==(比较)
int a = 10;
if (a = 5) // 错误!这是赋值,不是比较
{
printf("a = %d\n", a); // 总是执行,输出5
}
if (a == 5) // 正确!这才是比较
{
printf("a equals 5\n");
}
3.4 逻辑运算符
| 运算符 | 说明 | 示例 | 结果 |
|---|---|---|---|
| && | 逻辑与(AND) | (5>3) && (2<4) | 1 |
| || | 逻辑或(OR) | (5<3) || (2<4) | 1 |
| ! | 逻辑非(NOT) | !(5>3) | 0 |
3.4.1 短路特性
逻辑与(&&)的短路:
#include <stdio.h>
int main()
{
int a = 5, b = 0;
// 当第一个条件为假时,不会计算第二个条件
if (b != 0 && a / b > 2)
{
printf("条件成立\n");
}
else
{
printf("条件不成立\n"); // 输出这个
}
return 0;
}
逻辑或(||)的短路:
#include <stdio.h>
int func1()
{
printf("func1被调用\n");
return 1;
}
int func2()
{
printf("func2被调用\n");
return 0;
}
int main()
{
if (func1() || func2()) // func1返回真,不会调用func2
{
printf("条件成立\n");
}
return 0;
}
输出:
func1被调用
条件成立
3.5 位运算符
3.5.1 位运算符列表
| 运算符 | 说明 | 示例 | 二进制示例 |
|---|---|---|---|
| & | 按位与 | 5 & 3 | 0101 & 0011 = 0001 |
| | | 按位或 | 5 | 3 | 0101 | 0011 = 0111 |
| ^ | 按位异或 | 5 ^ 3 | 0101 ^ 0011 = 0110 |
| ~ | 按位取反 | ~5 | ~0101 = 1010 |
| << | 左移 | 5 << 1 | 0101 << 1 = 1010 |
| >> | 右移 | 5 >> 1 | 0101 >> 1 = 0010 |
3.5.2 位运算基础
#include <stdio.h>
int main()
{
unsigned int a = 5; // 0000 0101
unsigned int b = 3; // 0000 0011
printf("a & b = %u\n", a & b); // 0001 = 1
printf("a | b = %u\n", a | b); // 0111 = 7
printf("a ^ b = %u\n", a ^ b); // 0110 = 6
printf("~a = %u\n", ~a); // 1111...1010
printf("a << 1 = %u\n", a << 1); // 1010 = 10
printf("a >> 1 = %u\n", a >> 1); // 0010 = 2
return 0;
}
3.5.3 位运算的应用
1. 将第n位置1
int setBit(int num, int n)
{
return num | (1 << n);
}
int main()
{
int num = 0; // 0000 0000
num = setBit(num, 2); // 0000 0100 = 4
printf("%d\n", num); // 4
return 0;
}
2. 将第n位置0
int clearBit(int num, int n)
{
return num & ~(1 << n);
}
int main()
{
int num = 7; // 0000 0111
num = clearBit(num, 1); // 0000 0101 = 5
printf("%d\n", num); // 5
return 0;
}
3. 测试第n位
int testBit(int num, int n)
{
return (num & (1 << n)) != 0;
}
int main()
{
int num = 5; // 0000 0101
printf("第0位: %d\n", testBit(num, 0)); // 1
printf("第1位: %d\n", testBit(num, 1)); // 0
printf("第2位: %d\n", testBit(num, 2)); // 1
return 0;
}
4. 交换两个变量(不使用临时变量)
#include <stdio.h>
int main()
{
int a = 5, b = 3;
printf("交换前: a=%d, b=%d\n", a, b);
a = a ^ b;
b = a ^ b; // b = (a^b)^b = a
a = a ^ b; // a = (a^b)^a = b
printf("交换后: a=%d, b=%d\n", a, b);
return 0;
}
3.6 赋值运算符
3.6.1 基本赋值
int a = 10; // 基本赋值
3.6.2 复合赋值运算符
| 运算符 | 等价形式 | 示例 |
|---|---|---|
| += | a = a + b | a += 3 |
| -= | a = a - b | a -= 3 |
| *= | a = a * b | a *= 3 |
| /= | a = a / b | a /= 3 |
| %= | a = a % b | a %= 3 |
| &= | a = a & b | a &= 3 |
| |= | a = a | b | a |= 3 |
| ^= | a = a ^ b | a ^= 3 |
| <<= | a = a << b | a <<= 3 |
| >>= | a = a >> b | a >>= 3 |
#include <stdio.h>
int main()
{
int a = 10;
a += 5; // a = a + 5 = 15
printf("a = %d\n", a);
a *= 2; // a = a * 2 = 30
printf("a = %d\n", a);
a /= 3; // a = a / 3 = 10
printf("a = %d\n", a);
return 0;
}
3.7 条件运算符(三目运算符)
语法
条件 ? 表达式1 : 表达式2
如果条件为真,返回表达式1的值;否则返回表达式2的值。
示例
#include <stdio.h>
int main()
{
int a = 10, b = 20;
int max;
max = (a > b) ? a : b;
printf("max = %d\n", max); // 20
// 等价于:
if (a > b)
max = a;
else
max = b;
return 0;
}
嵌套使用
#include <stdio.h>
int main()
{
int score = 85;
char grade;
grade = (score >= 90) ? 'A' :
(score >= 80) ? 'B' :
(score >= 70) ? 'C' :
(score >= 60) ? 'D' : 'F';
printf("成绩等级: %c\n", grade); // B
return 0;
}
3.8 其他运算符
3.8.1 sizeof运算符
sizeof(类型)
sizeof 表达式
#include <stdio.h>
int main()
{
int a = 10;
int arr[10];
printf("int: %lu\n", sizeof(int)); // 4
printf("a: %lu\n", sizeof(a)); // 4
printf("arr: %lu\n", sizeof(arr)); // 40
printf("arr[0]: %lu\n", sizeof(arr[0])); // 4
return 0;
}
3.8.2 逗号运算符
表达式1, 表达式2, ..., 表达式n
从左到右依次计算,返回最后一个表达式的值。
#include <stdio.h>
int main()
{
int a, b, c;
a = (b = 3, c = 5, b + c); // a = 8
printf("a = %d\n", a);
return 0;
}
3.8.3 取址运算符 &
int a = 10;
int *p = &a; // 取a的地址
3.8.4 解引用运算符 *
int a = 10;
int *p = &a;
printf("%d\n", *p); // 输出a的值:10
3.9 运算符优先级
优先级从高到低:
| 优先级 | 运算符 | 结合性 |
|---|---|---|
| 1 | () [] -> . | 左→右 |
| 2 | ! ~ ++ -- + - * & sizeof | 右→左 |
| 3 | * / % | 左→右 |
| 4 | + - | 左→右 |
| 5 | << >> | 左→右 |
| 6 | < <= > >= | 左→右 |
| 7 | == != | 左→右 |
| 8 | & | 左→右 |
| 9 | ^ | 左→右 |
| 10 | | | 左→右 |
| 11 | && | 左→右 |
| 12 | || | 左→右 |
| 13 | ?: | 右→左 |
| 14 | = += -= *= /= %= &= ^= |= <<= >>= | 右→左 |
| 15 | , | 左→右 |
优先级示例
#include <stdio.h>
int main()
{
int a = 5, b = 3, c = 2;
int result1 = a + b * c; // 5 + (3*2) = 11
int result2 = (a + b) * c; // (5+3) * 2 = 16
int result3 = a > b && b > c; // (5>3) && (3>2) = 1
printf("result1 = %d\n", result1);
printf("result2 = %d\n", result2);
printf("result3 = %d\n", result3);
return 0;
}
3.10 实践练习
练习1: 位运算实现权限系统
#include <stdio.h>
#define READ (1 << 0) // 0001
#define WRITE (1 << 1) // 0010
#define EXECUTE (1 << 2) // 0100
int main()
{
int permission = 0;
// 添加权限
permission |= READ;
permission |= WRITE;
// 检查权限
if (permission & READ)
printf("有读权限\n");
if (permission & WRITE)
printf("有写权限\n");
if (permission & EXECUTE)
printf("有执行权限\n");
else
printf("无执行权限\n");
// 删除权限
permission &= ~WRITE;
printf("\n删除写权限后:\n");
if (permission & WRITE)
printf("有写权限\n");
else
printf("无写权限\n");
return 0;
}
练习2: 判断奇偶数
#include <stdio.h>
int main()
{
int num = 15;
// 使用位运算判断奇偶
if (num & 1)
printf("%d是奇数\n", num);
else
printf("%d是偶数\n", num);
return 0;
}
练习3: 计算2的幂次
#include <stdio.h>
int main()
{
int n = 10;
// 2^n = 1 << n
printf("2^%d = %d\n", n, 1 << n); // 1024
return 0;
}
3.11 常见错误
错误1: 赋值与比较混淆
int a = 10;
if (a = 5) // 错误!应该是 a == 5
{
printf("a = %d\n", a);
}
错误2: 逻辑运算符与位运算符混淆
int a = 5, b = 3;
// 逻辑与
if (a && b) // 正确:逻辑运算
printf("both are true\n");
// 位与
if (a & b) // 可能不是你想要的
printf("bitwise AND result is non-zero\n");
错误3: 除零错误
int a = 10, b = 0;
int c = a / b; // 运行时错误:除数为0
防御性编程:
if (b != 0)
{
int c = a / b;
}
else
{
printf("除数不能为0\n");
}
3.12 小结
本章学习了:
算术运算符(+, -, *, /, %, ++, --) 关系运算符(==, !=, <, >, <=, >=) 逻辑运算符(&&, ||, !)及其短路特性 位运算符(&, |, ^, ~, <<, >>)及其应用 赋值运算符(=, +=, -=等) 条件运算符(?😃 运算符优先级和结合性
重点提示:
- 注意区分
=(赋值)和==(比较) - 利用逻辑运算符的短路特性避免错误
- 位运算在底层编程和优化中非常有用
- 复杂表达式使用括号明确优先级
- 避免除零错误
上一章: 第02章 数据类型与变量下一章: 第04章 输入输出