🛡️

身份验证

答对题目即可进入,验证你是谁

剩余尝试次数:3

运算符 — 加减乘除与比较

C语言学习教程

运算符 — 加减乘除与比较

🌟 课前故事:你是一个”数据加工厂”的厂长

假设你是一家果汁加工厂的厂长。每天早晨,工人把新鲜水果(数据)搬进工厂,你的机器(运算符)开始工作:清洗、榨汁、过滤、装瓶(处理数据),最后产出果汁成品(结果)。

在C语言里,运算符就是你的”加工机器”:

数据(变量)是原材料,运算符是加工工具,结果就是产出。今天,我们来清点你的”工厂设备”,学习每一种运算符的用法。


📖 概念讲解:运算符是什么?

什么是运算符?

运算符(Operator)是告诉计算机执行特定数学运算、逻辑运算或操作的特殊符号。运算符作用的对象叫操作数(Operand)。

操作数      运算符      操作数
   ↓          ↓          ↓
  5          +          3        = 8
   ↑                     ↑        ↑
  左操作数              右操作数   结果

为什么需要运算符?

没有运算符,变量只是”存储盒”——数据躺在里面什么也做不了。有了运算符,程序才能:

C语言运算符家族(预览)

C语言的运算符非常丰富,今天重点学习最基本的五类:

┌──────────────────────────────────────────────┐
│              C语言运算符                      │
│                                              │
│  📐 算术运算符: +  -  *  /  %              │
│  📝 赋值运算符: =  +=  -=  *=  /=          │
│  🔄 自增自减:   ++  --                     │
│  ⚖️ 比较运算符: ==  !=  >  <  >=  <=       │
│  🧠 逻辑运算符: &&  ||  !                   │
│  ... (还有更多,后面学)                      │
└──────────────────────────────────────────────┘

📐 第一部分:算术运算符

五种基本算术运算

运算符名称示例结果说明
+加法5 + 38小学就会
-减法5 - 32小学就会
*乘法5 * 315注意:不是 ×!
/除法5 / 22整数除法会截断!
%取余(模)5 % 215除以2的余数

基本算术示例

#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);   // 10 + 3 = 13
    printf("a - b = %d\n", a - b);   // 10 - 3 = 7
    printf("a * b = %d\n", a * b);   // 10 * 3 = 30
    printf("a / b = %d\n", a / b);   // 10 / 3 = 3(注意!)
    printf("a %% b = %d\n", a % b);  // 10 % 3 = 1

    return 0;
}

注意: printf 中输出百分号 % 本身,要用 %%。所以 %d 是格式占位符,而 %% 才输出一个 % 字符。

⚠️ 整数除法的”大坑”

C语言中,两个整数相除,结果还是整数——小数部分被直接丢弃,不是四舍五入!

int a = 5, b = 2;

// 整数除法
int result1 = a / b;       // 5 / 2 = 2(不是2.5!)
float result2 = a / b;     // 5 / 2 = 2.000000(还是2!)

// 为什么都等于2?
// 因为 a 和 b 都是整数,所以先做整数除法得到 2
// 然后才把 2 转成 float(2.0)赋值给 result2
// 小数部分在除法时就已经丢失了!

正确做法——至少让一个操作数是浮点数:

float result3 = 5.0 / 2;      // 2.5 ✅  5.0是浮点数
float result4 = 5 / 2.0;      // 2.5 ✅  2.0是浮点数
float result5 = 5.0 / 2.0;    // 2.5 ✅  两个都是浮点数
float result6 = (float)5 / 2; // 2.5 ✅  强制转换5为浮点数

图解:整数除法 vs 浮点除法

整数除法:  5 / 2
┌──────────────────────┐
│  2  ← 商(小数丢弃) │
│ ───                  │
│ 2)5                  │
│   4                  │
│  ───                 │
│   1  ← 余数(丢弃)  │
└──────────────────────┘
结果 = 2

