首页
社区
课程
招聘
[原创]命运的代码(长篇连载)
2009-10-18 10:32 65676

[原创]命运的代码(长篇连载)

2009-10-18 10:32
65676
负一 声明

  本书非纪实作品,书内提及的公司名、人名、事件均为艺术虚构,并非真实。如有雷同,纯属巧合。

〇 前言

  我很想写一个故事,但是这故事的内容大部分在程序里。因为这是一个程序员的故事。请问这可以吗?
  这真的是很糟糕。因为除了程序员,没有人会喜欢程序。那些该死的代码,哦,它根本就不是写来让人看懂的。它就是用来迷惑人、糊弄人、让人显得无知的东西吧?发明这种东西的人,简直是世界上最自大的人…..
  不过,却有那么多人喜欢英语。也有那么多人喜欢汉语,这不是更奇怪吗?
  如果说汉语存在有规律的语法……好吧,那也要先排除掉一千种习惯用法、一千种特殊例外和一千种带着历史典故的成语吧。把剩下的干净的东西输入计算机,计算机依然会崩溃。它不会明白,人类为什么使用这么复杂而多变的交流方式,而不肯约定一个哪怕低效一点只要能固定规则的能清楚描述的协议……
  英语也好不到哪里去。单词数量毫无节制的暴增,各国人使用的奇特发音,没有人能说清到底有多少个单词存在,甚至不知道它们应该怎么念。
  相比而言,程序的语言是多么的简洁啊。C语言有多少个单词?我想C语言的关键字不超过100个。实际上,我自己会使用的估计不到20个……
  语法呢?五分钟就能教会了,无非就是大括弧,小括弧和分号?
  人类的语言就像天上的云彩,飘到哪是哪,说像什么就是什么。
  计算机的语言是某种精确的东西。就像无数的星球在引力的作用下的运行,就算是有碰撞,也是预先安排好的。就像宇宙心脏的滴答,始终精确的跳动。就像描述真理的语言。  

  这本书里的任何人都不是我。我只是一个想成为作者的人,我使用的是人类的语言。
  这是一本写给想了解程序员生活的读者看的书。程序员的生活里,充满的就是这样的东西。就像修鞋匠的店铺里总是挂满了鞋和鞋的零件,钟表匠的家里则随处都是滴答作响的东西一样。
  有时候读者也期望看到修鞋匠是如何升级成皮鞋大王,或者钟表匠如何成为卡东欧的董事长……遗憾的是,程序员的生活是写程序。当他不再是一个程序员,他也就没落到本书之外了。
  这只是一个很简单的、程序员成长的故事。

阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

收藏
点赞7
打赏
分享
最新回复 (118)
雪    币: 130
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
sojoo 2009-10-18 10:41
2
0
不会写小说的程序员,不是好厨师
雪    币: 164
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
schlieffen 1 2009-10-18 11:36
3
0
等待下文,看雪里面能多几篇小说是个不错的事情。
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
crazychu 1 2009-10-19 21:02
4
0
一 一切的开端

 在计算机的世界里,我接触到的第一个程序是计算从1加到100的程序。如果用神童高斯在七岁的时候发明的算法,任何人都可以在几秒钟内心算出从1加到100的结果。计算机不会发明新的算法,但是即使用最笨的方式一个数字一个数字的进行累加,来求得结果也是绝对不会超过0.1秒的。
 问题是,如何让计算机明白,我想要它完成的是从1加到100的工作呢?
 这个程序是BASIC写的——我的第一个计算机老师,他的外号叫做BASIC,因为他只教BASIC。我打算用C语言来重写这本书里的所有程序,因为,我想要追求全书代码的统一。这样读者就可以前后一致的看懂了。不管读者有没有学过C语言,这个程序 都非常的简单:
       
   int i,s = 0;
   for(i=1;i<=100;i++)
    s = s + i;
   printf(“%d”,s);

 首先是定义了两个变量 i和s。在计算机的思维里,变量总是代表着能改变的东西。比如,如果一个数字能从1变成2,再变成3,一直到变成100,这一定是一个变量。显然在计算从1加到100的过程中,这是非常有用的一个变量了。int代表了变量的类型是整数。
