C语言课开在十二月初,机房里的暖气片烧了两天才把室温烘到不冻手的程度。
白天还好,人坐在电脑前面手指动了就暖了。晚上最冷——人走了,暖气还在烧,但空荡荡的机房只有机器的嗡嗡声和暖气的咕噜声作伴,像两台拖拉机一唱一和。我坐在第三排靠窗的位置,手刚从军大衣口袋里抽出来的时候指头是僵的,敲第一个键的时候食指在键盘上弹了一下,键帽的塑料硬得像铁。
老师姓孙,四十出头,穿深蓝色羽绒服站在讲台上,投影仪把他的电脑屏幕打到白墙上——深蓝色的代码界面上亮着一行白色光标。他打字很快,两根手指在键盘上走,食指敲完中指跟上去,像在键盘上爬楼梯。黑板上写着今天的课题:C语言基础——从QBASIC到C。
"QBASIC是解释型语言,敲一行跑一行。C不是——C是编译型语言,你写完整个程序,编译,再运行。"他在黑板上写下两个词:解释,编译。粉笔字写得很快,字迹歪歪扭扭的,像他打字的节奏——快,但不讲究好看。"前者像翻译一句说一句。后者像写完一封信再寄出去。"
教室里一半人开始记笔记。另一半低着头——不是在记笔记,是在看手机或者发呆。孙老师不在意,他继续讲,语速不变,像暖气管里的水——匀速地走,不因为谁听谁不听就快一点或慢一点。
C语言对大部分同学来说是第一次接触编程,黑屏上的白字像天书。但对我来说不一样——我见过天书。QBASIC是天书的入门版,C是天书的进阶版。门已经推开过一次了,再推开一次不会更难。我坐在电脑前面,屏幕上的蓝色光打在脸上,手指放在键盘上的感觉像回到家——一种旧的、熟悉的、不需要重新适应的位置。
第一堂课讲变量和函数。int main() 花括号打开,花括号关闭。scanf读进去,printf写出来。和QBASIC的INPUT和PRINT是一个道理——名字变了,规矩变了,但水还是从高处往低处流。变量是容器,函数是管道,数据是水。输入是源头,输出是出水口。中间的计算就是管道里的弯头和阀门。
我的手在键盘上动得比脑子快。老师在讲台上讲for循环的时候我已经把管道流量计算器用C重写了一遍——二十三行代码,编译一次通过,没有报错。屏幕上.Output窗口弹出一行字:Pipe flow rate: 1.27 m³/s。
坐在旁边的张磊探头看了一眼我的屏幕。他看到输出结果的时候愣了一下。
"你以前学过?"
"学过一点。"
"啥语言?"
"QBASIC。"
他没再问了。他把老师的板书一个字一个字抄进笔记本里。笔记本很新,封面的塑封纸还没撕掉,书脊没有折痕。我的笔记本已经翻卷了边,给水工程那一章画了管网截面,C语言那一章画了代码流程图。
指针是第三周开始讲的。
孙老师在投影上画了一个方框,方框里写了一个数字5。然后他从方框画了一个箭头,箭头指向另一个方框,第二个方框里写的是5的地址——0x7fff5a3b。
"变量存的是值。指针存的是地址。你要找值,直接用变量名。你要找地址,用取址符&。你要从地址里取值,用解引用符*。"
全班安静了。安静不是听懂了的安静——是没听懂的安静。张磊的笔停在笔记本上不动了。前后左右的人互相看了一下,表情都是一样的:你要我找门牌号,我连路都不认识。
我盯着屏幕上的代码看了一遍。又看了一遍。
int a = 5;
int *p = &a;
printf("%d", *p);
第一行:a里存着5。第二行:p里存着a的地址。第三行:从p存的地址里把值取出来,打印。输出应该是5。
逻辑是对的。但在脑子里转不过来——为什么要把地址存起来?直接用a不就行了?
我做了一个实验。写了一段程序:指针指向指针指向指针。三级指针。编译,运行。屏幕上输出了一串地址,地址的地址,地址的地址的地址。每一层都指向下一层的门牌号,像一排房间,每间房里只有下一间的钥匙,最后一间房里才是真正的东西。
三层够了。再多层只是门牌号套门牌号。程序跑出来的结果是对的——终点指向5。地址只是一种找东西的方式。你要找的东西总在某个房间里放着。
然后我想到管网了——城市管网不也是这样吗?水源是源头,干管是一级指针,支管是二级指针,水龙头是三级指针,打开龙头取到的水才是值。管网就是一串地址。水从源头出发,经过干管、支管、立管、龙头——每一级都是一个指向下一级的门牌号。最末端的水龙头打开的时候,才取到真正的值。
我把这个想法写在了笔记本边上。没有画图,只写了四个字:管网即指针。
期末前的最后一个星期,我每天在机房待到十点。
机房晚上十点锁门。管理员是个五十多岁的老头,穿灰棉袄,拿一串钥匙在走廊里走,钥匙走得哗啦哗啦响。他来赶人的时候,整层楼只剩我一个人。他站在门口喊:"同学——走了!"屏幕上的光打在他脸上,他眯着眼睛看我的屏幕——上面是一段刚编译通过的代码,输出窗口显示:
Pipe diameter: 200mm
Flow velocity: 1.27 m/s
Pressure: 0.35 MPa
他看了两秒,说了句:"你们学生——天天对着这个不累?"
我说不累。这不是假话。编译通过的瞬间,屏幕上从红字错误变成一行正确的输出数字——那个感觉和投篮空心入网一样。球离开指尖的时候你知道它会进——代码编译通过的时候你也知道它会跑。这个确定感不需要任何人告诉你,你自己知道。
保温杯放在机箱上,水是中午灌的,到晚上已经凉了。我拧开杯盖喝一口——凉的白开水在嗓子里一路冷下去,和暖气片烤出来的干热撞在一起。机房里只有键盘的声音。我敲一行,删一行,再敲一行。空格键还是那块磨掉漆的空格键——不是这台电脑的,是网吧里那台旧机的空格键一直在记忆里。赵启明在高中网吧里帮我纠正指法的那个下午已经是四年前的事了,但空格键弹回来的手感是一样的——按下去,弹回来,咔嗒一声,像说了一句话然后闭嘴。
int main() 的大括号打开了,十几个函数像管道的节点一个一个排下去,每一个节点接收上一步的水,传给下一步。最后printf把水排在屏幕上——流速1.27。数字对了,程序对了。
陈默的电话是晚饭后打来的。
IC电话亭外面在下雪——不是南方那种湿漉漉落到地上就化的小雪,是北方那种一片一片飘下来、落在地上堆起来就不走的雪。电话亭的塑料门关不严,雪从缝隙里灌进来,落在我的袖子上化成水珠。脚下的雪已经积了两寸厚,踩上去嘎吱一声,靴印留在雪里,很快又被新雪盖住。
他的声音比上次更粗了。像铁锉磨粗铁板——不是打磨,是磨出一个更粗糙的表面。每一次通话他的声音都粗一层,像他手上又多了一层茧。
"我升了。组长。"
"管几个人?"
"五个。工资七百。"
七百。比六百多了一百。我在心里算了一下——他弟弟的学费、他母亲的药费、他自己的饭钱。七百块在东莞很紧。
"你写那个程序还有没有用?"
"有用。"
"啥用?"
我张了嘴,没说出话来。什么用?能算管道流量的用。能编译通过不打红字的用。能在屏幕上显示一排数字的用。我说不出来——不是不知道,是说不清。那种感觉像暖气管里咕噜咕噜跑的水——你听到声音了,知道它在走,但你说不清它走向哪里。
"有用。"我又说了一遍。他没追问。
电话那头的冲床声停了两秒。他跟旁边的人说了句什么——大概是让徒弟把料搬好。然后对我说:"我挂了啊。"
嘟——嘟——嘟。
走出电话亭的时候雪下大了。军大衣的肩膀上积了一层白,像披了一件白色的斗篷。我伸手拍了拍,雪粉从领口滑进去,化成冷水贴着脖子往下淌。
赵启明的信这时候到了——不是信,是一个快递包。包裹单贴在收发室的窗玻璃上,我拆开里面是一本书:《金融衍生品入门》,封面是蓝白条纹,定价三十八块。书里夹了一张纸条,纸条上写了一行字:
"期权就是给未来买个保险。你算管道流量、我算风险流量——方向是一样的。启明。"
我把书翻开。第一章讲期权定价模型。公式一排排的,和伯努利方程写法很像——等号左边是输入,右边是输出,中间是变量。但变量不是水和管径了——是行权价、波动率、到期时间。我把赵启明的纸条夹在第23页,合上书。
考试周来了。
图书馆的人从早排到晚。走廊里的暖气片坐满了人——不是座位,是直接坐在地上,背靠着暖气片,腿伸在过道里,书摊在膝盖上。保温杯排成一排在暖气片上烤着,不锈钢的杯壳烫得挨一下就缩手。有人在杯子上贴了小纸条写名字,怕拿混了。纸条被水汽泡得字迹模糊。
高等数学从第四章考到第十二章。我把课本和笔记本摊在桌上翻了三遍,最后合上书,在草稿纸上默写公式。伯努利方程、连续性方程、达西公式——每一个公式的右边都是"=常数"或者"= 0"。自然界的法则:进多少出多少,差为零。
给排水工程考的是管网水力计算。试卷最后一道大题给了一个管网图,六个节点,八根管段,要求计算每根管段的流量和水头损失。我把管网看了一眼——六个节点,八根管段。这不就是图论里的图吗?节点是顶点,管段是边,流量是权重。Dijkstra算法可以找最短路径——但这里不是最短路径,是最优流量分配。题目没要求编程,用计算器算就行。我用计算器算了四十分钟,算到最后一个节点的时候手心出了汗。
C语言考的是编程基础——变量、循环、数组、函数。指针出了一道选择题,三级指针,问输出是什么。我看了两秒,选了C。考试时间还剩二十分钟,我把卷子从头到尾检查了一遍,然后提前交了。
考前那天晚上睡不着。上铺的王强也睡不着——他翻了个身,床板咯吱响了一声,问我:"你说这考完了——放几天?"
"四十天。"
"四十天。"他重复了一遍。然后没声了。过了一会儿他又翻了个身,说了一句:"我爸说下学期给我买台电脑。"
"你又不碰电脑。"
"不碰但得有。寝室六个人得有五台——差一台不像话。"
我笑了一声。黑暗里他听见了,也笑了一声。
窗外有风。白杨树的枝条刮着窗玻璃,一下一下,像有人在外面用指节敲门。宿舍楼外面的路灯把枝条的影子投在窗帘上,影子一来一回地晃,像时钟的指针走了半圈又退回半圈。