浮点除法:  5.0 / 2.0
┌──────────────────────┐
│  2.5 ← 保留完整结果  │
│ ─────                │
│ 2.0)5.0              │
│     4.0              │
│    ────              │
│     1.0              │
│     1.0              │
│    ────              │
│       0              │
└──────────────────────┘
结果 = 2.5

取余运算符 % 的妙用

取余是人类很早就掌握的运算——“10颗糖分给3个人,每人3颗,还剩1颗”。在编程中,取余有很多实用场景:

#include <stdio.h>

int main() {
    // 1. 判断奇偶数
    int num1 = 7, num2 = 8;
    printf("%d%s\n", num1, num1 % 2 == 0 ? "偶数" : "奇数"); // 奇数
    printf("%d%s\n", num2, num2 % 2 == 0 ? "偶数" : "奇数"); // 偶数

    // 2. 取个位数
    int n = 12345;
    printf("个位:%d\n", n % 10);        // 5
    printf("十位:%d\n", (n / 10) % 10); // 4
    printf("百位:%d\n", (n / 100) % 10);// 3

    // 3. 循环周期(0~N-1的循环)
    for (int i = 0; i < 10; i++) {
        printf("%d ", i % 3);  // 输出:0 1 2 0 1 2 0 1 2 0
    }
    printf("\n");

    // 4. 判断闰年(能被4整除但不能被100整除,或能被400整除)
    int year = 2024;
    if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
        printf("%d 是闰年\n", year);
    }

    return 0;
}

重要限制: % 运算符的两个操作数必须是整数,不能用于浮点数!

// ❌ 错误
float x = 5.0 % 2.0;  // error: invalid operands to binary %

// ✅ 正确
int x = 5 % 2;  // 1

📝 第二部分:赋值运算符

基本赋值 =

int x;
x = 10;       // 把 10 赋值给 x
x = x + 5;    // 先算 x+5=15,再赋值给 x(x 变成 15)

重要理解: = 不是数学中的”等于”,而是赋值——把右边的值放入左边的变量。

复合赋值运算符(缩写)

写法等价于含义
x += 5x = x + 5x 增加 5
x -= 5x = x - 5x 减少 5
x *= 2x = x * 2x 翻倍
x /= 3x = x / 3x 变为原来的 1/3
x %= 3x = x % 3x 变为 x 除以 3 的余数
#include <stdio.h>

int main() {
    int x = 10;

    x += 5;   printf("x += 5  → %d\n", x);  // 15
    x -= 3;   printf("x -= 3  → %d\n", x);  // 12
    x *= 2;   printf("x *= 2  → %d\n", x);  // 24
    x /= 5;   printf("x /= 5  → %d\n", x);  // 4
    x %= 3;   printf("x %%= 3 → %d\n", x);  // 1

    return 0;
}

为什么用复合赋值?

  1. 写起来更短
  2. 更清晰表达”在这个变量基础上修改”的意图
  3. 某些情况下效率更高(编译器优化)

🔄 第三部分:自增 ++ 和自减 --

加1和减1是最常见的操作之一,C语言为它们提供了专用运算符:

int i = 0;

i++;    // 后置自增:i 加 1,返回加之前的值
++i;    // 前置自增:i 加 1,返回加之后的值
i--;    // 后置自减:i 减 1,返回减之前的值
--i;    // 前置自减:i 减 1,返回减之后的值

前置 vs 后置:关键区别

#include <stdio.h>

int main() {
    int a, b, x;

    // === 后置自增:先用后加 ===
    x = 5;
    a = x++;    // ① 先把 x 的值(5)赋给 a  ② 然后 x 加 1 变成 6
    printf("后置: a = %d, x = %d\n", a, x);  // a=5, x=6

    // === 前置自增:先加后用 ===
    x = 5;
    b = ++x;    // ① 先让 x 加 1 变成 6  ② 然后把 x 的值(6)赋给 b
    printf("前置: b = %d, x = %d\n", b, x);  // b=6, x=6

    return 0;
}