for是循环语句,这句话的下一句也就是“s=s+i”被循环反复的执行。循环的开始,i是1。循环继续的条件,是i小于等于100。每次循环之后,i都被加上1(i++表示i每次自增1)。s开始的时候是0(这在第一句就给予了指明),其后就不断的执行s = s + i,一共100次。在这100次中,i从1变到100。
 我那时读初二。计算机课是初二开始的特权。对于初一的新生而言,这样的待遇是没有的。这就让人觉得格外的荣幸。我觉得这段程序中,最神奇的就是那句:

 s = s + i;

 以我固有的观念,s就应该等于s。s怎么会等于s和i的和?除非i等于0,否则这样的等式显然不成立。但是,计算机确实可以令s等于s + i,其实就是将s + i的结果,存入到s中。BASIC老师介绍了一个很重要的概念:这里的“=”不是等于,而是赋值。将一个表达式的值,赋给一个变量。即使这个变量自己也出现在这个表达式中,这样的赋值依然是可以的。这就成为了一个很巧妙的状态:如果一个变量总是把自己设置成的自己的旧值加上某一个数字,它就一定会越来越大。就像滚雪球,它总是添加新的东西,而自己从不减少。于是从1加到100就这么被计算出来:它开始是0,然后加上1,加上2,一直加到100为止。
 从此,我知道计算机可以做很多的事情:它总是将变量和常量们加减乘除,然后再保存到其他的地方。它可以根据不同的条件,去执行不同的语句,做出不同的行为。比如说,如果输入的除数是0,就直接打印一个“错误”。如果输入的除数不是0,就显示出相除的结果。而且可以循环往复的完成巨量的工作。
 那些封装着的函数更加奇妙,掩盖了一切复杂的工作。我甚至都没有去想,究竟是怎么样的机制,才让我的程序输出的5050这样的数字,变换成显示器上的亮点,在屏幕上合适的位置显示出来。这些亮点又刚好组成这样的图案,看上去就像是5050这样的四个数字。我完全没有考虑过,只是知道,如果想要实现怎样的功能,就只管调用相应的函数就可以了。
 而其实,printf(“%d”,s);这一句,虽然只是简单的将s这个变量的值以整数的形式(%d代表整数)显示在屏幕上,却是整个程序中工作量最大的一句。CPU经过无数的处理向显示器发出合适的信号,虽然只是几毫秒之内的事情。而当我初步的理解这一步,已经是许多年之后的事情了。
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
crazychu 1 2009-10-20 20:47
5
0
 我现在有很多台电脑。我有两台台式机、三个笔记本,这还不算办公室里的三台。其中有一个是上网本,就是我现在捧着在床上码字的这台。现在买电脑真是容易,尤其是上网本,又小巧又便宜,性能也不是很差。换回十六年前,我的渴望就是有一台电脑摆在我的书桌上。我有事或者没事的时候可以敲着键盘,很轻松的显示器上看到我需要的结果。比如说完成我的数学作业(现在我发现,中学数学的作业如果去用电脑编程来完成,绝对不是件简单的工作。因为充斥着因式分解和几何证明题之类的很难找到一般规律,大多数靠经验和人类智力才能解决的问题)。
 对于经济状况很一般的家庭来说,这显然是不可能的。这需要一个为获得键盘和显示器而战斗的漫长过程。
 笔和纸完全可以代替键盘和显示器。而且任何程序都是可以用人脑来模拟执行的,虽然可能速度慢了一点。这个方法在很多年之后依然有人在使用:当买不起苹果机的程序员为iPhone编写程序时,就安装一个苹果机的虚拟机来模拟苹果机使用。他们为钱包里的内容物感到羞涩时,就不会介意运行速度带来的烦恼了。
 我在草稿纸上写了巨长的程序。程序总是很容易变得很长——当你想加入更多的功能的时候。尤其那时候我没有函数的概念。也就是说,我并不会将各个地方要反复使用的代码独立出来作为一个可以供别的地方调用的函数,我只会从头到尾的编写代码。
