这篇文章觉对不是 AI 写的
坏的代码如何坏。
它们常常不是一眼就能看出问题,而是让人眼神游离、心口发紧,在注释和变量名之间来回打量,始终搞不清楚「这玩意到底在干嘛」。
一、坏的命名让人晕头转向
int x, y, z;
if(x) y += z;
你知道 x
是不是代表「游戏结束」?不知道。y
是不是得分?不知道。z
是不是行数、奖励值、满行计数器?还是不知道。
这样的代码就像一个没有标签的药瓶,服了可能治病,也可能直接去世。特别是对于一个月后要调这个 bug 的你自己。
坏命名例子:
temp
、flag
、cnt1
、cnt2
、val1
、val2
、posx
、pozy
map1
、map2
、q1
、q2
、p
、pp
、ppp
C
、C2
、C3
、CC
、CCC
(洛谷特别高发)
这些命名并没有犯错,但只要你忘了上下文,它们就像一只披着布的幽灵——你知道它在场,但你不知道它是谁。
二、结构混乱导致依赖地狱
for(int i = 23; i >= 0; i--) {if(full(i)) do_down(i);
}
这段代码单看没毛病。逻辑是「从下往上检查每一行,满了就往下压」。但实际运行时,i = 21
时那一行是不是已经被之前的 do_down(22)
改了?我们不知道。
这种 “后面代码依赖了前面代码修改的数据结构” 的模式最容易出问题。
改法是将操作“纯化”,用临时数组替代原数组写入。每个步骤都在自己的宇宙中运行,不踩别人脚。
三、重复代码藏 Bug
void moveUp() { ... }
void moveDown() { ... }
void moveLeft() { ... }
void moveRight() { ... }
四个方向写了四个函数,看起来清爽,但改一个忘一个。稍微加点规则,比如不能穿墙,你要记得改四处。
理想方式是抽象成一个 move(dx, dy)
,将方向变成参数控制。甚至进一步抽象成 (x + dx, y + dy)
向量,配上一个方向数组。
const int dx[4] = {-1, 0, 1, 0};
const int dy[4] = {0, -1, 0, 1};
循环统一逻辑,维护成本立刻降低。
四、没有封装,满地 if else
if(state == 1) doA();
else if(state == 2) doB();
else if(state == 3) doC();
...
当你的状态变得多、行为变得复杂之后,这种代码就像一个长满瘤子的章鱼——每一只触手都在乱动。
封装是解药,状态转移表或者 switch
配合函数指针,甚至用面向对象都能清晰表达。
五、一锅烩函数让人迷路
void process() {...if(...) { ... }for(...) { ... }if(...) { ... }for(...) { ... }...
}
函数太长、做太多事。你无法复用,无法测试,无法理解这个函数的意图。
拆成多个小函数,每个函数有清晰名字,是最基本的自救方式。
六、误导性的注释
// delete the last row
for(int i = 0; i < n; i++) {a[i] = a[i+1];
}
问题一:这段代码其实是把所有行往上移。
问题二:a[n]
越界没看到。
问题三:注释和行为不一致。
这类注释非但不帮忙,还在挖坑。
注释的原则是:「要么准确,要么闭嘴」。
七、魔法数字满天飞
for(int i = 0; i < 23; i++) { ... }
23 是啥?底部?高度?地图宽度?没人知道。
改成:
const int Bottom = 23;
for(int i = 0; i <= Bottom; i++) { ... }
这时候你看到“23”,知道它不是随便拍脑袋拍出来的。更重要的是,你不会在十几个地方反复写错。
八、奇怪缩写,让人做梦都想不明白
int cmpf;
bool spt;
unordered_map<int, pii> ct2sz;
看起来像密码。缩写是对自己方便,但对读者是迷宫。
用完整命名,哪怕长一点,读得懂比省几个字母更重要。
九、结构乱到无法测试
每次你想「调一下这里看看问题在哪」,但这个地方引用了另外一个函数,这个函数还依赖全局变量、还涉及别的模块,最后发现你根本无法 isolated test(隔离测试)一个模块。
写代码时不考虑「如何测试」,调 bug 就像在黑夜里找丢了的头发。
十、无条件 return 或 continue,偷懒导致的 bug
for(...) {if(fail) return;do_something();
}
你以为 return 后面什么都不重要了,但其实可能打断了整个流程。上一个输入的数据影响了下一个输入,比赛直接罚时。
这种「流程跳转」要极其小心,return、continue、break 都是潜藏炸弹。
所以说,好代码的反面不是“写得烂”,而是“写得让你自己都不敢碰”。你会发现每次改动都小心翼翼,生怕哪个 if 被炸飞,哪个变量名让你看错。写得像是埋雷,不像在种花。
我们都写过坏代码,但我们都能改,哪怕一点点变清晰,都是好事。写得让自己未来回头看也会觉得:“诶,还行嘛。”就已经很厉害了喵~