记忆口诀:

图解:b = x++ vs b = ++x

x = 5                      x = 5
                           
b = x++;                   b = ++x;
                           
步骤1: b = x  (b=5)       步骤1: x = x+1  (x=6)
步骤2: x = x+1 (x=6)      步骤2: b = x    (b=6)

结果: b=5, x=6            结果: b=6, x=6
     ↑  ↑                      ↑  ↑
     先 后                     先 后
     用 加                     加 用

自增自减的典型使用场景

// 场景1:循环计数(最常用)
for (int i = 0; i < 10; i++) {  // i++:每轮循环后+1
    printf("%d ", i);
}

// 场景2:数组遍历
int arr[] = {1, 2, 3, 4, 5};
int idx = 0;
while (idx < 5) {
    printf("%d ", arr[idx++]);  // 先取arr[idx],然后idx+1
}

// 场景3:计数统计
int count = 0;
count++;  // 找到一个,计数+1
count++;  // 又找到一个,再+1

⚠️ 自增自减的陷阱

// ❌ 混乱代码(不要这样写!)
int x = 5;
int y = ++x + x++;   // 结果不确定!不同编译器可能不同!
printf("%d\n", y);

// ❌ 多次修改同一个变量
int a = 1;
printf("%d %d %d\n", a++, a++, a++); // 结果不确定!

// ✅ 清晰代码
int x = 5;
x++;                  // x 现在是 6
int y = x;            // y 也是 6
printf("%d\n", y);

原则: 一条语句中对同一个变量只使用一次自增/自减。让代码清晰比”炫技”重要100倍。


⚖️ 第四部分:比较运算符(关系运算符)

比较运算符用来比较两个值的大小关系,结果是一个”真假”值。

运算符含义示例结果
==等于5 == 30(假)
!=不等于5 != 31(真)
>大于5 > 31(真)
<小于5 < 30(假)
>=大于等于5 >= 51(真)
<=小于等于5 <= 30(假)

在C语言中:

#include <stdio.h>

int main() {
    int a = 10, b = 20;

    printf("a = %d, b = %d\n\n", a, b);
    printf("a == b : %d\n", a == b);  // 0 (10不等于20)
    printf("a != b : %d\n", a != b);  // 1 (10确实不等于20)
    printf("a > b  : %d\n", a > b);   // 0 (10不大于20)
    printf("a < b  : %d\n", a < b);   // 1 (10小于20)
    printf("a >= b : %d\n", a >= b);  // 0 (10不大于等于20)
    printf("a <= b : %d\n", a <= b);  // 1 (10小于等于20)

    return 0;
}

🔴 == 不是 =!这是新手最致命的错误!

// ❌ 经典错误
int x = 5;
if (x = 10) {           // 注意:这是一个等号!
    printf("永远会执行\n");  // 因为 x=10 是赋值,结果是10(非0=真)
}
// 这段代码不仅条件永远成立,还把 x 的值改成了 10!

// ✅ 正确写法
int x = 5;
if (x == 10) {          // 两个等号才是比较!
    printf("x 等于 10 时才会执行\n");
}

怎么记住? 一个等号 =赋值(把右边放入左边),两个等号 ==比较(左边等于右边吗?)。

小技巧: 写比较时把常量放左边——if (10 == x)。如果误写成 if (10 = x),编译器会报错(因为不能给常量赋值),帮你”提前发现”!

比较运算符的使用场景

#include <stdio.h>

int main() {
    int age = 20;
    float score = 85.5f;

    // 场景1:年龄判断
    if (age >= 18) {
        printf("已成年\n");
    }

    // 场景2:成绩判断
    if (score >= 60.0f && score <= 100.0f) {
        printf("成绩有效\n");
    } else if (score < 0.0f || score > 100.0f) {
        printf("成绩无效\n");
    }

    // 场景3:精确匹配
    int answer = 42;
    if (answer == 42) {
        printf("答对了!\n");
    }

    // 场景4:范围判断
    int year = 2024;
    if (year >= 2000 && year <= 2099) {
        printf("21世纪\n");
    }

    return 0;
}