程序执行的结果被我想象出来,但是它们很少实战。这是一个巨大的遗憾。尤其是我觉得程序的执行结果会很有趣时。
 那时候几乎所有的男生都酷爱游戏。我已经意识到我可以通过按键盘来输入我的控制信息,让程序中的变量随玩家的意图而改变:比如按下向左的箭头,坦克便向左移动……这是我能想象出的最有趣的事情了。
 但是我唯一知道的输入方法是调用函数scanf。如果写下如下的代码:

 int s;
 scanf(“%d”,&s);

 程序执行的时候,到了scanf这句就会停止下来,等待输入。如果在键盘上按下了6然后再按下回车,则程序会继续执行。并且s这个变量会等于6。
但是真是糟糕,如果要我输入6的话,按6就可以了,为什么要我再按下回车呢?想象一下我用这个控制坦克,按1,再回车,向左。按2,再回车,开炮……玩家会抱怨,为什么一个简单的操作,却要按两个键?
 我查阅了各种资料,寻找按一个键直接完成输入的方法。但是都没有成功。首先我在一个偏僻小城,书店里的计算机书本来就少得可怜。其次,那时还不知道什么叫做互联网,更别提搜索引擎了,家里连电话座机都没有。我最后接受了必须多按一下回车的现实。但是更糟糕的问题又来了。程序一执行到scanf就会停下来。为什么要停下来呢?这不是白痴的设计吗?你想象看,在枪林弹雨中,当你打算浴血前行时,计算机停止了下来,连爆炸的火焰、飞行的子弹都停止了——只是为了等待你输入你打算向左开还是向右开!
 不过我并没有死心。我尝试用各种方法来解决。比如说,如果我的屏幕有两个部分,上面的大的部分显示我的坦克在枪林弹雨中前进,不接受输入所以也永不停止。下面有一个小的部分等待我的输入。如果我输入1回车则上面的坦克向左转,输入2回车则开炮……(这是现在一些网游的控制方式),这样虽然按键还是很麻烦,但是从逻辑上来说尚可接受。
这其实更困难了。划分窗口本来就很难,更别说scanf一定会停下整个程序,除非能划分多线程了。
 这不得不让人抓狂。
 最后我意识到用scanf这种语句来解决需要实时输入的游戏的问题是不合适的。但是它有它的用处,它很适合那类不需要实时动作的比如策略类的、战棋类的游戏。后来在初三的时候,我开始笨拙的使用笨拙的GW-BASIC编写一个策略“三国”,类似光荣系列的三国游戏。不过没有画面,纯文本输出。比如打印一个菜单:
 1.建设工业。
 2.建设农业。
 3.增加人口。
 4.征集士兵。
 请选择:
 这时候,用scanf就很合适了。只要玩家输入1回车,然后就开始进入到建设工业的流程,城市的工业指数随之提高。现在我觉得足够牛的是,这个程序居然未使用任何一个独立的函数,它是一个真正的过程。它被当作一个传奇保存在中学电脑室那一堆386电脑的共享硬盘上。现在应该已经灰飞烟灭了吧。
