第 3 章 运算符与表达式
本章目标
把“算数”这件事彻底搞明白: 加减乘除怎么算、为什么 / 会得到小数、idiv 和 % 怎么用、 比较与逻辑怎么写、位运算是什么、优先级怎么记。学完你能算价格、拆时间、做判断、写公式。
3.1 算术运算符
| 运算符 | 含义 | 例子 | 结果 |
|---|---|---|---|
+ | 加 (或字符串拼接) | 2 + 3 | 5 |
- | 减 | 10 - 4 | 6 |
* | 乘 | 6 * 7 | 42 |
/ | 浮点除法 | 7 / 2 | 3.5 |
% | 取余 (只支持整数) | 7 % 2 | 1 |
-x | 取负 | -5 | -5 |
3.2 最大的坑: / 永远是浮点除法
这是初学者最容易写错的地方。CIN 里 / 不做整数除法:
function main() -> int {
println("7 / 2 = " + (7 / 2)) // 3.5, 不是 3
println("idiv(7, 2) = " + idiv(7, 2)) // 3 整数商
println("7 % 2 = " + (7 % 2)) // 1 余数
println("idiv(-7, 2) = " + idiv(-7, 2)) // -3 向零截断
println("-7 % 2 = " + (-7 % 2)) // -1 余数符号跟被除数
println("idiv(7, -2) = " + idiv(7, -2)) // -3
return 0
}7 / 2 = 3.5
idiv(7, 2) = 3
7 % 2 = 1
idiv(-7, 2) = -3
-7 % 2 = -1
idiv(7, -2) = -3记忆口诀: 要小数用 /, 要整数商用 idiv(a, b), 要余数用 %。
把 / 的结果存进 int 会被截断
int q = 7 / 2 // 3.5 赋给 int -> 3 (截断)
int q2 = idiv(7, 2) // 3 (推荐写法, 意图明确)两者结果一样, 但 idiv 让你的意图一目了然, 也避免以后改动时踩坑。
3.3 浮点运算与取整
function main() -> int {
float a = 10.0 / 4.0
float b = 10 / 4 // 整数参与也按浮点算
println("a = " + a)
println("b = " + b)
float price = 19.9
int yuan = price // 截断取整: 19
println("yuan = " + yuan)
return 0
}a = 2.5
b = 2.5
yuan = 19想要“四舍五入到两位小数”这种效果, 用整数“分”来算最稳:
function main() -> int {
float price = 12.5
int qty = 7
float total = price * qty // 87.5
int cents = round(total * 100) // 8750 (分)
println("总价 = " + idiv(cents, 100) + "." + (cents % 100))
return 0
}总价 = 87.50注意:
cents % 100是50时显示.50, 但如果是5会显示.5。 想要固定两位需要补零 (见 转换库c_pad_int)。
3.4 比较运算符
| 运算符 | 含义 | 例子 | 结果 |
|---|---|---|---|
== | 等于 | 3 == 3 | true |
!= | 不等于 | 3 != 4 | true |
> < | 大于 / 小于 | 5 > 2 | true |
>= <= | 大于等于 / 小于等于 | 5 <= 5 | true |
字符串不能用 == 比内容
== 比较的是字符串的地址。两个内容相同的字符串可能是 false:
string a = "ab" + "c" // 运行时拼出来的新字符串
string b = "abc" // 源码里的字面量
println(a == b) // false (地址不同!)
println(strcmp(a, b) == 0) // true (按内容比较, 正确做法)false
true规则: 字符串一律用 strcmp(a, b) == 0 判断相等, strcmp(a, b) < 0 判断“字典序在前”。
3.5 逻辑运算符与短路求值
| 运算符 | 含义 | 说明 |
|---|---|---|
&& | 与 | 两边都真才为真 |
|| | 或 | 只要一边为真就为真 |
! | 非 | 真变假、假变真 |
短路: && 左边为假时右边不执行; || 左边为真时右边不执行。用带打印的函数能直接看到:
function side(int tag) -> bool {
println(" (求值 " + tag + ")")
return true
}
function main() -> int {
bool a = false && side(1) // 左边为假, 右边不求值
bool b = true || side(2) // 左边为真, 右边不求值
bool c = true && side(3) // 必须求值右边
println("a=" + a + " b=" + b + " c=" + c)
return 0
} (求值 3)
a=false b=true c=true短路是安全网
if (i < n && arr[i] > 0) —— 当 i >= n 时右边不会被求值, 因此不会去读越界的 arr[i]。 把“范围检查”写在左边是常用技巧。
3.6 位运算 (进阶, 可以先跳过)
位运算直接操作整数的二进制位, 在掩码、标志位、性能优化里常用:
| 运算符 | 名称 | 例子 | 结果 |
|---|---|---|---|
& | 按位与 | 0b1100 & 0b1010 | 8 |
| | 按位或 | 0b1100 | 0b1010 | 14 |
^ | 按位异或 | 0b1100 ^ 0b1010 | 6 |
~ | 按位取反 | ~0 | -1 |
<< | 左移 | 1 << 4 | 16 |
>> | 算术右移 | -16 >> 2 | -4 |
function main() -> int {
int a = 0b1100 & 0b1010
int b = 0b1100 | 0b1010
int c = 0b1100 ^ 0b1010
println("and=" + a + " or=" + b + " xor=" + c)
int d = 1 << 4
int e = -16 >> 2
int f = ~0
println("shl=" + d + " asr=" + e + " not=" + f)
return 0
}and=8 or=14 xor=6
shl=16 asr=-4 not=-10b1100是二进制字面量 (等于十进制的 12); 也可以用0x十六进制、0o八进制;<<左移一位相当于乘 2,>>右移一位相当于除以 2 (负数保留符号);- 位运算只能用于整数, 对
float使用会报错。
3.7 复合赋值与自增自减
function main() -> int {
int x = 10
x += 5 // 15
x -= 3 // 12
x *= 2 // 24
x /= 4 // 6 (浮点除法结果 6.0, 存入 int 截断)
println("x = " + x)
int y = 5
int pre = ++y // 先加 1, 表达式值是新值 6
int post = y-- // 表达式值是旧值 6, 然后 y 变成 5
println("pre=" + pre + " post=" + post + " y=" + y)
return 0
}x = 6
pre=6 post=6 y=5别在一行里做太多自增
x++ 和 ++x 单独成句时没有区别; 混在复杂表达式里会让代码难读, 建议一次只改一个变量。
3.8 三目运算符: 一行写完 if-else
语法: 条件 ? 条件为真时的值 : 条件为假时的值
function main() -> int {
int score = 72
println(score >= 60 ? "及格" : "不及格")
int x = -3
int abs_x = x < 0 ? -x : x
println("abs_x = " + abs_x)
return 0
}及格
abs_x = 33.9 优先级: 不确定就加括号
从高到低 (简化版):
| 优先级 | 运算符 |
|---|---|
| 最高 | () 括号、f() 调用、[] 下标、x++ x-- |
| ↑ | ! ~ -x ++x --x |
* / % | |
+ - (含字符串拼接) | |
<< >> | |
< > <= >= | |
== != | |
& | |
^ | |
| | |
&& | |
|| | |
| 最低 | ?: 与各类赋值 = += … |
function main() -> int {
println("2 + 3 * 4 = " + (2 + 3 * 4)) // 14
println("(2 + 3) * 4 = " + ((2 + 3) * 4)) // 20
println("10 - 4 - 3 = " + (10 - 4 - 3)) // 3 (同级从左到右)
println("2 + 3 == 5 -> " + (2 + 3 == 5)) // true
return 0
}2 + 3 * 4 = 14
(2 + 3) * 4 = 20
10 - 4 - 3 = 3
2 + 3 == 5 -> true完整优先级表见 运算符参考。
3.10 常用数学函数
内建函数 (不需要 import) 可以直接用:
function main() -> int {
println("sqrt(16) = " + sqrt(16)) // 4
println("pow(2, 10) = " + pow(2, 10)) // 1024
println("abs(-7) = " + abs(-7)) // 7
println("floor(3.7) = " + floor(3.7)) // 3 (向下取整)
println("ceil(3.2) = " + ceil(3.2)) // 4 (向上取整)
println("round(2.5) = " + round(2.5)) // 3 (四舍五入)
println("min(3, 9) = " + min(3, 9)) // 3
println("max(3, 9) = " + max(3, 9)) // 9
return 0
}sqrt(16) = 4
pow(2, 10) = 1024
abs(-7) = 7
floor(3.7) = 3
ceil(3.2) = 4
round(2.5) = 3
min(3, 9) = 3
max(3, 9) = 9数学函数取值域要自己检查
sqrt(-1) 这类非法输入在不同执行路径下表现不同 (实测): Go 原生路径打印 NaN, 纯 Python 路径直接报 Execution Error: expected a nonnegative input 并退出码 1。 调用前先自己判断, 例如 if (x >= 0) { ... sqrt(x) ... }, 这样两条路径行为一致。
3.11 常见错误
| 现象 | 原因 | 解决 |
|---|---|---|
想要整数却得到 3.5 | / 是浮点除法 | 用 idiv(a, b) |
Float modulo not supported | 对 float 用了 % | 先转成整数再取余 |
Bitwise operator ... requires integer operands | 对 float/string 用了位运算 | 位运算只用于整数 |
| 字符串比较结果不对 | 用了 == | 用 strcmp(a, b) == 0 |
| 拼接顺序不对 | + 是从左到右 | 用括号包住先算的部分 |
| 循环里越界崩溃 | && 两侧顺序写反 | 把范围检查放左边 (短路) |
3.12 练习
- 计算并打印
17 / 5、idiv(17, 5)、17 % 5三个结果, 解释差别。 - 把 89 分钟换算成“1 小时 29 分”的形式。
- 用三目运算符打印
n = 7是不是偶数。 - 给定
int flags = 0b1010, 判断第 3 位 (从 0 开始数) 是 0 还是 1 (提示:& (1 << 3))。 - 让用户体重 (kg) 与身高 (m) 固定为
68.5和1.75, 计算 BMI 并保留两位小数。
参考实现见 习题与答案 · 第 3 章。
3.13 本章小结
/是浮点除法, 整数商用idiv, 余数用%; 负数的%结果符号跟被除数;- 浮点别用
==, 用差值判断; 想要两位小数用“分”做整数运算; - 字符串比较用
strcmp, 不要用==; &&/||会短路, 把安全检查写在左边;- 位运算只用于整数; 优先级不确定就加括号;
- 数学函数返回值域非法时各路径表现可能不同, 自己先判断。
下一章: 条件分支。