你见过的最想笑的,最奇葩的,最逗逼的代码是什么?

如题,什么语言的代码都行
关注者
4,104
被浏览
2,407,208

206 个回答

我曾经任职的一家公司,规模不小,几百号人吧。拳头产品是一款MOBA游戏(及其衍生品)。这款游戏使用C++/lua开发,其中C++代码扮演的是引擎的角色,其他功能全部是使用lua实现的。这是我这辈子见过的最奇葩的代码库。它前后历经几百人写就,没有规约,没有架构,没有文档,没有单元测试,没有code review,没有profiling,没有分支却被五六个在它之上构建的项目使用。它依然屹立不倒,至今战斗在前线,支撑着一个年收入过亿的游戏。


它简直就是一个帕奇维克。

这个codebase的作者们在公司的官方定位是“脚本策划”(后改名为“脚本程序员”),在鼎盛时期,这个脚本策划群有40人之众。招聘时只对应聘者有最基本的程序基础要求,大概就是大学C语言的水平。这样的大背景把这个codebase变成了一个极好的工程范例,它是一本worst-practise的百科全书。里面的代码虽然不像很多答主所列举的那些精心设计的奇葩代码引人发笑,却有着深刻的现实意义。

  1. 整个codebase有30MB左右,其中至少10MB是手写的,剩下的是自动生成的。以下都只说手写的代码。
  2. 最大的一个文件(在我手上的版本,好几年了)有763KB,包含近10000行代码。
  3. 代码里有迷之全局变量a、b、c、d、aa、bb、cc、dd、aaa、bbb、ccc、ddd等等等等,据说是上古时期CTO写就的,变量的值也是从引擎传递出来的,所以没人敢改。至于它们究竟是什么含义,谁知道呢,反正没有文档,全靠口口相传。
  4. 各种用于测试互斥二态的API(或者说,应该返回布尔值的API),有的返回0/非0,有的返回nil/非nil,有的返回false/true……后来大家都搞不清楚了,所以普遍的做法是在调用这种API时把这三种情况都判断一遍(实际上对于lua来说只要判两种,因为if(nil)和if(false)是等价的)
  5. 用全局变量还是局部变量,主要看心情。命名空间/module是没有的,所有函数都是全局函数。有一次我随手看了一下,全局变量几万个吧(lua里函数也算变量)。
  6. 有一次,公司决定出国际版,要对整个游戏做英文本地化。但是游戏内的所有文本都是写死在脚本里的,于是派著名脚本策划W把整个脚本代码里的文本过一遍。W熬了好几个夜,终于完成了这一浩瀚的工程。后来在codebase里,每一处文本字符串后面都跟了一条注释:
    -- W到此一游
    一共3000多处。
  7. 成百上千行的函数比比皆是,其中的佼佼者,我随手用伪代码写个例子:
    function castSkill(skillId)
        if 不是普通攻击(skillId) then
            -- 释放技能的逻辑,几百行
            return
        end
    
        castNormalAttackSkill(skillId)
    end
    
    function castNormalAttackSkill(skillId)
        -- 普通攻击的逻辑,几百行
    end
    你知道当初看到castSkill函数的最后一句时,我的心情是崩溃的。
  8. 引擎端为脚本端开放了一个广受好评的技能广播API。具体思路是,脚本告诉引擎,我放了一个ID为XXX的技能,引擎再告诉服务器,服务器通知所有客户端,所有客户端的脚本端都会收到一个参数为XXX的回调。
    后来大家觉得这个API太好用了,于是开发商店功能时,开始用这个API来买东西。具体来说,我买了个蓝瓶,相当于放了一个ID为YYY的技能,其他客户端收到这个YYY的回调后,帮我的道具栏里加个蓝瓶。为了避免和技能起冲突,约定ID为10000以下的是技能,10000以上是买道具。
    后来,大家又心照不宣地使用这个API实现了诸多消息广播功能,并约定了一个复杂的ID分配表——还是那句话,没有文档,全靠口口相传。
  9. 你知道lua可以写面向对象代码吗?只不过需要自己实现继承结构,很简单,用metatable就可以。整个codebase里至少有三种不同的OO实现,以及相同数量的人分别使用过它们。剩下的所有代码都不是OO的。
  10. 何止是OOP,AOP这么先进的东西都有人能熟练应用。有一次发版本后感觉很卡,追查了一下代码,发现有人搞了点黑科技,由于涉及到太多lua的专有概念,所以我用伪代码描述一下:
    遍历所有全局变量,对于每个全局变量var:
      如果var是函数,且名称以UI_开头:
        将这个函数替换为一个新的函数,新函数先打个stacktrace到log里,然后执行原函数
    妥妥的AOP。你以为你调用的是引擎给你的API,其实早就被别人包装了不知道好几道了,底裤都给你看光了。而且像lua这种代码可以裸写在全局空间(甚至不需要包装为函数)的语言,这种包装神出鬼没,说不定藏在哪个犄角旮旯里呢。
  11. 一种被广泛使用的编码模式:
    function foo(arg)
        local defaultArg = {}
        -- 一大段初始化defaultArg的代码,几行到几十行不等
        arg = arg or defaultArg  -- 这句话的意思是,如果arg为空,把defaultArg赋值给arg
        -- ...
    end
    
    更新,答记者问:这里并不是说or的用法不对,而是arg不为空时,defaultArg的大段初始化代码都白白执行了。
  12. 另一种被广泛使用的模式:
    for i = 1, 9, 1 do  -- for(var i = 1; i <= 9; i+=1)
        if i == 1 then
             -- ....
        end
        if i == 2 then
             -- ....
        end
        -- ...
    end


  13. -- 08年12月9日注释,09年12月9日之后看到这儿的兄弟请帮忙删除掉
    -- (被注释的大段代码)


  14. function ChangeKeytoString(i)
        if i == 39 then
            return "A"
        elseif i == 40 then
            return "B"
        -- ...
        end
    end


  15. if target_id == 9999 then
        target_id = -1
    elseif target_id == 9998 then
        target_id = -5
    end
    

    -1是什么,-5又是什么?好奇心害死猫,要么舔着脸去问这里资历最老的人(他也不一定记得),要么准备好咖啡,今天晚上就睡这儿了吧。
  16. 奇葩命名层出不穷,我就举一个最喜欢说的,有个技能叫吸血,在代码里的命名是suckBlood。
    更新,答记者问:比较好的命名应该是life drain(生命汲取,“吸”其实更像是“汲”的通假字,并不是代表吮吸这个动作),或者如 @Zhifan Yang 所说:lol里面物理吸血叫life steal,法术吸血叫spell vamp

先写到这里……我只是随手翻了两个文件的代码而已……唉满满的都是回忆啊。

想不起来哪里看到的了。。

public static DateTime getTomorrowDate()
{
     Thread.Sleep(24*60*60*1000);
     return DateTime.Now;
}



==========================

评论里面有人说睡排序,也是神搞笑。

int main(int c, char **v)
{
  while (--c > 1 && !fork());
  sleep(c = atoi(v[c]));
  printf("%d\n", c);
  wait(0);
  return 0;
}

from wiki: http://rosettacode.org/wiki/Sorting_algorithms/Sleep_sort