雪    币: 431
活跃值: (1875)
能力值: ( LV17,RANK:1820 )
在线值:
发帖
回帖
粉丝
riusksk 41 2009-10-20 21:42
6
0
楚狂人!写小说贵在坚持啊,很多人有开头没结尾,希望楼主可以坚持下去!
雪    币: 103
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
roadblosso 2009-10-21 11:20
7
0
嗯, 期待下文
雪    币: 4581
活跃值: (942)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
b23526 2009-10-21 11:54
8
0
等待完整版
雪    币: 227
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zpf 2009-10-21 14:49
9
0
牛啊  期待
雪    币: 247
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hbu任我行 2009-10-21 15:13
10
0
能坚持下去就是成功
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
crazychu 1 2009-10-22 06:45
11
0
 我爸妈说:“如果你考上重点高中,就给你买电脑。”
 不过他们说的电脑其实不是正宗的IBM兼容机。只不过是任天堂的8位游戏机的山寨版加上了一个键盘。当然这对我来说已经算一个巨大的激励了。高中我就上了全市最重点的高中。如愿的在书桌上摆上了键盘。虽然它几乎没有任何用,而且一个重大的烦恼是:有些键时灵时不灵。
 其实实时的输入在GW-BASIC上是完全可以实现的。GW-BASIC就有一个方法,用一个函数得到当前输入的字符并立刻返回,不需要输入回车来表示结束,我简直爱死这个函数了。如果执行的时候用户没有按下任何键,则会直接返回空字符。总之程序不会停止在这里,而会继续运行。如果用C语言来实现,需要两行:

 if(kbhit())
  c = getch();

 if是判断语句。根据括弧内的结果是否不为0来决定是否执行它后面紧跟的语句。kbhit表示当前是否有键按着。如果有,就调用c=getch()来获得输入的字符,保存在变量c中。接下来就可以直接用作决定坦克前进方向的依据了。如果当前没键按下,则kbhit会返回0,getch这句就不会被执行,程序继续运行。这也是一个很好的判断的例子。
 小时候有一种手掌游戏机,游戏的内容是空中掉下炸弹。下面有一个可以操作的移动的高射炮。通过移动高射炮向空中发射子弹,可以提前“击爆”这些炸弹。每击中一枚炸弹都可以得分。但是一旦炸弹未被击中,则迟早落在地上,游戏则结束。很难想象这样的一个游戏,可以用简单的几行程序来模拟。如果将一切画面和音效省去,只是显示一个运动的“A”字符来代表子弹,用“*”代表爆炸的效果,则上面的这个程序就可以用getch和kbhit之类简单的函数全部包办完成。我在我的“电脑”上反复的编写了这个程序无数次:因为我的学习机既没有硬盘,也没有软盘,它可以编程,但是从来都不可以保存。

 无论如何,一切就这样开端了。计算机的世界大门,和改变现实世界的机会。懵懂之间,我不知道我已经选择了我的职业,和我未来的命运。但是我知道比尔和保罗编写BASIC和DOS,求伯君编写四通打印机驱动的英雄故事。世态混沌,群雄并起。我能横刀立马,成为改变世界的力量吗?
那是一九九八年,我十八岁。那时我离开了南方,去西安上X大学。
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
snowdown 2009-10-22 09:18
12
0
我用处女贴来MARK
雪    币: 471
活跃值: (3202)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
xss 4 2009-10-22 12:17
13
0
不错,等养肥了再杀!
雪    币: 102
活跃值: (50)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
nence 2009-10-22 12:18
14
0
1970年的大哥,期待你的下文
雪    币: 99
活跃值: (154)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
iiris 2009-10-22 17:39
15
0
楼上的,人1980的。
mark
雪    币: 33
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
九龙之心 2009-10-22 22:32
16
0
期待下文……
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
crazychu 1 2009-10-22 23:10
17
0
二 北地之风

 西北与南方不同。南方有阳光和雨,西北则有尘土和风。那些古老的城市,与其说是在地面上建造的,不如说是在旧积的尘土上,用新积的尘土垒成的。
 游先生是X大学里一个研究员,我读大学时因为他的侄子是我的同学而得以认识他。他不懈努力的试图改变他的命运。他不断的发明新的玩意,不断的寻找来自各种门路的新的订单。二十世纪的最后一年,他开车载我和他的同事去渭南。在路上的间隙,还在背诵单词。他在忙乱中准备考博士。
 “如果这次再不考上,我在学校里就没法混下去啦。”
 他就像北方的风一样,在各地奔忙,永不衰竭。他总是给我秀出很新奇的东西。比如说,他相信人们早晚会把书籍储存在某种指甲盖大小的芯片中,并且抛弃了沉重的纸版书,手捧着小巧玲珑的电子设备,通过更换芯片来选择不同的书。
 既然如此,那就让我们来制作一个吧。
 于是他买来像巴掌那么大的液晶显示器(如果不知道哪里有买,作为现代人类,可以使用淘宝),按照附带的说明书组装电路板(电路板及其他零件是购买液晶显示器的附赠品,按图组装即可),电路板上有预留的接口可以插入单片机 。
 只是那个东西组装出来之后绝对不小巧,至少有一本牛津词典那么大。这个液晶显示器能显示蓝底白字。我在此之前,从未意识到我的C语言编写的程序,还能在这些东西上面发挥作用:他将仿真机 的一个接口连接在电脑串口上,另一个模拟单片机的插头则插在电路板上应该插入单片机的地方。在电脑上安装相关的软件之后(这些软件都是买仿真机的附赠品),我可以用C语言直接编写程序,用C51编译器编译之后,编译的结果传输到仿真机上,然后直接在真实的硬件上运行。这个过程甚至可以单步调试。
 虽然看上去很麻烦,但是实际上在单片机上编程比在PC机上编程要直接得多。那时我还不知道什么叫做嵌入式系统,我们直接用C语言编程解决一切问题。
 所有的内存都直接用地址来访问。我知道C语言用指针表示地址,比如说:

 char *p;
 char a;
 p = &a;

 变量p的类型是一个字符类型 的指针。而&是取地址的意思。p = &a也就意味着把字符变量a的地址赋给了p。然后就可以通过p来得到a的地址,也就是说,把p当作a的指针来使用了。如果要设置a为5,除了a = 5这样的写法外,还可以通过指针来设置:

 *p = 5;

 这里的*和&符号相反,表示从指针取得内容。
 不过在只在微机上编写应用程序的人,倒是十个有九个从未去关心这个p的数值到底是多少。以至于当看到说明书上说,按说明图示焊好之后,内存地址0xff10开始就是显存开始的位置。请直接读写这个区域来控制液晶屏幕的显示,我居然不知道用C语言如何才能读取地址0xff10的内容。其实这件事极为简单:

 char *p;
 p = (char *)0xff10;

 然后读写*p就可以操作内存地址0xff10的内容了。很简单,不是吗?从这里也可以看出指针的必要性。因为我难以定义一个变量,这个变量的地址刚好就是我要的地址,便于我操作这个地址上对应的硬件。