🧠 第五部分:逻辑运算符

逻辑运算符用来组合多个条件,实现复杂的”与或非”逻辑。

运算符名称含义真值表
&&逻辑与 (AND)两边都真才真T&&T=T, T&&F=F, F&&T=F, F&&F=F
||逻辑或 (OR)一边真就真T||T=T, T||F=T, F||T=T, F||F=F
!逻辑非 (NOT)取反!T=F, !F=T

逻辑与 && —— 全真才真

// 示例:判断一个数是否在 0~100 之间
int score = 85;

if (score >= 0 && score <= 100) {
    printf("成绩有效\n");
}
// score >= 0  → 真(85 >= 0)
// score <= 100 → 真(85 <= 100)
// 真 && 真 → 真 ✓
           && 真值表
    ┌──────────────────┐
    │  &&  │ 真 │ 假  │
    ├──────┼────┼─────┤
    │ 真   │ 真 │ 假  │
    │ 假   │ 假 │ 假  │
    └──────────────────┘
    
    记忆:&& 像一个严格的双重检查——
         两个条件都通过才算通过

逻辑或 || —— 有真就真

// 示例:判断一个数是否在 0~100 范围之外
int score = -5;

if (score < 0 || score > 100) {
    printf("成绩无效\n");
}
// score < 0   → 真(-5 < 0)
// score > 100 → 假(-5 > 100 不成立)
// 真 || 假 → 真 ✓
           || 真值表
    ┌──────────────────┐
    │  ||  │ 真 │ 假  │
    ├──────┼────┼─────┤
    │ 真   │ 真 │ 真  │
    │ 假   │ 真 │ 假  │
    └──────────────────┘
    
    记忆:|| 像一个宽松的选择——
         只要满足一个条件就通过

逻辑非 ! —— 颠倒黑白

int ready = 0;            // 0 表示"假"(没准备好)

if (!ready) {             // !0 = 真("没准备好"这个条件成立了)
    printf("还没准备好\n");
}

int logged_in = 1;        // 非0 表示"真"(已登录)

if (!logged_in) {         // !1 = 假("未登录"这个条件不成立)
    printf("请先登录\n"); // 这行不会执行
}

短路计算(重要概念)

&&|| 有一个重要的特性——短路计算

// 短路与 (&&):如果左边是假,右边根本不会执行
int a = 5;
if (a > 10 && a++ == 6) {
    printf("不会执行\n");
}
printf("a = %d\n", a);  // a = 5(a++ 没有被执行!)

// 短路或 (||):如果左边是真,右边根本不会执行
int b = 15;
if (b > 10 || b++ == 16) {
    printf("会执行\n");
}
printf("b = %d\n", b);  // b = 15(b++ 没有被执行!)

短路计算的价值:

// 安全地检查:先确保分母不为0,再做除法
int x = 10, y = 0;
if (y != 0 && x / y > 5) {  // 因为 y!=0 是假,x/y 根本不会计算,避免除以0崩溃
    printf("比值大于5\n");
}

📊 运算符优先级

当多种运算符一起使用时,有严格的先后顺序:

优先级从高到低(同级从左到右):

  ()       圆括号(优先级最高,有括号先算括号)

  ! ++ --  逻辑非、自增、自减(从右到左)

  * / %    乘、除、取余

  + -      加、减

  < > <= >= 比较运算符

  == !=    等于、不等于

  &&       逻辑与

  ||       逻辑或

  = += -=  赋值(优先级最低,从右到左)

优先级示例

// 没有括号时
int result1 = 5 + 3 * 2;      // 5 + (3*2) = 5 + 6 = 11
                               // 因为 * 优先级高于 +

int result2 = 10 - 4 / 2;     // 10 - (4/2) = 10 - 2 = 8
                               // 因为 / 优先级高于 -

