文/一个把编译器的全部错误信息都见过一遍的倒霉蛋
每个人写第一个Hello World的时候,都以为自己会一次成功。
手指颤抖着敲完代码,满怀期待地按下运行键,然后——屏幕上跳出一行红色的报错。
那一瞬间的感觉,像你在聚光灯下准备发表演讲,结果刚开口话筒就发出刺耳的啸叫。整个机房的人可能都在看着你,你盯着那行英文,脑子里一片空白,每一个单词都认识,连在一起完全不知道在说什么。
我自己的第一次就是这样。大学第一堂C语言课,老师在讲台上演示了一遍,说”大家自己试一下”。我照着黑板上的代码逐字敲完,编译,报错。报错信息是undefined reference to ‘main’。我看了一眼黑板,明明有main啊,写在第三行呢。
后来旁边的同学凑过来一看,说:”你文件名后面加了个.c吗?”
我:”还要加.c?”
然后我改了文件名,重新编译,这次过了。屏幕上终于出现了”Hello, World!”。但我盯着那个报错信息看了好久,心里想的不是”我终于成功了”,而是”为什么刚才那个错误要告诉我说’undefined reference’,它明明看到了我的main函数,只是因为我没把文件名后缀写对而已”。
这个疑问我后来想了很多年才明白。编译器的报错信息,从来就不是给你这个”人”看的。它是给另外一个程序看的——链接器。而在那个程序的世界里,找不到main就是一个”未定义引用”,它不管你文件名是啥。
那些年,我被Hello World骗过的各种坑
后来我发现,我不是一个人。几乎每一个初学者第一次写Hello World都会遇到各种各样的错误。我在网上搜集过很多人的”第一次报错经历”,五花八门得可以出一本书。
有人把print打成了pritn,然后Python报错说NameError: name ‘pritn’ is not defined。他说他当时对着这个报错看了五分钟,心想”我的电脑是不是不认识pritn这个单词”。
有人把”Hello, World!”的双引号打成了中文的“”,然后编译器报了一长串他完全看不懂的语法错误。他说他当时差点放弃了,觉得编程太难了,连括号都分中英文。
有人把代码文件保存成了.txt,然后怎么编译都说找不到文件。他以为是自己命令输错了,把gcc hello.c改成了各种变体,甚至尝试了gcc hello、gcc hello.txt、gcc “hello world”,折腾了半小时才发现windows的文件夹选项里”隐藏已知文件类型的扩展名”这个勾要取消掉。
还有人更离谱,把printf写成了print,然后C编译器报错implicit declaration of function ‘print’,他说他查了半天这个”implicit declaration”是什么意思,最后发现是自己把函数名字拼错了。
现在回头看这些错误,每一个都蠢得可爱。但在当时,每一个都像一个巨大的障碍。你不知道自己做错了什么,报错信息用的是你不熟悉的语言,描述的是你不熟悉的概念,而你唯一知道的事情就是——你只是想输出一行”Hello, World!”,而已。
那些报错,其实是在教你两件重要的事
现在回想起来,那些红色的报错信息,其实在教给我两件比”怎么正确打印Hello World”重要得多的事。
第一件事:计算机是个极端老实的东西。
它不会因为你很努力就放你一马,不会因为你是初学者就多给你一些宽容,也不会看你快哭了就忽略掉那个多余的标点。它什么都不管。你给它什么,它就处理什么。你少了一个分号,它就告诉你”语法错误”。它不会帮你补上,不会猜测你的意图,不会说”算了,这次就这样吧”。
这种”老实”一开始让人抓狂,但后来你会慢慢喜欢上它。因为这意味着一个确定的世界——你的代码是错的,它就会告诉你错了。你的代码是对的,它就一定会正确运行。没有任何”也许””大概””可能”。这种确定性在现实世界中太稀缺了,而编程世界提供了它。
第二件事:报错信息是你最好的老师。
我当程序员这么多年,最涨功力的时期从来不是”一切顺利”的时候。全是”出了个bug,死活找不到原因,最后花了三个小时才解决”的时候。每一次你被一个问题卡住、翻文档、查Stack Overflow、反复试验、最后终于搞明白的那一刻,你的能力是肉眼可见地在往上走的。
Hello World的那次报错,是你整个编程生涯里最小最轻的”被卡住”的经历。但那个经历里埋着所有”解决bug”的底层逻辑:读报错→理解它说的意思→定位问题→尝试修复→验证。这套流程你从第一行代码就开始练习了,后面的每一个bug,不管是多复杂的、多严重的、多隐蔽的,它的解决方法都脱胎于这个流程。
如果你第一次写就成功了,反而可能是遗憾
我有时候会想:如果我的第一次Hello World一次就成功了,没有报错,没有折腾,没有那些蠢到家的错误和令人抓狂的报错信息——那我现在对编程的理解,会不会不一样?
我觉得会。
如果没有那次失败,我可能永远不知道文件后缀名是干什么的。可能永远不明白编译和链接是两个阶段。可能永远体会不到那种”从错误中找到答案”的成就感。我可能只会把编程当作一个”写对了就能跑”的简单工具,而不会意识到它背后有一整套复杂的系统在支撑着那看似简单的一行输出。
所以我后来跟所有初学编程的人都说同一句话:第一次跑不出来是好事。
跑不出来,你才会仔细去看报错信息。看了报错信息,你才会去了解”undefined reference”是什么意思。了解了这个概念,你就开始接触到编译原理的入口了。这个入口会把你引向链接器、加载器、内存布局、目标文件格式……
这一切的开始,就是因为你第一次写Hello World的时候,忘了给文件名加一个点c。
报错里的那些美好瞬间
我收集过很多关于”第一次报错”的故事,有些特别温暖。
有个女生跟我说,她第一次写Hello World报错了,报错信息是IndentationError: unexpected indent。她完全不懂缩进是什么意思,截图发到了班级群里。然后群里十几个同学同时回复了”把行首的空格删掉”。她说她删掉空格重新运行,程序通过了,但更让她开心的是那十几条同时弹出的回复。
有个大叔说,他第一次写Hello World是在一个线下编程工作坊里。他报了错,旁边的志愿者老师过来帮他看,指着他代码里的一个中文括号说”改成英文的就好”。他改了,重新运行,成功。然后那位老师在他笔记本上写了一句话:”记住这个括号,它会陪你很多年。”他到现在都留着那页纸。
还有个程序员说,他第一次写Hello World报错报得特别离谱——电脑直接蓝屏了。后来发现是操作系统的问题,跟他的代码没关系。但他因此被同学笑了好几年,提起就是”你当年可是把Windows写蓝屏的男人”。他说这个外号他现在还在用,技术分享的自我介绍里都会提一句。
你看,那些红色的报错,最终都变成了彩色的回忆。
所以你只管写,报错是正常的
如果你现在正要写自己的第一个Hello World,或者你已经写了但还没成功,我想告诉你的是:
那行红色的字,不是对你能力的否定,它是计算机在用它唯一的方式跟你说话——它说:”嘿,你这里有点问题,你看看是不是这个意思。”
你把它改了,再运行,它又说:”这里还有个小问题,你再看看。”
你改了,再运行,它还说。
直到有一天,它不说了。屏幕上干干净净地出现了那两行字。那一刻你会觉得之前所有的红字都值了。
而等到很久以后,你成了别人眼里的”老程序员”,再回头看那个第一次报错的晚上,你会发现让你记住的从来不是终于成功的那一瞬间。
让你记住的,是你对着那行红色的报错信息,皱着眉头、一个一个词查、一遍一遍试的那个自己。
那个自己,比任何一次成功的输出,都更值得被记住。