我这时才意识到,原来在屏幕上打印一个字符,实现像printf这样的函数,其实并非一件如使用它那样的、简单的事情。用什么样的方法,可以让由256*192一共四万九千一百五十二个小光点组成的显示屏显示我要的汉字呢?
 液晶显示器电路板的说明书已经告诉了我显存的位置。所谓的显存是这样的一片内存空间:它的每个比特 就对应着一个显示器上的一个点。如果这个比特是0,那么显示器上对应的点就是暗着的。如果这个比特是1,那么显示器上对应着的点就是亮着的。写这些内存,就可以控制显示器上像素点的明灭了。
 那么假设控制显示器并不是困难的一件事。那么,我如何把一个“6”这样的数字显示在屏幕上呢?
 首先,假定我用一个8*8的点阵来显示“6”这个数字,这个点阵大概是这样的(请注意下面的数字方阵中的1所组成的形状):

 0 1 1 1 1 1 1 0  对应十六进制数是0x7e;
 1 0 0 0 0 0 0 1  对应十六进制数是0x81;
 1 0 1 1 1 1 0 0  对应十六进制数是0xbc;
 1 1 0 0 0 0 1 0  对应十六进制数是0xc2;
 1 0 0 0 0 0 0 1  对应十六进制数是0x7e;
 1 0 0 0 0 0 0 1  对应十六进制数是0x81;
 0 1 0 0 0 0 1 0  对应十六进制数是0x81;
 0 0 1 1 1 1 0 0  对应十六进制数是0x3c;

 我承认我缺乏艺术细胞,我排列的“6”并不是很优美。但是隐约能看出是一个阿拉伯数字“6”的形状。这需要一共8个字节(每个字节是8个比特)的一个数组。数组是同样类型的一组变量的组合。每个变量可以称为一个元素,而下面的定义语句中方括号中的数字则表示这个数组的元素的个数。我可以把显示“6”需要定义的点阵表示成一个数组如下:

 char pm6[8] ={
  0x7e,0x81,0xbc,0xc2,0x7e,0x81,0x81,0x3c};

 不过就算“6”这个字符可以暂时这样对付过去,其他的问题也远远没有解决。如果要显示各种字符,那么毫无疑问每个符号都必须制作一个这样的数组。而且我们要做的是电子书!上万个汉字的点阵的制作是免不了的。且不说我的字本来就写得糟糕,就算我在纸上把这些字一个一个排出来,转换成数字写到程序里——如果我2分钟能排好一个,也需要20000分钟也就是超过300小时,为了这个差不多就得工作一个月。而且还全是体力活。
 这种工作或许有专业的技术可以解决。比如说,也许可以把印刷好的文字扫描进电脑,然后用程序转换成点阵信息。也可能有更好的保存信息的方式,不过绝对不简单。
 “这个问题很好解决……”游先生说:“UCDOS 安装之后,目录下就有汉字点阵库文件,拷出来用就行了。”
 原来UCDOS的开发者早就已经制作了这些汉字点阵信息,并且全部保存在UCDOS中的汉字库文件里。这样想要输出哪个汉字的时候,只要把这个文件中的对应的点阵信息读出来然后直接写入显存就可以了。
 当然今天这样做可能会招来官司,虽然这样能节约社会劳动力。不久前方正起诉暴雪,认为中文版魔兽世界中使用的汉字侵犯了方正的知识产权,并索赔1亿美元就是很好的例子。
 当标准的宋体汉字出现在我们自己焊接的液晶屏幕上时,我们着实兴奋了一阵子。一周之后,上边已经可以显示好长一段“三侠五义”的内容了,而且还能通过按键上下翻页。除去稍有些笨重之外,这个东西非常酷。