// 比较和算术混用时
int result3 = 5 + 3 > 7;      // (5+3) > 7 = 8 > 7 = 1(真)
                               // 因为 + 优先级高于 >

// 逻辑和比较混用时
int result4 = 5 > 3 && 2 < 4; // (5>3) && (2<4) = 1 && 1 = 1(真)
                               // 因为 比较 优先级高于 &&

记不住优先级怎么办?——加括号!

// ❌ 让人困惑的代码
int result = a + b > c && d || e;

// ✅ 加括号后一目了然
int result = ((a + b) > c) && d || e;

// 🏆 最佳实践:不依赖优先级,用括号明确你的意图
int result = ((a + b) > c) && (d || e);

括号三原则:

  1. 不确定优先级时——加括号
  2. 优先级太多层时——加括号
  3. 想让别人一眼看懂时——加括号

多加括号不会影响程序性能,只会让代码更清晰。——编译器会帮你优化掉的。


⚠️ 常见错误(5个经典”坑”)

错误1:=== 搞混(再强调一遍!)

// ❌ 错误代码
int score = 85;
if (score = 100) {         // 赋值!score 被改成 100,条件恒为真
    printf("满分!\n");    // 永远会输出
}
printf("%d\n", score);     // 100(值被偷偷改了!)

// ✅ 正确代码
int score = 85;
if (score == 100) {        // 比较!
    printf("满分!\n");    // 只有真的是100分才输出
}

错误2:整数除法截断

// ❌ 错误代码
float result = 5 / 2;      // result = 2.000000(小数部分丢了!)
printf("%f\n", result);    // 用户困惑:为什么不是 2.5?

// ✅ 正确代码
float result = 5.0 / 2;    // result = 2.500000
// 或者
float result = (float)5 / 2;  // 强制转换

错误3:取余用错类型

// ❌ 错误代码
float x = 5.0f;
int r = x % 2;             // 编译错误:% 不能用于浮点数

// ✅ 正确代码
int x = 5;
int r = x % 2;             // ✅ 两个操作数都是整数

错误4:逻辑运算的括号遗漏

// ❌ 错误代码——意图:判断 x 在 10 到 20 之间
int x = 15;
if (10 < x < 20) {         // 你以为在判断 10 < x < 20
    printf("在范围内\n");
}
// 实际执行:(10 < x) < 20
//          = (1) < 20      ← 因为 10<15 结果为真(1)
//          = 1             ← 1<20 为真,所以恒为真!

// ✅ 正确代码
int x = 15;
if (x > 10 && x < 20) {    // 分开写,用 && 连接
    printf("在范围内\n");
}

错误5:混淆 &&&(及 |||

// & 和 | 是"位运算符"(对二进制位操作)——后面学
// && 和 || 是"逻辑运算符"(判断真假)

// ❌ 容易混淆
if (a > 5 & b < 10)    // & 是位运算,虽然这里"碰巧也管用"但不是正确意图
if (a > 5 && b < 10)   // && 才是逻辑与,表达"两个条件都满足"

// 初学者建议:始终用 && 和 || 做逻辑判断

💡 练习建议

初级练习

  1. 四则运算:输入两个整数,输出它们的和、差、积、商、余数
  2. 判断奇偶:输入一个整数,判断它是奇数还是偶数
  3. 成绩分级:输入成绩(0-100),判断是否及格(>=60)

中级练习

  1. 闰年判断:输入一个年份,判断是否是闰年
  2. 三位数拆分:输入一个三位数,分别输出百位、十位、个位数字
  3. 三角形判断:输入三个边长,判断能否构成三角形(任意两边之和大于第三边)

进阶思考

  1. 运算符优先级探索:不查表,猜猜 2 + 3 * 4 - 6 / 2 的结果是多少,然后写代码验证
  2. 短路实验:设计实验验证 &&|| 的短路行为
  3. 交换算法:不用第三个变量,交换两个 int 变量的值(提示:用加减法或异或)

📝 要点速查清单


🔗 相关主题