上周带一个刚转行写 C 的小同事,他写了个计算折扣比例的逻辑,结果算出来的比例全是 0。一问才知道,他写了个类似1 / 2这样的代码,以为能得到0.5。今天我们就来聊聊 C 语言里的算术运算符,特别是除法和求余里那些新手必踩的深坑。
聊聊算术运算符的底层脾气
C 语言里的加(+)、减(-)、乘(*)基本上跟我们在小学学到的数学算式没区别,最让人脑壳疼的是除法(/)和求余(%)。
首先说除法。C 语言在做除法时,会根据参与计算的数据类型来决定怎么算:
- 如果两个数都是整数,那算出来的结果也只能是整数。比如
5 / 2,在数学上是2.5,但 C 语言会粗暴地把小数点后面的部分直接掐掉,结果就是2。这不叫四舍五入,这叫截断。即使你写9 / 10,结果也是0。 - 如果参与计算的数里有一个是浮点数(小数),C 语言就会按照小数除法来算。比如
5.0 / 2或者5 / 2.0,得到的就是2.5。
再说求余。很多初学者搞不懂%这个符号是干嘛的,其实它就是求除法之后的余数。比如你有 7 颗糖果,分给 3 个小朋友,每人分到 2 颗,最后剩下 1 颗不够分了。在 C 语言里,7 % 3的结果就是那个剩下的1。
这里有一个硬性规定:求余运算符%的左右两边必须是整数。如果你用浮点数求余,编译器在编译时就会直接报错报错,根本通不过。
至于优先级,C 语言也是先乘除求余、后加减。如果优先级一样(比如除法和乘法同时出现),就按照从左到右的顺序来算。要是觉得记不准,最稳妥的办法就是直接套括号,括号的优先级是最高的。
上代码看看
我们用一个“组队打比赛”的例子来演示这几个运算符。假设我们有一批游戏玩家,需要按固定人数组队,我们来算算能组多少个队,剩下几个人,以及平均每队分到的资源:
#include <stdio.h> int main() { int total_players = 14; int team_limit = 4; // 1. 整数除法:计算能组成几个满员队伍 int teams = total_players / team_limit; // 2. 求余运算:计算不够组队、剩下的零头人数 int leftover = total_players % team_limit; // 3. 浮点数除法:计算如果平分虚拟资源,平均每队分多少 // 注意:这里把 14 写成了 14.0,以此强制触发小数除法 double avg_resources = 14.0 / team_limit; printf("总人数:%d 人,每队要求:%d 人\n", total_players, team_limit); printf("成功组建队伍数:%d 队\n", teams); printf("零头人数:%d 人\n", leftover); printf("平均每队分配资源:%.2f 个单位\n", avg_resources); return 0; }代码逐行拆解:
int teams = total_players / team_limit;: 因为total_players(14)和team_limit(4)都是int类型,所以14 / 4执行的是整数除法,舍弃小数部分,得到结果3。int leftover = total_players % team_limit;: 这里执行的是模运算,计算14除以4的余数。14减去3 * 4得到2。double avg_resources = 14.0 / team_limit;: 这里我故意写成了14.0。因为14.0是一个double类型的浮点数,当它与int类型的team_limit相除时,C 语言会自动把team_limit也临时转成浮点数参与计算,最后算出来的结果就是带小数的3.5。
新手必踩的两个大坑
如果你写算术运算,下面这两个报错或逻辑错误经常会让你崩溃。
踩坑注意:对小数使用求余符号,或者除数为 0 导致运行时崩溃。
错误代码示范:
#include <stdio.h> int main() { double value = 7.5; int divisor = 0; // 错误 1:对浮点数 value 进行求余计算 int rem = value % 2; // 错误 2:除数为 0 参与了除法运算 int result = 10 / divisor; printf("结果是:%d, %d\n", rem, result); return 0; }怎么修正:
第一个错误,编译器在扫描到value % 2时会直接罢工,报错提示类似 "invalid operands to binary %"(无效的二进制求余操作数)。这是因为%运算符从设计上就只能作用于整数。如果你非要对小数求余,得使用标准数学库<math.h>里的fmod函数,而不是用%。
第二个错误更致命。在数学里我们知道除数不能为 0,C 语言同样如此。如果除数是 0,比如上面的10 / divisor,编译器可能不会报错,但是在程序运行起来执行到这一行时,计算机会触发硬件层面的除零异常,导致你的程序没有任何提示直接崩溃闪退。在写除法之前,如果除数是用户输入的变量,必须做好防错检查,确认它不为 0 再进行除法。
留个小练习
纸上谈兵没用,自己动手写一下这段代码,算算时间。
- 挑战题目: 编写一个 C 程序,声明一个代表总分钟数的整型变量(比如
145分钟)。通过除法和求余运算,计算出这堆时间相当于几个小时零几分钟,并把结果规范地打印出来。比如145分钟应该输出2 小时 25 分钟。
参考答案与思路:
我们知道 1 小时等于 60 分钟。要算小时数,就用总分钟数除以 60,利用整数除法的截断特性拿到整数小时;要算零头分钟数,就用总分钟数对 60 求余,拿到的余数就是不够一小时的分钟数。
#include <stdio.h> int main() { int total_minutes = 145; // 计算整小时数 int hours = total_minutes / 60; // 计算剩下的分钟数 int minutes = total_minutes % 60; printf("%d 分钟相当于:%d 小时 %d 分钟\n", total_minutes, hours, minutes); return 0; }直接把代码复制到你的编译器里跑一遍,自己修改total_minutes的值,看看输出的结果是不是完全符合预期。