雪    币: 440
活跃值: (87)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
asdfslw 3 2009-10-23 08:02
18
0
不错。 我来顶一下。
雪    币: 197
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
曾用名 2009-10-23 08:59
19
0
没仔细看先支持下
雪    币: 1040
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
KeyKernel 1 2009-10-23 09:08
20
0
不错不错  哈哈哈。。支持继续啊 。。。
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
XinDOS 2009-10-23 18:16
21
0
支持楼主原创,呵呵
雪    币: 1211
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jmuguy 2009-10-23 18:42
22
0
支持楼主。。期待下文。。。。
雪    币: 15
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zuccbug 2009-10-24 22:51
23
0
不错不错,等连载完,希望能整理出电子书,下来再看:)
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
crazychu 1 2009-10-25 10:58
24
0
 谭浩强的《C语言程序设计》非常仔细的描述了C语言的语法,甚至我从来不使用的多维数组在内存中的排列方式都写得异常的清楚。但是遗憾的是,X大学的C语言教材既没有告诉我什么应该写在头文件 里,什么应该写在C文件里,也没有告诉我如何把不同的功能实现在不同的文件里。
 幸运的是,我终于学会使用函数。当一段代码具有某种通用的功能,可以被反复调用的时候,我们可以把它写成一个“函数”,函数总是根据输入来执行,并返回输出结果。比如说我要在屏幕上(x,y)的地方显示一个汉字,我没有必要每次都写一段代码去读取汉字点阵库,然后操作显存,而只需要写一个函数。这个函数的输入就是要显示的位置(x,y)和要显示的汉字。
 不过我上课并不认真,C语言课程结束的时候,我还不知道该如何使用函数声明。如果我在某个函数中调用了另一个函数,我必须提前声明这个函数,否则,要么被调用的函数的实现刚好在前,要么会出现编译 错误。
 这曾经让我很犯难。但是我迅速的解决了这个问题:我每次写代码的时候总是小心的排列所有的函数,让被调用的函数,总是实现在调用它的函数之前。我觉得这是天经地义的。
 但是这样的话,如果一个C文件内的函数,如何调用另一个C文件内的函数呢?
