算术运算符
算术运算符是 C 语言中最基础的运算符,用于执行数学计算。C 提供了加、减、乘、除、取模五种基本算术运算,以及正负号运算符。理解整数除法的截断行为和取模运算的定义,是避免数值计算错误的关键。
基本运算符
| 运算符 | 名称 | 示例 | 结果 |
|---|---|---|---|
+ | 加法 | 5 + 3 | 8 |
- | 减法 | 5 - 3 | 2 |
* | 乘法 | 5 * 3 | 15 |
/ | 除法 | 5 / 3 | 1(整数除法) |
% | 取模 | 5 % 3 | 2 |
+ | 正号 | +5 | 5 |
- | 负号 | -5 | -5 |
int a = 17, b = 5;
printf("%d + %d = %d\n", a, b, a + b); /* 22 */
printf("%d - %d = %d\n", a, b, a - b); /* 12 */
printf("%d * %d = %d\n", a, b, a * b); /* 85 */
printf("%d / %d = %d\n", a, b, a / b); /* 3 */
printf("%d %% %d = %d\n", a, b, a % b); /* 2 */
整数除法的截断
C99 规定整数除法向零截断(Truncate Toward Zero),即舍弃小数部分:
printf("%d\n", 17 / 5); /* 3 */
printf("%d\n", -17 / 5); /* -3(向零截断) */
printf("%d\n", 17 / -5); /* -3 */
printf("%d\n", -17 / -5); /* 3 */
C89 对负数的整数除法方向未定义(实现定义),C99 统一为向零截断。这是 C99 的重要改进之一。
浮点除法:只要操作数中有一个是浮点类型,结果就是浮点:
printf("%f\n", 17.0 / 5); /* 3.400000 */
printf("%f\n", 17 / 5.0); /* 3.400000 */
printf("%f\n", (double)17 / 5); /* 3.400000 */
常见错误:两个整数相除期望得到浮点结果:
int a = 5, b = 2;
double wrong = a / b; /* 整数除法:5/2 = 2,再转为 2.0 */
double right1 = (double)a / b; /* 5.0/2 = 2.5 */
double right2 = a / (double)b; /* 5/2.0 = 2.5 */
double right3 = 1.0 * a / b; /* 5.0/2 = 2.5 */
取模运算
取模运算符 % 只适用于整数类型,结果是除法的余数:
printf("%d\n", 17 % 5); /* 2 */
printf("%d\n", -17 % 5); /* -2(C99:余数符号与被除数相同) */
printf("%d\n", 17 % -5); /* 2 */
printf("%d\n", -17 % -5); /* -2 */
C99 规定:a / b 向零截断,a % b 满足 a == (a / b) * b + a % b。因此余数的符号与被除数(左操作数)相同。
常见应用:
/* 判断奇偶 */
if (n % 2 == 0)
printf("Even\n");
else
printf("Odd\n");
/* 循环计数器 */
for (int i = 0; i < 100; i++) {
if (i % 10 == 0)
printf("\n"); /* 每 10 个换行 */
printf("%d ", i);
}
/* 限制范围 */
int wrap = (index % 10 + 10) % 10; /* 确保结果在 0-9,即使 index 为负 */
溢出与回绕
有符号整数溢出是未定义行为:
int max = INT_MAX;
max = max + 1; /* 未定义行为! */
无符号整数溢出是定义的回绕行为:
unsigned int u = UINT_MAX;
u = u + 1; /* 回绕到 0 */
printf("%u\n", u); /* 0 */
运算符优先级
算术运算符的优先级(从高到低):
- 正号
+、负号-(一元) - 乘法
*、除法/、取模% - 加法
+、减法-(二元)
int result = 5 + 3 * 2; /* 5 + (3 * 2) = 11,不是 (5 + 3) * 2 = 16 */
int result2 = 10 - 5 + 2; /* (10 - 5) + 2 = 7,左结合 */
int result3 = 100 / 10 / 2; /* (100 / 10) / 2 = 5,左结合 */
不确定时,使用括号明确意图:
int result = (5 + 3) * 2; /* 16 */
常见错误
对浮点数使用取模:
double x = 5.5 % 2.0; /* 错误:% 只适用于整数 */
/* 浮点取模用 fmod */
#include <math.h>
double y = fmod(5.5, 2.0); /* 1.5 */
除以零:
int x = 5 / 0; /* 未定义行为!运行时可能崩溃 */
/* 安全做法 */
int divisor = 0;
int dividend = 5;
if (divisor != 0)
printf("%d\n", dividend / divisor);
else
printf("Cannot divide by zero\n");
整数溢出:
int a = 2000000000;
int b = 2000000000;
long long sum = a + b; /* 错误:a + b 先溢出,再赋值 */
long long correct = (long long)a + b; /* 正确:先提升为 long long */