1. 操作符概述
1.1 什么是操作符?
操作符(Operator)是C语言中用于执行各种操作的符号。它们是构成表达式的基本元素,告诉编译器对数据进行什么样的运算。
1.2 操作符的分类
分类 包含的操作符 优先级
算术操作符 + - * / % 较高
移位操作符 << >> 中等
位操作符 & \| ^ ~ 中等
赋值操作符 = += -= 等 较低
关系操作符 > < >= <= == != 较低
逻辑操作符 && \|\| ! 很低
条件操作符 ? : 很低
逗号操作符 , 最低
2. 算术操作符
2.1 基本算术操作符
#include <stdio.h>
int main() {
int a = 10, b = 3;
printf("a = %d, b = %d\n\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(" : (double)a / b = %.2f (浮点数除法)\n", (double)a / b);
// 取模(求余数)
printf("取模 (%%) : a %% b = %d (余数)\n", a % b);
return 0;
}
运行结果:
a = 10, b = 3
加法 (+) : a + b = 13
减法 (-) : a - b = 7
乘法 (*) : a * b = 30
除法 (/) : a / b = 3 (整数除法,截断小数部分)
: (double)a / b = 3.33 (浮点数除法)
取模 (%) : a % b = 1 (余数)
2.2 重要注意事项
// ⚠️ 注意1:整数除法和浮点数除法
int x = 5, y = 2;
float result1 = x / y; // result1 = 2.0 (先整数除法,再转浮点)
float result2 = (float)x / y; // result2 = 2.5 (正确的浮点数除法)
float result3 = x / (float)y; // result3 = 2.5
float result4 = 5.0 / 2; // result4 = 2.5
// ⚠️ 注意2:取模操作符的操作数必须是整数
int mod1 = 10 % 3; // ✅ 正确,结果是1
// int mod2 = 10 % 3.0; // ❌ 错误,取模不能用于浮点数
// ⚠️ 注意3:除数为0
// int div = 10 / 0; // ❌ 运行时错误,除法零错误
3. 移位操作符
移位操作符用于将二进制位向左或向右移动。
3.1 左移操作符 <<
#include <stdio.h>
int main() {
unsigned int num = 5; // 二进制:0000 0101
printf("原始数: %u, 二进制: 0000 0101\n", num);
printf("左移1位: %u << 1 = %u, 二进制: 0000 1010\n", num, num << 1);
printf("左移2位: %u << 2 = %u, 二进制: 0001 0100\n", num, num << 2);
printf("左移3位: %u << 3 = %u, 二进制: 0010 1000\n", num, num << 3);
// 左移相当于乘以2的幂
printf("\n左移n位相当于乘以2^n\n");
printf("5 << 1 = %d, 5 * 2 = %d\n", 5 << 1, 5 * 2);
printf("5 << 3 = %d, 5 * 8 = %d\n", 5 << 3, 5 * 8);
return 0;
}
3.2 右移操作符 >>
#include <stdio.h>
int main() {
unsigned int num = 20; // 二进制:0001 0100
printf("原始数: %u, 二进制: 0001 0100\n", num);
printf("右移1位: %u >> 1 = %u, 二进制: 0000 1010\n", num, num >> 1);
printf("右移2位: %u >> 2 = %u, 二进制: 0000 0101\n", num, num >> 2);
printf("右移3位: %u >> 3 = %u, 二进制: 0000 0010\n", num, num >> 3);
// 右移相当于除以2的幂
printf("\n右移n位相当于除以2^n\n");
printf("20 >> 1 = %d, 20 / 2 = %d\n", 20 >> 1, 20 / 2);
printf("20 >> 2 = %d, 20 / 4 = %d\n", 20 >> 2, 20 / 4);
return 0;
}
3.3 有符号数的右移(算术右移 vs 逻辑右移)
#include <stdio.h>
int main() {
// 有符号负数
int negative = -8; // 补码表示:1111 1000
printf("有符号负数右移(算术右移):\n");
printf("-8 >> 1 = %d\n", negative >> 1); // 结果为 -4
// 无符号数右移(逻辑右移)
unsigned int positive = 0x80000000;
printf("\n无符号数右移(逻辑右移):\n");
printf("0x80000000 >> 1 = 0x%x\n", positive >> 1);
return 0;
}
4. 位操作符
位操作符直接对整数的二进制位进行操作。
4.1 位操作符详解
#include <stdio.h>
void printBinary(unsigned int num) {
for (int i = 31; i >= 0; i--) {
printf("%d", (num >> i) & 1);
if (i % 4 == 0) printf(" ");
}
printf("\n");
}
int main() {
unsigned int a = 0b10101010; // 二进制:1010 1010 (170)
unsigned int b = 0b11001100; // 二进制:1100 1100 (204)
printf("a = %u, 二进制: ", a);
printBinary(a);
printf("b = %u, 二进制: ", b);
printBinary(b);
// 按位与 &
printf("\n按位与 (&):\n");
printf("a & b = %u, 二进制: ", a & b);
printBinary(a & b);
// 对应位都为1时结果为1,否则为0
// 按位或 |
printf("\n按位或 (|):\n");
printf("a | b = %u, 二进制: ", a | b);
printBinary(a | b);
// 对应位至少有一个为1时结果为1
// 按位异或 ^
printf("\n按位异或 (^):\n");
printf("a ^ b = %u, 二进制: ", a ^ b);
printBinary(a ^ b);
// 对应位不同时结果为1,相同时为0
// 按位取反 ~
printf("\n按位取反 (~):\n");
printf("~a = %u, 二进制: ", ~a);
printBinary(~a);
// 每一位取反,0变1,1变0
return 0;
}
4.2 位操作符的实用技巧
#include <stdio.h>
int main() {
unsigned int num = 0b10101010; // 170
// 1. 判断奇偶性(最低位为1是奇数)
if (num & 1) {
printf("%d 是奇数\n", num);
} else {
printf("%d 是偶数\n", num);
}
// 2. 获取第n位的值(n从0开始)
int n = 3;
int bit = (num >> n) & 1;
printf("第%d位的值是: %d\n", n, bit);
// 3. 设置第n位为1
num = num | (1 << n);
printf("设置第%d位后: %d\n", n, num);
// 4. 清除第n位(设置为0)
num = num & ~(1 << n);
printf("清除第%d位后: %d\n", n, num);
// 5. 翻转第n位
num = num ^ (1 << n);
printf("翻转第%d位后: %d\n", n, num);
// 6. 判断是否为2的幂
unsigned int power = 16;
if (power && !(power & (power - 1))) {
printf("%d 是2的幂\n", power);
} else {
printf("%d 不是2的幂\n", power);
}
return 0;
}
5. 赋值操作符
5.1 基本赋值
int a = 10; // 简单赋值
int b, c;
b = c = 20; // 链式赋值,从右向左执行
5.2 复合赋值操作符
#include <stdio.h>
int main() {
int x = 10;
printf("初始值: x = %d\n\n", x);
x += 5; // 等价于 x = x + 5
printf("x += 5 → x = %d\n", x);
x -= 3; // 等价于 x = x - 3
printf("x -= 3 → x = %d\n", x);
x *= 2; // 等价于 x = x * 2
printf("x *= 2 → x = %d\n", x);
x /= 4; // 等价于 x = x / 4
printf("x /= 4 → x = %d\n", x);
x %= 3; // 等价于 x = x % 3
printf("x %%= 3 → x = %d\n", x);
x <<= 2; // 等价于 x = x << 2
printf("x <<= 2 → x = %d\n", x);
x >>= 1; // 等价于 x = x >> 1
printf("x >>= 1 → x = %d\n", x);
x &= 3; // 等价于 x = x & 3
printf("x &= 3 → x = %d\n", x);
x |= 5; // 等价于 x = x | 5
printf("x |= 5 → x = %d\n", x);
x ^= 7; // 等价于 x = x ^ 7
printf("x ^= 7 → x = %d\n", x);
return 0;
}
6. 关系操作符
关系操作符用于比较两个值的大小关系,返回1(真)或0(假)。
#include <stdio.h>
int main() {
int a = 10, b = 20, c = 10;
printf("a = %d, b = %d, c = %d\n\n", a, b, c);
printf("a > b : %d\n", a > b); // 大于
printf("a < b : %d\n", a < b); // 小于
printf("a >= c : %d\n", a >= c); // 大于等于
printf("a <= c : %d\n", a <= c); // 小于等于
printf("a == c : %d\n", a == c); // 等于
printf("a != b : %d\n", a != b); // 不等于
// ⚠️ 常见错误:将 == 误写为 =
if (a = 5) { // 这不是比较,而是赋值!条件永远为真
printf("\n⚠️ 警告:这里误将 == 写成了 =\n");
}
// 正确写法
if (a == 5) {
printf("a 等于 5\n");
}
return 0;
}
7. 逻辑操作符
逻辑操作符用于组合多个条件表达式。
#include <stdio.h>
#include <stdbool.h>
int main() {
int age = 25;
bool hasLicense = true;
bool hasInsurance = false;
printf("年龄: %d\n", age);
printf("有驾照: %s\n", hasLicense ? "是" : "否");
printf("有保险: %s\n\n", hasInsurance ? "是" : "否");
// 逻辑与 (&&): 两个条件都为真时结果为真
if (age >= 18 && hasLicense) {
printf("✅ 可以驾驶(年龄>=18且有驾照)\n");
} else {
printf("❌ 不能驾驶\n");
}
// 逻辑或 (||): 至少一个条件为真时结果为真
if (hasLicense || hasInsurance) {
printf("✅ 至少满足一项要求\n");
} else {
printf("❌ 两项都不满足\n");
}
// 逻辑非 (!): 取反
if (!hasInsurance) {
printf("⚠️ 没有保险,请注意安全!\n");
}
// 短路求值特性
int x = 0;
if (x != 0 && 10 / x > 1) { // 短路:x != 0为假,不会执行10/x
printf("这行不会执行\n");
}
printf("短路求值避免了除零错误\n");
return 0;
}
短路求值详解
#include <stdio.h>
int test1() {
printf("test1 被调用\n");
return 0;
}
int test2() {
printf("test2 被调用\n");
return 1;
}
int main() {
printf("=== 逻辑与短路 ===\n");
if (test1() && test2()) {
// test1()返回0,test2()不会被调用
printf("条件为真\n");
} else {
printf("条件为假\n");
}
printf("\n=== 逻辑或短路 ===\n");
if (test2() || test1()) {
// test2()返回1,test1()不会被调用
printf("条件为真\n");
} else {
printf("条件为假\n");
}
return 0;
}
8. 条件操作符(三目操作符)
条件操作符 ? : 是C语言中唯一的三目操作符,可以简化if-else语句。
#include <stdio.h>
int main() {
int score = 85;
// 基本用法
char grade = (score >= 90) ? 'A' : (score >= 80) ? 'B' : (score >= 70) ? 'C' : 'F';
printf("分数: %d, 等级: %c\n", score, grade);
// 嵌套使用
int a = 10, b = 20;
int max = (a > b) ? a : b;
printf("最大值: %d\n", max);
// 在输出中使用
printf("a %s b\n", (a > b) ? "大于" : (a < b) ? "小于" : "等于");
// ⚠️ 注意:条件操作符不是if语句的完全替代品
// 以下情况使用if更清晰
int x = 5;
if (x > 0) {
printf("正数\n");
} else if (x < 0) {
printf("负数\n");
} else {
printf("零\n");
}
return 0;
}
9. 逗号操作符
逗号操作符用于将多个表达式组合成一个表达式,按从左到右的顺序执行,整个表达式的值是最后一个表达式的值。
#include <stdio.h>
int main() {
int a, b, c;
// 逗号操作符在for循环中的应用
printf("逗号操作符在for循环中:\n");
for (a = 0, b = 10; a < b; a++, b--) {
printf("a = %d, b = %d\n", a, b);
}
// 逗号操作符的值
printf("\n逗号操作符的值:\n");
int result = (a = 3, b = 5, c = a + b);
printf("(a=3, b=5, c=a+b) = %d\n", result);
// 在函数调用中的使用(注意区分)
// printf("%d, %d", (x, y), z); // 这里逗号是操作符
// printf("%d, %d", x, y); // 这里逗号是分隔符
return 0;
}
10. 操作符优先级和结合性
10.1 优先级表
#include <stdio.h>
int main() {
// 优先级决定了操作数的结合顺序
int a = 5, b = 10, c = 15;
// 乘除优先级高于加减
int result1 = a + b * c; // 等价于 a + (b * c)
int result2 = (a + b) * c; // 括号改变优先级
printf("a + b * c = %d\n", result1); // 5 + 150 = 155
printf("(a + b) * c = %d\n", result2); // 15 * 15 = 225
// 逻辑操作符优先级
int x = 1, y = 2, z = 3;
int result3 = x < y && y < z; // 等价于 (x < y) && (y < z)
printf("x < y && y < z = %d\n", result3);
// 常见优先级问题
int result4 = a > b ? a : b; // 等价于 (a > b) ? a : b
// int result5 = a > b ? a : b = 10; // 错误!优先级问题
return 0;
}
10.2 优先级完整列表(从高到低)
优先级 操作符 描述 结合性
1 () [] . -> 括号、下标、成员 左→右
2 ++ -- + - ! ~ * & (type) 单目操作符 右→左
3 * / % 乘除取模 左→右
4 + - 加减 左→右
5 << >> 移位 左→右
6 < <= > >= 关系 左→右
7 == != 相等性 左→右
8 & 按位与 左→右
9 ^ 按位异或 左→右
10 \| 按位或 左→右
11 && 逻辑与 左→右
12 \|\| 逻辑或 左→右
13 ?: 条件 右→左
14 = += -= 等 赋值 右→左
15 , 逗号 左→右
11. sizeof 操作符
sizeof 是一个编译时操作符,用于计算数据类型或变量所占的内存字节数。
#include <stdio.h>
int main() {
// 基本数据类型的大小
printf("=== 基本数据类型大小 ===\n");
printf("sizeof(char) = %zu 字节\n", sizeof(char));
printf("sizeof(short) = %zu 字节\n", sizeof(short));
printf("sizeof(int) = %zu 字节\n", sizeof(int));
printf("sizeof(long) = %zu 字节\n", sizeof(long));
printf("sizeof(long long)= %zu 字节\n", sizeof(long long));
printf("sizeof(float) = %zu 字节\n", sizeof(float));
printf("sizeof(double) = %zu 字节\n", sizeof(double));
// 变量的大小
int arr[10];
printf("\n=== 变量大小 ===\n");
printf("sizeof(arr) = %zu 字节\n", sizeof(arr));
printf("sizeof(arr[0]) = %zu 字节\n", sizeof(arr[0]));
printf("数组元素个数 = %zu\n", sizeof(arr) / sizeof(arr[0]));
// 指针的大小
int *ptr;
printf("\n=== 指针大小 ===\n");
printf("sizeof(ptr) = %zu 字节\n", sizeof(ptr));
// 字符串常量
printf("\n=== 字符串常量 ===\n");
printf("sizeof(\"hello\") = %zu (包括结尾的'\\0')\n", sizeof("hello"));
return 0;
}
12. 类型转换
12.1 隐式类型转换
#include <stdio.h>
int main() {
// 1. 算术转换:将较小类型提升为较大类型
int a = 10;
double b = 3.14;
double result = a + b; // a 自动转换为 double
printf("10 + 3.14 = %.2f\n", result);
// 2. 赋值转换
int x = 3.14; // double 转换为 int,小数部分截断
printf("int x = 3.14 → x = %d\n", x);
// 3. 函数参数传递
printf("sizeof('A') = %zu\n", sizeof('A')); // 字符常量被提升为 int
// 4. 整型提升
char c1 = 100, c2 = 200;
int sum = c1 + c2; // char 提升为 int 再计算
printf("100 + 200 = %d\n", sum);
return 0;
}
12.2 显式类型转换(强制类型转换)
#include <stdio.h>
int main() {
int a = 10, b = 3;
// 强制转换为浮点数进行精确除法
float result1 = (float)a / b;
printf("(float)10 / 3 = %.2f\n", result1);
// 指针类型转换
int num = 0x12345678;
unsigned char *ptr = (unsigned char*)#
printf("整数的字节表示(小端序):\n");
for (int i = 0; i < sizeof(num); i++) {
printf("字节%d: 0x%02x\n", i, ptr[i]);
}
// 避免数据丢失的警告
long long bigNum = 0x123456789ABCDEF0;
int smallNum = (int)bigNum; // 显式转换,可能丢失数据
printf("大数转小数: 0x%llx → 0x%x\n", bigNum, smallNum);
return 0;
}
13. 综合练习
练习1:位操作应用 - 简单加密
#include <stdio.h>
#include <string.h>
// 简单的异或加密
void xorEncrypt(char *data, char key, size_t len) {
for (size_t i = 0; i < len; i++) {
data[i] ^= key; // 异或操作
}
}
int main() {
char message[] = "Hello, World!";
char key = 0x5A; // 密钥
size_t len = strlen(message);
printf("原始消息: %s\n", message);
// 加密
xorEncrypt(message, key, len);
printf("加密后: ");
for (size_t i = 0; i < len; i++) {
printf("%02X ", (unsigned char)message[i]);
}
printf("\n");
// 解密(相同的操作)
xorEncrypt(message, key, len);
printf("解密后: %s\n", message);
return 0;
}
练习2:操作符优先级陷阱
#include <stdio.h>
int main() {
int a = 1, b = 2, c = 3;
// 陷阱1:逻辑与 vs 按位与
if (a & b) { // 按位与,结果为0
printf("a & b 为真\n");
} else {
printf("a & b 为假\n");
}
if (a && b) { // 逻辑与,结果为真
printf("a && b 为真\n");
}
// 陷阱2:赋值 vs 比较
int x = 5;
if (x = 10) { // 赋值操作,条件为真
printf("x = %d (这是赋值,不是比较)\n", x);
}
// 陷阱3:优先级导致的错误
int result = a + b > c ? a : b; // 等价于 (a + b > c) ? a : b
printf("result = %d\n", result);
return 0;
}
14. 常见错误和最佳实践
14.1 常见错误
// ❌ 错误1:混淆 = 和 ==
if (x = 5) { // 应该是 x == 5
// 这个条件永远为真
}
// ❌ 错误2:位操作符和逻辑操作符混淆
if (x & y) // 按位与
if (x && y) // 逻辑与
// ❌ 错误3:忽视优先级
int result = a << 1 + b; // 实际是 a << (1 + b),不是 (a << 1) + b
// ❌ 错误4:自增自运算符的副作用
int i = 0;
int arr[] = {1, 2, 3};
int idx = 0;
// arr[idx++] = arr[idx]; // 未定义行为
arr[idx] = arr[idx + 1]; // 正确写法
idx++;
14.2 最佳实践
// ✅ 1. 使用括号明确优先级
int result = (a << 1) + b; // 清晰明了
// ✅ 2. 避免使用逗号操作符(除了for循环)
// 不好:x = (a++, b++, a + b);
// 好:a++; b++; x = a + b;
// ✅ 3. 位操作时使用无符号类型
unsigned int flags = 0;
flags |= (1 << 3); // 设置第3位
// ✅ 4. 使用 sizeof 时注意类型
int arr[10];
int size = sizeof(arr) / sizeof(arr[0]); // 正确
// int size = sizeof(arr) / sizeof(int); // 也可以,但容易出错
// ✅ 5. 检查除零和取模运算
if (y != 0) {
result = x / y;
mod = x % y;
}
15. 总结
操作符类别 关键点 常见用途
算术 注意整数除法截断 数学计算
移位 左移乘2,右移除2 高效乘除运算、位标志操作
位操作 直接操作二进制位 标志位、权限控制、加密
赋值 复合赋值简洁高效 变量更新
关系 返回0或1 条件判断
逻辑 短路求值特性 复杂条件组合
条件 简化if-else 简单条件赋值
逗号 组合表达式 for循环
核心要点:
理解每个操作符的功能和优先级
使用括号明确运算顺序
区分 = 和 ==
注意整数除法和浮点数除法的区别
合理使用位操作提高效率
警惕未定义行为
学习建议:
· 多写代码实践,体会操作符的行为
· 使用 printf 输出中间结果验证理解
· 在复杂表达式中善用括号
· 阅读优秀代码,学习他人的写法
记住:掌握操作符是写出高效、正确C代码的基础!