对于此事,我的应对策略是:在前一个C文件的头部写上“#include”来包含另一个C文件。比如:

 #include “functions.c”

 在现在,包含一个c文件而不是头文件绝对是一个匪夷所思的做法。因为c文件里一般都有函数的实现,如何两个包含了同一个.c文件的c文件编译的目标文件就会含有同样的符号,在连接的时候必定冲突。若不是特殊的有意为之的情况,用“#include”来包含C文件会是让所有C程序员笑掉大牙的一件事情。
 但是我的电子书可是没出任何问题。原因是那时我还不会使用连接器。对我来说,把所有的C文件都“#include”到一起然后作为一个C文件编译一次就可以了,根本不需要连接。

 汉字库可以和代码保存在一起,放在固化存储器中的固定的位置。但是书籍的内容就不同了。电子书存在的价值,就在于通过一个设备就可以阅读许多不同的书。显然不能把书固化在设备里。
 那时我还没有见过U盘,但是已经有闪存芯片。游先生计划把书的内容保存在512k大小的闪存芯片里。512k大约是512千个字节。一般的说一个汉字可以用两个字节的数据来表示,那么这样的一个闪存芯片只能保存大约25万字。在不考虑图片、公式和排版,而仅仅只保存文字的情况下,作为一部中等长度的小说或许也足够了。不能用更大的闪存是因为那时闪存还很昂贵,不像现在U盘和SD卡几乎满街都是。但是实际出售的电子书会是用的固化的存储器,那就会便宜很多了。
 每当要在存储器里遇到保存一点东西的问题,我就会碰到那个古老的难题也就是文件系统所解决的问题。当我们要把多个文件(比如好几本小说)保存在一个芯片中的时候,并可能要删除某个旧文件,增加某个新文件的时候,问题就出现了。
 闪存芯片拥有连续的存储空间。如果我要保存A,B,C三个大小不一的文件,这是比较简单的。假定空间足够,我从闪存芯片开头开始保存,连续存放。并记下每个文件保存的开始地址,以便读者看书的时候读取。
 某天,读者对B文件腻烦了,直接删除了它。这时候闪存的存储空间中出现B文件大小那么大的一个空洞。
 某天,读者决定把文件D保存到电子书设备中。但是D比B要大,在原来的空洞中放不下,只好继续保存到后边空旷的地方。
 这样反复的操作的结果是,大大小小的空洞越来越多,最后什么文件也放不下了。虽然其实里边空余的空间还很多。
 我们需要一个文件系统,来分配这些存储空间。
 那时我参考了DOS的FAT文件系统,进行简化之后写出了大堆的代码。这些代码有时可以工作,但其中的BUG 越来越多。我反复的调试,直到游先生信心崩溃。
 其实他的放弃我想并不是因为我的软件的问题,而是他意识到我们试图去撬动这个产业有蚍蜉撼树之感。他需要不断的投入资金和精力,而商业化的应用还遥遥无期。
 于此同时,他的新项目又滚滚而来,不可能不腾出手去做的。
 我对那堆越来越垃圾的代码也早就心烦,于是赶紧借口期末考试将至拍屁股走人。
 即使是到了今天,单独的手持电子书设备依然不是很流行,虽然技术在日新月异的发展中。有很多人通过手机来阅读电子书,再使用一个单独的手持电子书设备已经显得有点多余。当然手机兼电子书设备有手机的缺点:屏幕太小,手机屏幕的荧光让眼睛觉得不舒服等等。最近有一种新的表面上看去像是纸张的不发光的电子书出来,有人说可能会导致电子书流行起来。
 不管电子书的前途如何,我觉得人们都还肯定还需要一种手持设备。这种设备或许比手机有更大和更舒适的显示屏幕(比如可以选择不发光),更方便的操作方式(比如很容易的复制粘贴做笔记等等),又比笔记本电脑更便携。
 虽然我还是更喜欢夹杂着油墨和纸张味的书香气。
雪    币: 124
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
crazychu 1 2009-10-25 11:04
25
0
 汽车开过渭河大桥,桥下是开阔无际的河床。河流缩减成了原来的十分之一,变成一条弯弯曲曲的小溪。人们在河床上办起了驾校。剩余的地方则种满了蔬菜。游先生自己开车。我和他的一个同事是乘客。我们从西安出发去渭南,上了高速公路。
 我不知道此行的确切目的地,我猜是某个秘密军事基地?但是我知道此行的目的。这目的和一个神秘的老头有关。我曾见过他一面。
 自从电子书的合作之后,游先生似乎对我的印象相当不错,一有项目就和我联系。当然我这种根本就不追求现金回报,纯粹把写代码当作好玩的学生本来就很难得。
 这次的项目并非那种胡思乱想的未来产业,而是标准的现金牛。八十年代末国家进口了某种先进武器的生产线,后来打算在国内生产这种顶级武器。大部分零件将自己来生产。其中就涉及到一种零件,这种东西需要特殊的方式进行焊接。不知为何,最终的责任落到了那个我只见过一面的神秘老头那里。我估计这个人可能是X大学里某研究所的一个教授。老头和游先生是老朋友。他设计了一个安装在某个铁轨上的焊炉。这个炉子可以由电动机带动在铁轨上移动。移动到某个部位,可以进行焊接。移动到另外一个部位,则可以进行充气之类其他的工作。根据需要,焊炉里可以充入气体,比如氩气。这个炉子需要自动按照某种可以调节的工作流程来工作。比如说:充气->移动->加热->焊接…还需要根据炉内不同的温度做出不同的反应,比如移动位置、冷却等等。老头设计了炉子,游先生的同事是一个电工,设计安装了所有的电路,包括能控制炉子移动的电机。一切就绪,就差一台计算机,来控制炉子的动作了。
 游先生买来了工控机。工控机本质上和一台PC并不多大差别,只是形状看上去稍有不同。主板的插槽极多,而且看上极其粗大。散热风扇巨大无比。原因是工控机并非游戏机,对性能要求并不高,但是接口一定要多。多了才能控制更多的东西。另外一定要稳定。车间比不得开着空调的服务机房,在这里既要耐得住高温和潮湿,又要禁得起灰尘和振动。工控机比同等性能的PC昂贵许多,而且清一色的都是台湾人的产品。
 考虑到复杂的工作环境,用机械式的硬盘就不行了。万一一个振动让磁头撞坏了盘面……除了计算机,被控制的炉子可能也得跟着遭殃。为此用了一个被他们称为半导体硬盘的东西。才800M,却贵得要命。
 另外让我吃惊的是,他们简单的在工控机上安装了一个DOS系统。他们自古以来都坚持使用DOS,又简单又稳定。然后就可以用Turbo C 之类简单的开发工具来写程序了。
 工控机中的程序通过串口输出命令就可以控制焊炉的移动和充气过程。不过这还有一个问题,就是程序还得根据炉中的温度来做出不同的反应。这个问题的解决也颇简单,游先生买了一个温度仪表。
这种仪表看上去像一个CS里看到的定时炸弹。但是上边的红色的跳动的数字并非是到爆炸还剩余的时间,而是温度。它有一个串口插头可以插在计算机的串口上。每秒钟它都通过串口向计算机报告一次它读取到的温度。在它的温度感受器安装到焊炉中之前,我看到的温度实际上是当前的气温。
 我们这次的显示屏是彩色的了。依然是液晶的,但是比做电子书时的显示器稍大。此外还配有键盘。键盘和标准键盘的接口和扫描码 都是一样的,只是形状不同。工业键盘的键就像画在塑料纸上,外表是平的,按上去会陷下去。按键的时候颇得花一点力气。这是为了避免不小心有谁或什么东西碰到键盘的时候,某个键被意外的按下。同时工人师傅的力气都很大,免得被他们揿坏了。
 我主要的工作和以前做游戏的时候差不多。我要在显示器上提供一个界面,能显示焊炉现在的状态(如位置、温度等等),如果温度高过界限,我得提示警报。
 还得提供菜单和选项,让工艺师可以用键盘编制工艺流程。一个简单的工艺流程就是这么一组东西:
 1.充氩气,15秒。
 2.移动到焊接位。5秒。
 3.加热至温度300度。等待60秒。
 4.停止加热。移动到冷却位。等待30秒。
 ……
 根据不同的焊接需要,可以编制不同的工艺流程。这些流程会被保存在工控机的半导体硬盘上。对计算机而言,只要把这些数据当作文件保存起来,在工人师傅选择了工艺流程之后,一步一步读出来按照这个给串口发指令,控制炉子一步步操作就行了。这对我而言,又是一个移动高炮攻击敌机的游戏。
游客
登录 | 注册 方可回帖
返回