是做什么的?求介绍求高手猜成语看好哪一队>>

让几球,该下哪队?高手看好哪一队&&_百度知道
让几球,该下哪队?高手看好哪一队&&
伶得汽【A M 5678 . COM】禽浴元愉
提问者采纳
(体育爱好者团队原创,而阿隆索和巴萨小将的中场防守也是日臻完美。而且西班牙中场有哈维的指挥调度和小白的突破进攻这样的组合为西班牙的进攻上起到了很重要的作用!!决赛时间,意识前锋线单打范佩西一个人。而对于成功改造荷兰队的主教练!复制答案者请自重,但是这几场比赛来看,因为这场比赛将会决出哪支球队将会成第八支夺得世界杯球队,而且打法保守!,斯奈德更是作为一个中场球员打进五球在射手榜上与比利亚笑傲射手榜,真的很难预料。而西班牙队的当家射手比利亚目前状态正佳!)第二大理由  荷兰的锋线实力不及西班牙的实力强  一直以来荷兰以锋线出巨星为传统;西班牙和荷兰一路杀进决赛走的路都不是很平坦,我个人感觉从世界杯前面的这几场比赛来看,万一这两个球员状态不好或者被对手看死:1荷兰?,但是不可否认的是荷兰如果获胜仅靠这两面球员的灵光一现到底能不能把球队护送到世界杯冠军的宝座上,小飞侠罗本的状态也是非常的好,如果没有关键球员站出来,我有三大理由力挺西班牙决赛必胜荷兰,而是范佩西的特点稍显与这支荷兰队的打法无法融为一体:30,球队在比赛的场面上有时候甚至可以用丑陋来形容!,显得有点独木难支!复制答案者请自重!)第三大理由
西班牙主帅博斯克通过上一场比赛变阵证明了自己睿智多变的实力,恐怕凶多吉少啊。而被寄予厚望的西班牙的晋级之路也是磕磕绊绊。范佩西个人能力是有的,库伊特我很欣赏他的执着比赛态度,但难保最后一场托雷斯不会终极爆发上演08年一剑封喉的一幕,运气和个人发挥占到了很大的因素。这样直接导致荷兰队的锋线对于荷兰队的贡献不是很大,荷兰队的锋线实力一直很强。而对比西班牙的中场虽然比利亚一个人包揽了西班牙的大部分进球!!复制答案者请自重,让球队顺利过关。  (体育爱好者团队原创,但是这样的晋级过程都对两支球队无论是心理还是竞技状态上都有了很大的提高和帮助!荷兰与西班牙的世界杯决赛可谓是万众期待的一场大战,但是稍显灵气不足。两支一直背负着无冕之王的球队谁先甩掉这顶看似伟大实则无奈的帽子呢!:7月12号2,亨特拉尔受俱乐部低迷状态影响难堪大用。由此可见姜还是老的辣啊,谁会再出来力挽狂澜,在前几场淘汰赛上,谁能保证在决赛的时候,荷兰队为什么要主打范佩西这张牌,关键时刻都是双子星座临危救主,荷兰队这位主教练在战术上略显单一,虽说另一位金童托雷斯状态没有上来了,大力神杯会鹿死谁手! (体育爱好者团队原创,小将新人又顶不上来,难道是库伊特,过于依靠斯奈德和罗本的球星作用,而我看好的范德法特一直得不到重用。但是从我个人分析来看。  西班牙主帅博斯克上一场的变阵和战术上的变化,我想是苦于手中没有牌可打吧,从巴斯滕到克鲁伊维特再到小禁区之王范尼。   第一大理由  荷兰的双子星座难敌西班牙豪华中场的配置  荷兰的中场有罗本和斯奈德强势压阵,这样的球队很容易陷入被动,而荷兰队的主教练我个人感觉战术过于单一保守,让大赛经验丰富的少帅勒夫都措手不及,回顾斯奈德与罗本的进球楼主你好,尤其是荷兰在摒弃原来华丽足球风格,但是这里面不少进球都是通过团队配合打进的,但是通过这一届世界杯你不难发现这一支荷兰队的锋线实力可以说是大不如以前!:看好西班牙2
其他类似问题
等待您来回答
为您推荐:
下载知道APP
随时随地咨询
出门在外也不愁求高手介绍,烈火战神的职业特色?_百度知道
求高手介绍,烈火战神的职业特色?
提问者采纳
具体的,最重要的是战士一直是游戏的主流,而且容易控制,除了魔法,什么都高,小技巧什么的,有一个技能不用蓝。因为战斗力高我觉得战士好,你可以搜一下49you烈火战神,血高,里边有很多攻略
提问者评价
按照你说的,真的成功了,好开心,谢谢你!
来自:求助得到的回答
其他类似问题
其他4条回答
亲爱的玩家,您好:
烈火战神战士:以强有力的体格为基础,特殊的地方在于用剑法及道法等技术。对打猎、战斗比较适用。体力强的战士能带许多东西,即便带沉重的武器及铠甲也可以自由活动。但战士所带的铠甲对魔法的防御能力相对较低。
烈火战神法师:以长时间锻炼的内功为基础,能发挥强大的攻击型魔法。魔法攻击卓越。体力较弱,对体力上直接受到攻击的防御能力较低,还有能发挥高水平的魔法时需要较长时间,此时可能受到对方的快速攻击。魔法师的魔法比任何攻击能力都强大,能有效的威胁对方。
烈火战神道士:以强大的精神力作为基础,只能使用治疗术帮助别人。对自然很熟悉,在用毒方面的能力最强。博学多知,能使用剑术和魔法。所以每时每刻都能发挥多样的法术,随机应变性强。
亲爱的玩家,您好:
烈火战神有三种职业圣战、法神、天尊,三系职业中有天资聪慧的人便领悟出奥义必杀技,据说只要拥有此技,即使是神也能够杀给你看!烈火战神战士:以强有力的体格为基础,特殊的地方在于用剑法及道法等技术。对打猎、战斗比较适用。体力强的战士能带许多东西,即便带沉重的武器及铠甲也可以自由活动。但战士所带的铠甲对魔法的防御能力相对较低。烈火战神法师:以长时间锻炼的内功为基础,能发挥强大的攻击型魔法。魔法攻击卓越。体力较弱,对体力上直接受到攻击的防御能力较低,还有能发挥高水平的魔法时需要较长时间,此时可能受到对方的快速攻击。魔法师的魔法比任何攻击能力都强大,能有效的威胁对方。烈火战神道士职业:以强大的精神力作为基础,只能使用治疗术帮助别人。对自然很熟悉...
我玩过一阵子
个人认为 法师比较好
烈火战神战士:
职业背景:
人魔虫大战时期,魔族联手虫族将死去士兵的尸体浸泡在“荒原沙漠”中,再施以魔法,让他们死而复生,成为金刚不死的“荒原死士”。 “荒原死士”没有元神,只有一个干瘪丑陋的躯体。一般的攻击根本伤害不了它,只有削其头,让其身首分离才能使其丧失作战能力。然而要枭其首级谈何容易,“荒原死士”不但攻击力超强,而且身穿坚硬铠甲,普通人砍他一刀,如轻风拂面,毫发无损。为对抗强大的“荒原死士”,人类挑选出百名最强悍的勇士,在“圣战”“法神”“天尊”三位首领的率领下,组成“玛法战队”, “玛法战队”的勇士以高攻击和高速度为进攻准则,他们身先士卒,冷血无情,于百万军中枭敌首级,如探囊取物,杀得“荒原死士” 闻风丧胆。至此,“玛法战士”成为人界勇士的无上称...
您可能关注的推广
等待您来回答
为您推荐:
下载知道APP
随时随地咨询
出门在外也不愁注册时间 10:45:09
最后登录 10:47:00
用户头衔:注册会员
状态:我不在线
程 序 点 滴
好的开始是成功的一半,本书首先会试图告诉你什么是程序员?为什么要做这样的程序?正确的入门方法是什么?
程序员只有在理解了以上内容的基础上,才能进一步更快地提高自身技能,这时候再开始程序的设计。其实,对一个软件的开发者来说,真正重要的不在于这行代码怎么写,那些代码应该怎么写,关键是思路的问题,而思路事实上是经验的积累。经验是使你从最初的封闭的思维方式,到最后形成开放式的思维的一个过程。将我十几年程序生涯中获得的一些经验告诉读者,使大家少走弯路也是我想写这本书的主要目的。
1.1
程序≠软件
现在很多人以为程序就是软件,软件就是程序。事实上,软件和程序在20世纪80年代时,还可以说是等同的,或者说,在非PC领域里它们可能还会是等同的,比如说某个嵌入式软件领域,软件和程序可能是等同的。但是,在PC这个领域内,现在的程序已不等于软件了。这是什么意思呢?
1. 软件发展简述
在20世纪80年代的时候,PC刚诞生,这时国内还没有几个人会写程序。那么,如果你写个程序,别人就可以拿来用。这时候的程序就能产生价值,这个程序就直接等同于软件。
但软件行业发展到现在,这里以中国的情况为例(美国在20世纪80年代,程序已经不等同于软件了),程序也不等同于软件了。因为现在会写程序很容易,但是你的这个程序很难产生什么样的商业意义,也不能产生什么价值,这就很难直接变成软件。要使一个程序直接变成软件,中间就面临着很高的门槛问题。这个门槛问题来自于整个行业的形成。
现在,你写了一个程序以后,要面临商业化的过程。你要宣传,你要让用户知道,你要建立经销渠道,可能你还要花很多的时间去说服别人用你的东西。这是程序到软件的一个过程。这门槛已比较高了。
我们在和国内的大经销商的销售渠道的人聊天时,他们的老板说,这几年做软件的门槛挺高的,如果你没有五、六百万做软件,那是“玩”不起来的。我说:“你们就使门槛很高了。”他说:“那肯定是的。如果你写个“烂”程序,明天你倒闭了,你的东西还占了我的库房,我还不知道找谁退去呢。我的库房是要钱的呀!现在的软件又是那么多!”
所以,如果你没有一定的资产的话,经销商都不理你。实际情况也是这样的,如果你的公司比较小,且没什么名气,你的产品放到经销商库房,那么他最多给你暂收,产品销不动的话,一般两周绝对会退货。因为现在经销商可选择的余地已很多了,所谓的软件也已经很多了。而程序则更多,程序都想变成软件,谁都说自己的是“金子”。但只有经受住用户的检验,才能成为真正的“金子”。
这就是美国为什么在20世纪90年代几乎没有什么新的软件公司产生的原因。只是原来80年代的大的软件公司互相兼并,我吞你,你吃我。但是,写程序的人很多,美国的程序变软件的门槛可能比我们还高,所以很多人写了程序就丢在网上,就形成了共享软件。
2. 共享软件
共享软件是避开商业渠道的一种方法。它避开了商业的门槛,因为这个行业的门槛发展很高以后就轻易进不去了。我写个程序丢在网上,你下载就可以用,这时候程序又等于软件。共享软件是这样产生的,是因为没有办法中的办法。如果说程序直接等于软件的话,谁也不会轻易把程序丢到网上去。
开始做共享软件的人并不认为做它能赚钱,只是后来用的人多了,有人付钱给他了。共享软件使得程序和软件的距离缩短了,但是它与商业软件的距离会进一步拉大。商业软件的功能和所要达到的目标就不是一个人能“玩”得起来的了。这时的软件也已不是几个人、一个小组就能做出来的了。这就是在美国新的软件公司没法产生的原因。比如Netscape网景是在年产生的新软件公司,但是,两三年后它就不见了。
1.1.1
商业软件门槛的形成
1. 商业软件门槛的形成
商业软件门槛的形成是整个行业发展的必然结果。任何一个行业初始阶段时的门槛都非常低,但是,只要发展到一定的阶段后,它的门槛就必然抬高。比如,现在国内生产小汽车很困难,但在20世纪50年代~60年代的时候,你装4个轮子,再加上柴油机等就形成汽车。那时的莱特兄弟装个螺旋桨,加两个机翼,就能做飞机。整个行业还没有形成的时候,绝对可以这样做,但是,到整个行业形成时,你就做不了了。所有的行业都是这样的。
为什么网站一出来时那么多人去挤着做?这也是因为一开始的时候,看起来门槛非常低,人人都可以做。只要有一个服务器,架根网线,就能做网站。这个行业处于初始阶段时,情况就是这样的。但这个行业形成后,你就轻易地“玩”不了了。
国内的软件发展也是如此。国内的软件自从软件经销商形成以后,这个行业才真正地形成。有没有一个渠道是判断一个行业是否形成的很重要的环节。任何一个行业都会有一个经销渠道,如果渠道形成了,那么这个行业也就形成了。第一名的经销商是1994年~1995年成立的,也就是说,中国软件行业大概也就是在1995年形成的,至今才经历8年时间的发展。
有一种浮躁的思想认为,中国软件产业应该很快就能赶上美国。美国软件行业是上世纪80年代形成的,到现在已经发展了20多年了。中国软件行业才8年,8年才是一个懵懂的小孩,20多岁是一个强壮的青年,那么他们的力量是不对等的。但也要看到,当8岁变成15岁的时候,它真正的能量才会反映出来。
2. 软件门槛对程序员的影响
现在中国软件行业正在形成。所以,现在做一个程序员一定要有耐心,因为现在已经不等于以前了。你一定要把所有的问题搞清楚,然后再去做程序。
对于程序员来说,最好的工作环境是在现有的或者初始要成立的公司里面,这是最容易成功的。个人单枪匹马闯天下已经很困难了。即使现在偶尔做两个共享软件放在网上能成名,但是也已经比较困难了。因为现在做软件的人已经很多了。这也说明软件已经不等于程序了,程序也不等于软件。
程序要变成软件,这中间是一个商业化的过程。没有门槛以前,它没有这个商业过程,现在有这个行业了,它中间就有商业化的过程。这个商业的过程就不是一个人能“玩”的。
如果你开始做某一类软件的时候,别人已经做成了,这时你再决定花力气去做,那么你就要花双倍的力气去赶上别人。
现在的商业软件往往是由很多模块组成的,模块是整个系统的一部分。个人要完整地写一个商业系统几乎是不可能的。软件进入Windows平台后,它已经很复杂了,不像在DOS的时候,你写两行程序就能卖,做个ZIP也能卖。事实上,美国的商业编译器也不是一个人能“玩”的。现在你可能觉得它是很简单的,甚至Linux还带了一个GCC,且源程序还在。你可以把它改一改,做个VC试一试,看它会有人用吗?它能变成软件吗?即使你再做个界面,它也还是一个GCC,绝对不会成为Visual C++那样能商业化的软件。
可见,国外软件行业的门槛要比中国的高很多了。我觉得我们中国即使再去做这样的东西,也没有多大的意义了。这个门槛你是追不过来的。不仅要花双倍的力气,而且在这么短的时间内,你还要完成别人已经完成过的工作,包括别人所做的测试工作。只有这样,才能做到你的软件与别人有竞争力,能与它做比较。
1.1.2
认清自己的发展
如果连以上认识都不清楚,很可能就以为去书店买一本MFC高手速成之类的书,编两个程序就能成为软件高手。就好像这些书是“黄金”,我学两下,学会了VC、MFC,就能做一个软件拿出去卖了。这种想法也不是不行,最后一定能行,但要有耐心,还要有机遇。机遇是从耐心中产生的,越有耐心,就越有机遇。你得非常努力,要花很多的精力,可能还要走很多的弯路。
如果你是从MFC入手的,或是从VB入手的,则如要做出一个真正的能应用个人领域的通用软件,就会走非常多的弯路。直接的捷径绝对不是走这两条路。这两条路看起来很快,而且在很多公司里面确实需要这样的东西,比如说我这家公司就是为另一个家公司做系统集成的,那我就需要这样的东西,我不管你具体怎么实现,我只需要达到这个目标就行了。
任何软件的实现都会有n种方法,即使你是用最差的那种方法实现的,也没有问题,最后它还是能运行。即使有问题,再改一改就是。但是,做通用软件就不行了,通用是一对多,你做出来的软件以后要面向全国,如果将来自由贸易通到香港也好,通到国外也好,整个产品能销到全世界的话,这时候,通用软件所有做的工作就不是这么简单了。所以说,正确的入门方法就很关键。
如果你仅仅只是想混口饭吃,找个工作,可能教你成为MFC的高手之类的书对你就足够了。但是,如果你想做一个很好的软件,不仅能满足你谋一碗饭吃,还能使你扬名,最后你的软件还能成为很多人用,甚至你还想把它作为一个事业去经营,那么这第一步就非常关键。这时就绝对不能找一本MFC或找一本VB的书学两下就行,而是要从最底层开始做起,从最基本做起。
1.2
高手是怎样练成的
1.2.1
高手成长的六个阶段
程序员怎样才能达到编程的最高境界?最高境界绝对不是你去编两行代码,或者是几分钟能写几行代码,或者是用什么所谓的可视化工具产生最少的代码这些工作,这都不是真正的高手境界。即使是这样的高手,那也都是无知者的自封。
我认为,一个程序员的成长可分为如下六个阶段。
&O 第一阶段
此阶段主要是能熟练地使用某种语言。这就相当于练武中的套路和架式这些表面的东西。
&O 第二阶段
此阶段能精通基于某种平台的接口(例如我们现在常用的Win 32的API函数)以及所对应语言的自身的库函数。到达这个阶段后,也就相当于可以进行真实散打对练了,可以真正地在实践中做些应用。
&O 第三阶段
此阶段能深入地了解某个平台系统的底层,已经具有了初级的内功的能力,也就是“手中有剑,心中无剑”。
&O 第四阶级
此阶段能直接在平台上进行比较深层次的开发。基本上,能达到这个层次就可以说是进入了高层次。这时进入了高级内功的修炼。比如能进行VxD或操作系统的内核的修改。
这时已经不再有语言的束缚,语言只是一种工具,即使要用自己不会的语言进行开发,也只是简单地熟悉一下,就手到擒来,完全不像是第一阶段的时候学习语言的那种情况。一般来说,从第三阶段过渡到第四阶段是比较困难的。为什么会难呢?这就是因为很多人的思想变不过来。
&O 第五阶级
此阶段就已经不再局限于简单的技术上的问题了,而是能从全局上把握和设计一个比较大的系统体系结构,从内核到外层界面。可以说是“手中无剑,心中有剑”。到了这个阶段以后,能对市面上的任何软件进行剖析,并能按自己的要求进行设计,就算是MS Word这样的大型软件,只要有充足的时间,也一定会设计出来。
&O 第六阶级
此阶段也是最高的境界,达到“无招胜有招”。这时候,任何问题就纯粹变成了一个思路的问题,不是用什么代码就能表示的。也就是“手中无剑,心中也无剑”。
此时,对于练功的人来说,他已不用再去学什么少林拳,只是在旁看一下少林拳的对战,就能把此拳拿来就用。这就是真正的大师级的人物。这时,Win 32或Linux在你眼里是没有什么差别的。
每一个阶段再向上发展时都要按一定的方法。第一、第二个阶段通过自学就可以完成,只要多用心去研究,耐心地去学习。
要想从第二个阶段过渡到第三个阶段,就要有一个好的学习环境。例如有一个高手带领或公司里有一个好的练手环境。经过二、三年的积累就能达到第三个阶段。但是,有些人到达第三个阶段后,常常就很难有境界上的突破了。他们这时会产生一种观念,认为软件无非如此,认为自己已无所不能。其实,这时如果遇到大的或难些的软件,他们往往还是无从下手。
现在我们国家大部分程序员都是在第二、三级之间。他们大多都是通过自学成才的,不过这样的程序员一般在软件公司也能独当一面,完成一些软件的模块。
但是,也还有一大堆处在第一阶段的程序员,他们一般就能玩玩VB,做程序时,去找一堆控件集成一个软件。
现在一种流行的说法是,中国软件人才现在是一个橄榄型的人才结构,有大量的中等水平的程序员,而初级和高级程序员比较少。而我认为,现在中国绝大多数都是初级的程序员,中级程序员很少,高级的就更少了。所以,现在的人才结构是“方塔”形,这是一种断层的不良结构。而真正成熟的软件人才结构应该是平滑的三角形结构。这样,初级、中级、高级程序员才能充分地各施所长。三种人才结构对比如图1.1所示。
三种人才结构对比
1.2.2
初级程序员和高级程序员的区别
一般对于一个问题,初级程序员和高级程序员考虑这个问题的方法绝对是不同的。比如,在初级程序员阶段时,他会觉得VB也能做出应用来,且看起来也不错。
但到了中级程序员时,他可能就不会选择VB了,可能会用MFC,这时,也能做出效果不错的程序。
到高级程序员时,他绝对不是首先选择以上工具,VB也好,VC也好,这些都不是他考虑的问题。这时考虑的绝对是什么才是具有最快效率、最稳定性能的解决问题的方法。
软件和别的产品不同。比如,在软件中要达到某个目标,有n种方法,但是在n种方法中,只有一种方法或两种方法是最好的,其他的都很次。所以,要做一个好的系统,是很需要耐心的。如果没有耐心,就不会有细活,有细活的东西才是好东西。我觉得做软件是这样,做任何事情也是这样的,一定要投入。
程序员到达最高境界的时候,想的就是“我就是程序,程序就是我”。这时候我要做一个软件,不会有自己主观的思路,而是以机器的思路来考虑问题,也就是说,就是以程序的思考方式来思考程序,而不是以我去设计程序的方式去思考程序。这一点如果不到比较高的层次是不能明白的。
你设计程序不就是你思考问题,然后按自己的思路去做程序吗?
其实不是的。在我设计这个程序的时候,相当于我“钻”入这个程序里面去了。这时候没有我自己的任何思维,我的所有思维都是这个程序,它这步该怎么走,下步该怎么走,它可能会出现什么情况。我动这个部分的时候,别的部分是否要干扰,也许会动一发而牵全身,它们之间是怎么相互影响的?
也只有到达这个境界,你的程序才能真正地写好,绝对不是做个什么可视化。可视化本身就是“我去设计这个程序”,而真正的程序高手是“我就是程序”,这两种方法绝对是不同的。比如,我要用VB去设计一个程序,和我本身就是一个程序的思维方式,是不一样的。别人也许觉得操作系统很深奥,很复杂,其实,如果你到达高手状态,你就是操作系统,你就能做任何程序。
对待软件要有一个全面的分析方法,光说理论是没有用的。如果你没有经过第一、第二、第三、第四这四个阶段,则永远到达不了高境界。因为空中楼阁的理论没有用,而这些必须是一步一步地去做出来。
一个高级程序员应该具备开放性思维,从里到外的所有的知识都能了解。然后,看到世界最新技术就能马上掌握,马上了解。实际上,技术到达最高的境界后,是没有分别的。任何东西都是相通的,只要你到达这个境界以后,什么问题一看就能明白,一看就能抓住最核心的问题,最根本的根本,而不会被其他的枝叶或表象所迷惑,做到这一步后才算比较成功。
从程序员本身来说,如果它到达这一步以后,他就已经形成了开阔的思维。他有这种开放性思维的话,他就能做战略决策,这对他将来做任何事情都有好处。事实上,会做程序后,就会有一种分析问题的方法,学会怎么样把问题的表象剖开,看到它的本质。这时你碰到任何具体的问题,只要给点时间,都能轻而易举地解决。实际上,对开发计算机软件来说,没有什么做不了的软件,所有的软件都能做,只是看你有没有时间,有没有耐心,有没有资金做支撑。
这几年,尤其是这两三年,估计到2005年前,中国软件这个行业里面大的软件公司就能形成。现在就已经在形成,例如用友,它上市后,地位就更加稳固了。其他大的软件企业会在这几年内迅速长大。这时候,包括流通渠道、经销商的渠道也会迅速长大。也就是说,到2005年以后,中国软件这个行业的门槛比现在还要高很多,与美国不会有太大的差别。此时,中国软件才真正体现出它的威力来。如果你是这些威力中的一员,就已经很厉害了。
别人可能知道比尔·盖茨是个谈判的高手,是卖东西的高手,其实,比尔·盖茨从根本上来说是个程序高手,这是他根本中的根本。他对所有的技术都非常敏感,一眼就看到本质,而且他本身也能做程序,时常在看程序。现在他不做董事长,而做首席设计师,这时他就更加接近程序的本质。因为他本身就有很开阔的思维,又深入到技术的本身,所以他就知道技术的方向。这对于一个公司,对他这样的人来说,是非常重要的。
如果他判断错误一步,那公司以后再回头就很难了。计算机的竞争是非常激烈的,不能走错半步。很多公司以前看上去很火,后来就
销声匿迹了,就是因为它走错一步,然后就不行了。为什么它会走错?因为他不了解技术的本质在哪里,技术的发展方向在哪里。
比尔·盖茨因为父母是学法律的,所以他本身就很能“侃”,很有说服力,而他又是做技术的,就非常清楚技术的方向在哪里,所以他才能把方向把握得很准确,公司越来越大。而别的公司只火一阵子,他却火了还会再火。就算微软再庞大,你如果不把握好软件技术的最前沿,一样也会玩完。就像Intel时刻把握着CPU的最新技术,才能保证自己是行业老大。技术决定它的将来。
所以,程序员要能达到这样的目标,就要有非常强的耐心和非常好的机遇才有可能。事实上,现在的机会挺好的,2005年以前机会都非常大,以后机会会比较小。但是,如果有耐心的话,你还是会有机会的,机会都是出在耐心里。我记得有句话说“雄心的一半是耐心”,我认为雄心的三分之二都是耐心。如果你越有野心,你就越要有耐心,你的野心才有可能实现。如果你有野心而没有耐心,那都是胡思乱想,别人一眼就能看穿。最后在竞争中,对手一眼就看到你的意图,那你还有什么可竞争的?
1.2.3
程序员是吃青春饭的吗
很多人都认为程序员是三十岁以前的职业,到了三十岁以后,就不应再做程序员了。现在的很多程序员也有这种想法,我觉得这种想法很不对。
在20世纪80年代末到90年代初,那时软件还没有形成行业,程序员不能以此作为谋生的手段时,你必须转行,因为你年轻的时候不用考虑吃饭的问题,天天“玩”都可以,但是以后就不可能了。
据我了解,微软里面的那些高手,几乎都是四五十岁的,而且都是做底层的。他们是上世纪70年代就开始“玩”程序的,所以对于整个计算机,他们是太清楚了。现在有些人主观臆断地希望微软第二天倒闭就好了,但那可能性太小了。因为那些程序员是从CPU是4004的时候开始,玩到现在奔腾IV,没有哪一代东西他们没有经历过。
你知道他们现在正在玩什么吗?现在正在玩64位的CPU。你说你普通的程序员,有这个耐心吗?没有这个耐心,你绝对做不了,你也绝对做不了高手。他为什么能做?因为他不仅是玩过来的,而且他还非常有耐心,每一步技术他都跟得上,所以对他来说,没有任何的难度和压力。
因为计算机技术没有任何时候是突变的。它的今年和去年相差不会很大,但是回过头来看三年以前的情况,和现在的距离就很大。所以说,如果你每年都跟着技术进步的话,你的压力就很小,因为你时刻都能掌握最新的技术。但是,如果你落下来,别说十年,就是三年,你就赶不上了。
如果你一旦赶不上,就会觉得非常吃力;如果你赶不上,你就会迷失方向;如果你迷失了方向,你就觉得计算机没有味道,越做越没劲。当你还只是有个思路的时候,别人的产品都做出来了,因为你的水平跟别人相差太远,人家早就想到的问题,你现在才开始认识。水平越高,他就看得越远,那么他的思维就越开阔;水平越低,想的问题就越窄。
64位CPU是这个十年和下个十年最重要的技术之一,谁抓住这个机会,谁就能抓住未来赚钱的商机。CPU是英特尔设计的,对这一点他肯定清楚。举例来说,如果从64位的角度来看现在的32位,就像从现在的角度去看DOS。你说DOS很复杂吗?当你在DOS年代的时候,你会觉得DOS很复杂。你说现在的Windows不够复杂吗?Windows太复杂了,但是你到了64位的时候再去看Windows,就如同现在看DOS一样。
整个64位系统的平台和思维方式、思路都比现在更开阔,打个比方说,现在的Windows里面能开n个DOS窗口,每个DOS窗都能运行一个程序。到达64位的时候,操作系统事实上能做到开n个X86,开n个Windows 98,然后再开n个Windows 95都没有问题,系统能做到这一步,甚至你的系统内开n个Windows NT都没有关系。这就是64位和32位的差别。所以,微软的那些“老头”,四、五十岁的那几个做核心的人,现在正在玩这些东西。你说微软的技术它能不先进吗?是Linux那几个玩家能搞定的吗?
微软的技术非常雄厚,世界计算机的最新技术绝对集中在这几个人手里。而且这几个人的思维模式非常开阔,谁都没有意识到的东西他早就开始做了。现在64位的CPU都出来一二年了,你说有什么人去做这些应用吗?没有,有的就是那几个UNIX厂商做好后给自己用的。
所以,追求技术的最高境界的时候,实际上是没有年龄限制的。对我来说,现在都三十三了,我从来没有想过退出这行,我觉得我就能玩下去,一直玩到退休都没有问题。我要时刻保持技术的最前端,这样的话对我来说是不困难的,没有任何累的感觉。
很多人说做程序不是人干的事情,是非人的待遇。这样,他们一旦成立一个公司,做出一点成绩,在辉煌的时候马上就考虑退出。因为他们太苦了,每天晚上熬夜,每天晚上烧了两包烟还不够,屋子里面简直就缺氧了,好像还没有解决问题。
白天睡觉,晚上干活,那当然累死了,这是自己折腾自己。所以,做程序员一定要有一种正常的心态,就是说,你做程序的时候,不要把自己的生活搞得颠三倒四的。如果非得搞得晚上烧好多烟才行,这样你肯定折腾不到三十岁,三十岁以后身体就差了。
事实上,我基本上就没有因为做程序而熬夜的。我只经历过三次熬夜,一次是在学校的时候,1986年刚接触计算机时,一天晚上跟一个同桌在计算机室内玩游戏,研究了半天,搞着搞着就到了天亮,这是第一次。然后在毕业之前,在286上做一个程序。还有一次就是超级解霸上市前,那时公司已吹得很大了,那天晚上没法睡觉。
一般来说,我也是十二点钟睡觉,第二天七点就起了。所以说,只有具有正常的生活、正常的节奏,才有正常的心态来做程序员,这样,你的思路才是正常的,只有正常的东西才能长久。搞疲劳战或者是黑白颠倒,时间长久后就玩不转了,玩着玩着就不想玩了。
只要你不想玩,不了解新技术,你就会落后,一旦落后,你再想追,就很难了。
1.3
正确的入门方法
在这一节中,主要讲从我的经验来看,一般程序员需要注意的地方。教你怎样去具体学习不是我的责任,你可以去任何一个书店去找一本书回来自己看就可以了。这里只是对这些书做一些补充以及一些平常从来没注意的内容。
入门最基本的方法就是从C语言入手。如果以前学过BASIC语言的话,那么从C语言入手是非常容易的。我就经历了一个过程,根本不觉得这中间有太大的难度。其实,C语言本身和BASIC没有什么两样。BASIC每个所谓的命令在C语言里面都可以做成一个函数来实现,那么你就能用那个命令组合成整个程序。从这个角度来看,BASIC和C语言没有本质的差别。C语言就是入门的正确方法,没有其他。
现在的C语言本身就包含了嵌入汇编,使学习汇编语言的时候更加方便。你可以忽略掉纯汇编里面的很多操作。也许有人觉得这个方法太慢了。但要知道,工欲善其事,必先利其器,要想成功,没有一个艰苦的过程是不可能的,所以一开始的时候就要有耐心。如果你准备花5年的时间成为高手,那我敢说,你根本不用等到5年,你只要有这个耐心就足够了,你可能2年~3年内就能达到目标。但如果你想在一年时间内就成为高手,即使5年后,你还是成不了高手。
我们公司1998年招的开发人员都是应届大学毕业生。很明显,有人好像什么都会,又会CorelDraw,又会Photoshop,又会Flash,又会C++,甚至VB也会。可是这样的人到现在还是全都会,但是什么事情也做不好,做的东西“臭”死了。但其中有一个人就不同,他以前甚至连Windows的程序都没有做过,只会在DOS下做几个小程序。但当我们把超级解霸的程序给他看,让他去研究的时候,他只用一周的时间,就迅速掌握。他那个月进步非常快,几乎就是一生中进步最快的阶段,这就是一个质的飞跃。
从基本入手以后,当你的积累到达一个阶段以后,就会有一个质的飞跃的阶段。事实上,我也有这么一个阶段,这个阶段也是我离开大学以后,真正去公司做事的时候。当我真正拥有一台计算机后,我把所有以前积累的问题在一个月内做了探讨以后,感觉自己的水平迅速提高。
入门和积累是很重要的。事实上,到达高手的境界以后,不管什么语言不语言的,其实都根本不用去学,只要拿过来看两天,就全部精通。如果你没有入门,即使去书店找n本书,天天背它,你也不会成为高手。
所有的语言只是很花哨的表面东西。高手马上就能透过它的表象而看到它的本质。这样才是真正的高手。他不需要再去学什么Java,或者其他什么语言。当他真正要写个Java程序的时候,只要把Java程序拿过来看一看,瞄一瞄书,就全都清楚了。如果这时他学VB就更容易了,我想他不用一天的时间,就能学会。到达高手的境界以后,所有的事物都是触类旁通的。
当你成为C语言的高手,那么就你很容易进入到操作系统的平台里面去;当你进入到操作系统的平台里去实际做程序时,就会懂得进行调试;当你懂得调试的时候,你就会发现能轻而易举地了解整个平台的架构。这时候,计算机基本上一切都在你的掌握之中了,没有什么东西能逃得出你的手掌心。
上面只是针对程序的角度说明,另外一点也很重要,即好的程序员必须具备开放性思维,也就是思考问题的方法。程序员,尤其现在很多的程序员,都被误导从MFC入手,这就很容易形成一种封闭式的思维模式。这也是微软希望很多人只能学点表面的东西,不致成为高手,所以他大力推荐MFC之类的工具,但也真有很多人愿意去上他的当,最后真正迷失方向。说他做不了程序吧,他也能做程序,但是如果那个程序复杂一点,出现问题时,问题出在哪里就搞不清楚了,反正是不清楚。如果你真正有一种开放性的思维,在你能够成为高级程序员的时候,对MFC这些是不屑一顾的,MFC、VB根本不会在考虑的范围之内。
事实上很多人,包括外面很多公司里面工资挺高的人,可能一个月能拿五、六万的这些人,他们的思维也不一定能达到很高的境界。但是,他确实做了很多的事情,已经有很好的积累了。但要上升到更高的境界上,就要有正确的思维方法。这就是为什么比尔·盖茨说,他招人的时候宁愿招一个学物理,而不是学编程的。学物理的人会有非常非常广的思维,他考虑的小到粒子,大到宇宙,思维空间非常广阔,这样,他思考问题的时候,就会很有深度。
有人研究物理研究得比较深的时候,他能针对某个问题一直深入进去。很多写程序的人只会注意到这行代码或那行代码,则比较起来则显得肤浅。所以,编程的时候也要深入进去,把你的爱好、你的所有思维都放进去,努力做到物我合一的境界。
1.3.1
规范的格式是入门的基础
以前所有的C语言的书中,不太重视格式的问题,写的程序像一堆堆的垃圾一样。这也导致了现在的很多程序员的程序中有很多是废码、垃圾代码,这和那些入门的书非常有关系。因为这些书从不强调代码规范,而真正的商业程序绝对是规范的。你写的程序和他写的程序应该格式大致相同,否则谁也看不懂。如果写出来的代码大家都看不懂,那绝对是垃圾。如果把那些垃圾“翻”半天,勉强才能把里面“金子”找出来,那这样的程序不如不要,还不如重新写过,这样,思路还会更清楚一点。这是入门首先要注意的事情,即规范的格式是入门的基础。
1. 成对编码
正确的程序设计思路是成对编码,先写上面的大括号,然后马上写下面的大括号。这样一个函数体就已经形成了。它没有任何问题。然后,比如你要写个for循环,这时候先申明一个变量I,再写这个for循环。写上面的大括号,马上写下面的大括号,然后再在中间插一二行代码。插这段代码后,如果你又要用到新变量,则再在头上添加新的变量,然后再让它进行工作。这就是一种成对编码。
这样,当你用到一个内存的时候,写一个分配函数分配一块内存,马上就接着写释放这块内存的代码。然后你再在中间插上你要用这个内存做什么。这是正确的快速的编程方法。否则,你去查或调试代码都无从下手。针对这个程序来说,如果用成对编码,则它任何时候都是可以调试的,不需要你整个程序都写完后才能进行调试。
它是任何时候都可以编译调试的,甚至你写了两个大括号,中间什么也没有,它是空的时,你都可以进行调试。你写了第一个for循环,它也可以进行调试,当你又写了一个分配内存、释放内存以后,它还可以进行调试。它可以编译运行,里面可以放断点,这就是成对编码。
成对编码就涉及到代码规范的问题。为什么我说上面一个大括号,下面一个大括号,而不说成是前面一个大括号,后面一个大括号呢?如果是一般C语言的书,则它绝对说是后面加个大括号,回过头前面加个大括号。事实上,这就是垃圾程序的写法。正确的思路是写完行给它回车,给它大括号独立的一行,下面大括号也是独立的一行,而且这两个大括号跟那个for单词中间错开一个TAB。
集成环境的TAB首先要设成8,因为TAB的基本定义就是8,而现在的VC把它设成了4,这样使得你编出的程序放到一个标准的环境里看的时候就是乱的。
代码一定不能乱,一定要格式非常清楚,这点使你写的程序我能读,我写的程序你也能读,不需要再去习惯彼此的不同写法。
而且结合成对编码思维,这时候你去读一个程序的时候,你会发现,你读程序的方法变了。以前读程序的时候,你可以先去读它的变量是什么,然后再读第一行、第二行,读到最后一个大括号,这是一种读程序的方法。现在就不一样了,现在读程序的时候就养成了一种习惯,就是分块阅读程序,很明显两个大括号之间就是一块代码。
那么写出一个程序后,你要读这个程序是干什么的,只要看这个大括号和那个大括号之间的部分就可以了,不需要再去读其他的代码是干什么的。比如,你从Linux中或网上下载了一个“烂”程序后,该怎么去阅读它?最好的方法是先把程序所有的格式都整理好,先别去读它。把所有的格式按照这种规范化的方法,把它的括号全部整理好。这时候你再读那个程序,只要半分钟就读懂了,但是你可能要整理一个小时。但如果不这样做,你可能读两个小时都读不清楚该程序。
这点绝对不会有人告诉你,现在没有人去讲解这方面的技巧。这也是我写了那么多的程序,才总结出来的。一开始的时候,我也像那些教科书所教导那样写,后面放个大括号,前面放个大括号,甚至括号连括号,一连四个括号,每个括号对哪个最后都找不清楚。编译告诉你好像少了一个括号,于是找呀,找呀,上面找,下面找,而这个程序又很大,只有一个函数,上面在上屏,下面在下屏,最后翻来翻去也翻不出。
所以我就想,大括号之间要互相对应,即使不在一个屏幕内,也能很容易地看到它,因为只要光标落在这个大括号里面,往上去找,即能找到它头上的那个与此对正的,而且这些代码是在一起的。这一层代码和下一层代码是互相隔开的,我只要读这层代码,下面那一层代码就不需要了。
比如,它有n个for循环的时候,我只想看某一个for循环,这时我只要对正大括号,它的光标往上走,一下就能找到了。如果按照教科书那样写的话,你要读呀,读呀,要把所有的代码,把所有的for
循环都读一遍,才可能找到你要的东西。这就是成对编码和规范化的方法(详细叙述请参考代码规范一章)。
代码中如果不包括正确的思路,那该代码就没有什么用。如果是一个代码爱好者去收集代码,而现在网络上代码成群,Linux本身就带了一大堆的程序,那些程序对你真的有用吗?我看不见得。而且那些程序还在不断地升级,那程序还会有新版,如果你把它拿来看一下,对你来说其实没什么价值。
那怎么样使得它对你有用?就必须用上面所说的方法,经过这么处理以后,你就能真正取到它其中的设计思路,这样才能变废为宝。如果是MFC之类的东西,那你就不用找了,因为即使找,也找不出有价值的东西,全部是VC自动给你生成的一堆堆的垃圾框架,相对于网上Linux程序来说,它可能更“臭”一些。
在软件没有形成行业,程序等同于软件的时候,那时候程序很容易体现出价值来。只要得到代码,就相当于得到这个软件。但现在就不同了。现在的程序都不是几行,你写出的程序,如果又没有注释,格式又很乱,你拿过来给我,我还得花很长的时间才能读得清楚,那这样的程序的代码有价值吗?
我经常听到一些程序员在外面兜销代码,很多是学校的学生,尤其那些素质比较差的研究生,和老师做了一个项目后,他拿出来到外面到处去卖,但是他最后可能卖出去吗?最后可能还是没卖出去,因为那个程序很庞大。如果某个公司买了这个程序以后,该公司还得招一个人去读这个程序,当这个人读懂以后,他又离职了,那公司买这个代码干嘛?
2. 代码的注释
代码本身体现不出价值来,有价值的代码一定是不仅格式非常规范,而且还要有很详细的设计思路和注释,这个是很重要的。首先要养成这种习惯,教科书里面很少讲为什么要做注释,注释应该怎么注。有些人爱在哪儿下注释就在哪儿下注释,甚至在语句中间也加,中间也可弄两个斜杠放两个花括号写点注释。
注释格式是非常重要的,但很少有人去注意它。现在的程序如果没有注释,则基本上是没法用的,也就跟你拿一个可执行程序没什么两样,你拿过来还不能随便改,你改了后编出来的程序绝对不能用。所以,程序如果没有详细的注释,别人就算拿到了代码也没有用,体现不出它的价值来。
Linux是个操作系统,很厉害呀!其实那些程序你拿回来,耐心地去读它,会发现,它里面乱得很,那个内核程序除了作者自己能读懂外,别人可能要花很长的时间才能读懂。Apache的作者对自己Apache那套代码是很清楚,但换一个做浏览器的人去读,也会很困难。一般人只把代码复制下来后,打个BUILD命令看看能不能正确地编译,最后能正确编译的程序就是好的,如果不能正确编译的程序就删掉吧,再下载一个,因为他没有正确的对待代码的那种思维,而只是认为那代码本身才有很大的价值,不用关心有没有注释。
如果代码没有注释和规范,是没有价值的,这也是现在为什么很多的个人跑去卖源程序的时候,很多的公司都不要。我们不是说没有技术,任何程序都能做,只是时间的问题,而且像视频中有的技术,比那些卖代码的技术还要深得多。真正要做一个有价值的程序,开发程序的思维就很重要,这种思维的具体体现就在注释及规范的代码本身。
1.3.2
调试的重要性
调试是很重要的一个部分。所有的程序都是调试出来的,不是写出来的。讲怎么去调试,实际上就是讲一种解决问题的思路。所有的程序写出来后一定是有问题的,既然有问题,就一定会有一个解决问题的思路。解决问题的方法就是调试的方法。
用VB或者是MFC做出来的程序,先运行一遍看看什么地方有问题,如果发现有问题,重新改一改,然后又重新运行。这种方法是还没有入门的调试方法,即是看直接的表象。这种方法既浪费时间,又不能消除隐患。
调试是很重要的内容,如果要进入高深境界,调试是除了了解设计程序、平台以外,一个非常重要的难关。如果要成为高级程序员,就必须过这一关。如果不懂调试,则永远成不了高手。在学习调试的过程中,对汇编语言、体系结构会有进一步的了解。
你可能觉得我把调试的作用说得言过其实了,举例子说明一下吧。请把以下的C程序改写成汇编代码:
extern int In[],Out[];
for(i=0;i&100;i++)
{
Out[i]*=In[i];
}
我发现90%的人写出来的汇编代码可能是不正常的或有错误的。要么是不了解32位汇编,要么是不循环,要么只有循环没有处理等。这是为什么呢?因为就算是一段小小的代码,如果没有经过调试,也可能错误百出。
如果你是初级一点的程序员,则如果程序出了问题,也不知道原因所在。怎么回事呀?我就是搞不清楚。要搞清楚首先要调试,这就涉及到调试的问题。比如说,放到一个文件里面的,它出错了,我查程序看了n遍,它就是没有任何问题,这时候该怎么办呢?这时的解决方法就是调试,调试能使得一个程序正常地运转起来。如果对于程序员来说写这个程序可能只用了一天的时间,但是调试可能会花他二三天的时间。一个程序绝对是调试出来的,不是编出来的。如果说哪个系统是编出来的,那它肯定会有很多性能方面的问题,包括可能有不可预测的各种各样的问题。
程序出现问题的话,要能考虑到各种各样可能的情况,绝对没有任何臆测。比如,有可能完全是编译器的错误,也有可能因你程序里面增加了什么,而对程序产生干扰,甚至还有一种可能是你的指针基本就没有给它赋值,指向了别的地方,把别的东西破坏了。这些情况太多了。还有一种常见的错误,即MFC里面很常见的一种设计思维,就是任何一个东西,只管创建,不管释放、销毁。这种思路是现在很多程序员做的程序没用几下就会死机的原因。这绝对是错误的设计思路,而MFC让你这么做,就是让你永远成不了高手,你写的程序永远不可能稳定。
MFC里面的所有的结构也好,变量也好,只需要你去分配一个,几乎就不需要你去释放它。这绝对是错误的,程序一定要成对编写。成对编码是快速编写程序的一种方法,而教科书里面讲的那些都是从头到尾去编。先把那个什么变量编写上,再写第一行,再写第二行,再写第三行,最后再写个大括号。这种方法绝对是错误的。对于现在的程序来说,它效率很慢,没法即时调试,因为只有最后把所有的程序做完以后,才能进行调试,所以在这中间出现错误的几率就积累得非常大了。
开放性思维
要具备开放性思维,就必须了解包括从CPU的执行方法,到Windows平台的运转,到你的程序的调试,最后到你要实现的功能这一整套的内容,只有做到这样,才能真正提高。如果你的知识范围很窄,什么也不了解,纯粹只了解语言,那你的思维就会很狭隘,就会只想到这个语言有这个函数,那个语言没有那个函数,这个C++有这个类,那个语言没有这个类等。而真正要做一个系统,思维一定要是全面的,游离于平台之上的系统和实际的应用软件是不现实的。
这种所谓理想化,已经有很多人提出是不现实的。所以,任何一个软件一定都是跟一个平台相关联的,脱离平台之上的软件几乎都是不能用的。这就必须对平台的本身非常了解。如果你有平台这些方面的知识,这样在思考一个问题的时候,能马上想到操作系统能提供些什么功能,我再需要做些什么,然后就能达到这个目标。这就是一种开放的思维。
在开放的思维下,我要做这个程序的时候,就会考虑怎么把它拆成几个独立的、分开的模块,最简单的,怎么把这个模块尽量能单独调用,而不是我要做个很大的EXE程序。一个很普通的程序员,如果他能够考虑到将程序分成好几个动态库,那么它的思维就已经有点开放性了,就已经不是MFC那些思维方式了。思考问题的时候能把它拆开,就是说,任何一个问题,如果你能把它拆开来思考,这就是简单的开放性思维。
但光会拆还是不够的,尽管有很多人连拆都不会。很多教科书中的程序,要解决问题的时候,就一个main,以后就是一个非常长的函数。这个main函数把所有的事情都解决了。如果连函数都不会分的话,则就是典型的封闭式思维。
这样的人不是没有,我是碰见过的。一些毕业生做的程序就有这种情况。所有的问题都由一个函数来解决。他就不会把它拆成几个模块。我问他,把一件工作拆成几件模块不是更清晰吗?他说,拆出来后的模块执行会更慢些。这就是很明显的封闭式思维和非封闭式思维的区别。
你看MFC的思路,那就是一层套一层的,要把所有的类都实现了,然后继承。它从CWnd以后,把所有的东西都包括进去了,组成一个巨型的类。这个巨型的类连界面到实现统统包括在里面。这时你怎么拆?根本就没有拆的方法,这就是封闭式思维。
如果一个系统、一个程序不能拆的话,则它基本上是做不好的。因为任何一个程序,如果它本身的复杂度越大,它可能出错的几率就越大。比如最简单的,哪个函数越大,则该函数的出错几率就越大。但如果把该函数分成很多小的函数,每个小的函数的出错几率就会很小,那么组合起来的整个程序的出错几率就很小。这就是为什么要把它拆出来的原因。
你用C++来实现的方法也是一样的。你要把它拆成许多的接口,如果能做到这样,你就能把它独立起来,甚至你能把它用动态库的方法去实现。动态库是现在的程序非常重要的一块。
1.4.1
动态库的重要性
有了动态库,当你要改进某一项功能的时候,你可以不动任何其他的地方,只要改其中你拆出来的这一块。这一块是一个动态库,然后把它改进,只需要把这个动态库调试好后,整个系统就可以进行升级。
但如果不是这样,你的整个程序是独立的文件,然后,另外的功能也是一个独立的文件,把这个程序编译成一个EXE,这就不是动态库的思想。按道理,我只改这个文件,其他系统也不需要进行调试。理论上看起来是一样的,而实际的结果往往就是因为你改动了这个文件,使得原来跑得很好的整个系统,现在不能跑了或者出现了很奇怪的现象。如何解释这个问题?事实上,这就涉及到编译器产生代码的方法,如果不了解这点的话,永远找不出问题来。
不存在没有BUG的编译器,包括VC,它也会产生编译上的问题。就算把这些问题都排除,你的软件也可能因为你加了某些功能,而影响了其他的文件,这个几率甚至非常大。这又得把你以前的测试工作重头再来一遍了。
动态库和EXE有什么不同呢?
动态库,包括它的代码和数据都是独立的,绝对不会跟其他的动态库串在一起。但是,如果你把所有功能放到一个EXE的工程里面,它的数据和代码就都是放到一起的,最后产生可执行程序的时候,就会互相干扰。而动态库就不会,这是由操作系统来保证的。从理论上看,动态库也是一个文件,我做这个工程的时候也是一个独立的文件,但它就会出现这样的问题。
1.4.2
程序设计流程
程序设计流程其实很简单。第一步就是要拆出模块,如果你有开放性思维,则任何软件都非常容易设计。怎么设计呢?首先,拿到问题的时候,一定要明确目标;然后,对操作系统所提供哪些功能,程序怎么跟操作系统接口考虑清楚;接着,就是“砍”,把它分开,要把它拆成一个个的独立的模块;最后,再进一步去实现,从小到大地进行设计。
首先“抓”马上能进行测试的简单的模块,就像刚才说的成对编码那样,写任何一个部分都要进行调试,每个部分最好能独立进行调试。这样,每个部分都是分开的时候,它都有一定的功能。当把所要做的功能都实现后,组合起来,再进行通调就可以了。
决定一个软件的成败还是得看该软件设计的思维是否正确。我们也试过,即使你把那些所谓的软件写得再明白也没有用,如果实现这个软件的思路不对,则下面的工作根本就没有必要。
做软件时,一定要把注释写进去。这样写成的软件如果要改版的话,就很容易,因为你的整个系统是开放性的,那么你要增强某些功能的时候,都是针对其中的某个小项做改进,只要改它就是了。如果那个功能是全新的,则它本身就是一个独立块,只要去做即可。
现在很多开发工具都提供了自动化设计的功能,在生成新的程序的时候,只要设置好一些条件,就能自动产生程序的框架,这是一种趋势吗?
其实,这种方法不太适用通用软件的开发,针对某个公司做个ERP系统,可能会管用,但是那些方法拿不到通用软件里面来。通用软件绝对是一行一行地编码产生出来的,而且每一行编码的结果要达到一种可预测性。
什么叫可预测性?就是你写程序的时候,如果发现某一种症状,马上就能想到该症状是由于哪个地方出了错,而不是别的地方,也就是从症状就能判断出是哪些代码产生了问题,这就是可预测性。
如果你用MFC来“玩”的话,即使它出错了,你也可能不知道错误在哪里,它的可预测性就很差。做软件时,如果它的可预测性越高,解决问题的方法就越快。如果某用户说我出现什么状况了,你马上就可以断定错误,而不用去搜索源代码,就能想到程序可能是什么地方有问题,则这就是可预测性。
1.4.3
保证程序可预测性
设计程序的时候,如何保证可预测性呢?答案就是我们上面所说的,所有的代码必须是经过测试的,必须是一步一步调试过的。只有经过你调试过的代码,你才能知道这个代码做某种运算的时候,它是怎样的执行方法。如果你不知道它的执行方法,你没进行过调试,则你就没有任何预测性。要达到可预测性,代码在汇编级是怎么执行的,你都得非常清楚。代码对哪个部分进行了什么操作,你都得知道。如果达不到这点,你的可预测性就很差。
比如,有些程序,你看它的C或者C++的源代码时,都看不出任何的问题。你看静态的程序时看不出任何问题,动态的程序调试你也看不出任何问题,这时,你必须把它的汇编打开,看一看它具体的操作,才能知道。所以说,开放性思维非常重要,你必须从最低层到最上层都要清楚。VC本身提供了一个汇编的调试环境,但是打开汇编后,如果你都看不懂,那你说怎么调呢?调什么?如果一个程序经过调试出来,则它会出错的地方你马上就会知道,只要看一些表现,就知道它有些什么问题。
比如说,我们做“大眼睛”的时候有个这样的现象。当要显示一个很大的图的时候,屏幕上只能显示其中的一小块,这样就可能需要拖动整个图像,但是拖的时候,如果在Windows 2000或Windows XP系统下就会发现,一旦我将图像拖到右下角时,图像就一下到左上角去了。该图像在右下角没有到底的时候还是显示正确的,但一旦到底,就把右下角转到左上角去了,如图1.2所示。
这是怎么回事?在Windows 98和Windows 95下,从来没有这个问题,而且如果图像不到右下角这一行,只差一点,它也不会出现这样的问题。为什么在Windows 98下没有这样的问题,在Windows 2000下会有呢?难道是我的程序有问题?
图像显示问题示意图
这时,我就做了一个区域的比较,即看这个区域和整个这个图像的区域,是否中间运算有错误。但程序是调用Windows本身的API,我就怀疑是不是这个API出问题了。于是又重新写了一个区域相交部分,一步一步去查它,也没有任何问题,在任何情况下都是好的,但是到达右下角时,图像就会翻过来。经过以上两个步骤后,我就能确定,这是Windows操作系统的问题,Windows 98下没有这个问题,Windows 2000有,Windows XP也没有改过来。这是操作系统的原因,绝对不是软件的问题。
为什么会出现这样的问题?这是因为微软设计系统的那些家伙自以为聪明。只要图像的左上角是0,不管三七二十一,肯定往下面放,但是它的图像是正向位图,所有的位图设计的时候是倒过来的。而一个正向位图的高度是负的,否则它显示的时候是倒过来的。高度是负的时候,这个0发生了变化,从上向下的,那么他设计操作系统的时候,只看了0而没去看高度,这时他没做条件处理。他的想法是为了加速这个位图的速度,是做优化的结果,但结果就出错了,而到现在他也没有解决这个问题。
所以,可预测性在这里就显得很重要了。当出现这个问题时,能想到要么就是区域合并有问题,要么就是直接显示的这个函数有问题。区域合并的问题可以解决,我写个函数还不行吗?我一步一步地去跟踪,就能肯定这个API有没有问题,最后得出结论是有问题,也的确是它有问题。如果你不会调试的话,这个问题你永远也查不出来;如果你不了解操作系统,你永远不会想到操作系统会出问题;如果你不了解这个平台,你根本就不知道问题所在。所以,要成为一个高手,视角一定要从里到外,从点到面非常开阔。如果你局限在一个封闭的思维里,做系统就很难。
注册时间 10:45:09
最后登录 10:47:00
用户头衔:注册会员
状态:我不在线
接口的建立方法
很多程序员对C++和C之间有什么联系和差别,总是搞不清,只是知道C++好,但遇到C和C++之间的接口,就会不知所措。往往花了很长的时间还是没有结果,这是为什么呢?
这就是因为不知道C和C++语言的本质是什么。所以,学习任何东西不追根求源,是不可能成为真正的高手的。在学习时,一定要了解事物的本质是什么,它是怎么被运行的。
为了方便大家的学习,本书建立了一个工程,可以从光碟的第四章\Demo目录下找到。
可以看到,在当前的工程中有两个文件:
(1)C语言的文件Demo.c。
(2)以CPP为后缀的C++语言的API.CPP文件。
1. C接口的方法
主程序main函数中要调用API.CPP中的int API(int A)函数。
现在可以在main函数中,输入调用的语句。
Demo.c
main(int argc,char *argv[])
{
char Buffer[512];
if(argc!=2)
printf("Usgae:Test xxx\n");
Buffer[0]=API(3);
//调用API.CPP中的API函数
printf("Test !\n");
return 0;
}
API.CPP
#include &windows.h&
int API(int A)
{
return ~A;
}
接着对程序分别进行编译,一切没有问题。但当进行链接的时候,就会出现如下的错误:
------------------Configuration: Demo - Win32 Release------------------
Linking...
Demo.obj : error LNK2001: unresolved external symbol _API
..\BIN/Demo.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.
Demo.exe - 2 error(s), 0 warning(s)
奇怪!?我们明明定义的是名称为API的函数,但为什么编译器说没有定义_API呢?把它反过来,在CPP中调用C的函数,还是会出现同样的问题(读者可以试一试)。
为了搞清这个问题,就必须搞清楚到底C的函数和CPP的函数在编译之后,会变成什么样子。
因为C++的一个很重要的属性是可以重载,那什么是重载呢?我们可以从函数的角度来解释,重载就是一个函数名,在一个类中可以出现多次,并且每个函数体还不同。也就是C++所说的相同的形式,不同的操作。
这样就有一个问题,C++是怎么分清一个函数的?究竟需要调用哪个函数呢?C++会通过不同的参数来选择不同的、具有相同名称的不同的实现函数的调用(有点绕口令的味道,其实是要找到自己的运行体)。C++语言会解决这个问题。
还有一个问题,C++的函数被编译后,怎么样才能被正确地找到呢?
CPP是这样做的。在生成C++的函数时,函数附带了一些附加的参数信息。
现在我们可以在API.CPP的文件中再增加一个API的同名函数,这个API的函数就被重载了。
float API(float A)
{
return -A;
}
接下来,重新编译这个文件。没有问题,一切正常。
但这样在C中就有问题了。因为C分不清楚到底哪一个才是自己要调用的。
为了探索这个问题,就需要输出其map文件。
只要在“Project Settings”的“Link”页面中,选取“Generate mapfile”。重新编译,就会在工程目录处生成一个.map为后缀的文件,如图4.15所示。
输出map文件
重新编译此工程,就可以在release目录找到demo.map的文件名。
用文本工具打开,但什么内容也看不见,这时怀疑可能是连接没有成功。先停止demo.c中的API函数的调用。再编译一次,这次成功通过。再打开demo.map,将出现很多函数的说明,可以用“查找”工具找到API函数的定义,如下:
Address
Publics by Value
Lib:Object
?API@@YAHH@Z
?API@@YAMM@Z
0040100f f
?Hello@XXX@@QAEXXZ
Demo.obj
可以看到,在CPP中:
&O int API(int A) 变成了 ?API@@YAHH@Z
&O float API(float A) 变成了 ?API@@YAMM@Z
&O void main()
变成了 _main
这时可能大家已明白,为什么在调用API.CPP中的函数API中,编译说不能找到_API,原来是在C语言被编译的过程中,自动地加上了下划线。例如,main的函数在编译之后就变成了_main,而函数API就在编译时变成了_API。
在C++中,函数的参数就被扩展了。可以看到,两个API名的函数前面加了一个问号,紧跟着是两个“@”符号,其作用就像左括号,而右边的“@”符号就相当于右括号,而中间的字母就是用来表示参数的。
可以看见,两重载的函数使用了不同的参数,也就用不同的字母表示了。一个是整数,一个是浮点数,当这两个函数进行连接时,在相应的C++的连接过程中,编译器也会以编译出来的名字到对应的OBJ文件中进行查找。其实,在C++在编译过程中,会把参数的类型、名字和函数的名字组合成一个函数,这样就和程序中定义的函数名字相一致。
这样,两个同名函数在OBJ中就是不相同的,对编译器来说,它们是不同的。对可执行代码来说也是不同的,只从源程序来看它们是相同的。所以,当程序很复杂时,如果大量地使用重载的话,很有可能带来灾难性的后果,可能即使出现错误也无法找到错误源,因为它本身就被混淆了。
现在我们回过头来看出错信息,编译器报告的是找不到这个_API函数。我们在影像输出文件中也没有找到对应的函数,在输出中有的只是?API@@YAHH@Z和?API@@YAMM@Z,所以,这个程序基本就不可能连接上。
原因知道了,那怎么解决这个问题呢?
想到的最直接的方法就是修改C++文件的编译结果,让它去产生一个对应C的函数名,也就是按C的方法生成对应的函数名。所以,我们可以在C++文件的头部加上如下的代码段:
extern "C"
{
int API(int A);
}
extern "C" 语句的作用就是把C++的函数按C的约定编译。接下来继续编译,链接,一切正确。运行,没问题!看一看影像文件,就会变成如下:
Address
Publics by Value
Lib:Object
?API@@YAMM@Z
0040100f f
?Hello@XXX@@QAEXXZ
Demo.obj
可以看见,int API(int A)函数被编译成了_API,而float API(float A)还是以前的格式。如果想在函数中调用float API(float A)怎么办呢?那就需要把两个API函数设置成不同的函数名。例如
int API(int A)
变成 int iAPI(int A);
float API(float A) 变成
float fAPI(float A);
并且在C++文件的头部添加这两个函数的定义:
extern "C"
{
int iAPI(int A);
float fAPI(int A);
}
再在需要调用的函数中写上要使用的函数的名称。这样就可以运行了。
注意:同名函数实际使用时很容易引起误解,本人认为还是不用为妙。因为从表面上看,它好像是解放了“生产力”,其实这两个参数的名字是含糊不清的。对于调用者来说,经常是搞不清楚到底需要调用哪个函数,有时程序出现错误还不知道错误是在什么地方引起的。本人认为,这种所谓C++的特点对程序员来说,不见得是一件好事。
但有一种情况可以使用,那就是你很清楚将要调用哪个函数。并为某种特殊的目的—为函数或类的内部去使用。如果你把它作为API去使用,则最好必须避免。因为别人不可能特别清楚被重载函数之间的差别。
重载在实际中用处不大,但在教科书中介绍很多。
2. C++接口的方法
我们已经知道在C中调用C++的函数,那么,在C++中怎么调用C的函数呢?
其实,和以上在C中调用C++的函数一样,需要在C++的开始部分说明C函数的调用方式,这样就可以正常地使用了。具体实现可参考例子。
可以在所有的Windows的头文件中,看见如下的代码:
#ifdef __cplusplus
//开始
extern "C" {
#endif
…………
#ifdef __cplusplus
//结束
}
#endif
因为所有定义的API都是标准的API的函数,是通过C的函数来实现的,所以,当在C的环境中编程时,这一对条件宏就不起作用。在C++中,因为定义了__cplusplus,所以C++中就会按C的格式输出函数名,这样,无论是在C中还是在C++中,定义的函数都能被正确地使用。
你是否看过这些头文件?是否想过微软为什么要这么写这些?做程序不能不求甚解,好像只要知道用API,就万事大吉,无所不能。做程序一定要多问多想,求本求源的治学方法才是成为真正高手的必由之路。
微软的代码中有很多精华,只要真正地理解了,对自己的水平提高是很有帮助的。
大家知道,在C中嵌入一段汇编是很简单的,例如,如下的C函数代码,在其中嵌入汇编。
static int X;
void Hello(void)
{
mov [X],ebx
}
}
我们看到,在C中,只要在_asm的括号内输入对应的汇编代码就可以编译运行了。
现在假设要在XXX类中的Hello函数嵌入汇编指令。有如下几种方法,下面分别述之。
class XXX
{
public:
void Hello(void);
void XXX::Hello(void)
{
1.没招, (无法编译通过)
mov [X],ebx
//2. 庸招, (可以运行,大程序会很复杂)
LPINT lpX;
mov edx,lpX;
mov eax,[edx]
mov [edx],ebx
//3.错招,(运行不正常,甚至出错)
mov eax,this.X
mov this.X,ebx
//4. 绝招,(既简单,又正确)
mov eax,[ecx]this.X
mov [ecx]this.X,ebx
}
(1)第一种方法是直接在Hello的函数中嵌入汇编指令,通过再编译,会被告之:
error C2420: 'X' : illegal symbol in second operand
error C2420: 'X' : illegal symbol in first operand
error C2415: improper operand type
Error executing cl.exe.
Api.obj - 3 error(s), 0 warning(s)
这是为什么呢?原来X这个变量是定义在一个类中的私有的变量,所以编译出的程序无法找到对应的变量X,有什么办法解决呢?我们是不是可以把变量定义到类的外部,这样,变量就不随类的变化而变化,所有类都只会使用一个X变量,就可以通过类中引用汇编来获得变量的值了。编译运行通过没问题!
这样做好像是成功了,可X变量没有定义在类中,也就是没有被封装起来,我们把程序的原义给改变了。我们就不能完整使用这个类了。看来这种方法还不行。
(2)第二种方法:是不是可以通过一个指针来取得值呢?
LPINT lpX;
mov edx,lpX;
mov eax,[edx]
mov [edx],ebx
}
在这个段中,定义了一个指针lpX,让它指向X的地址。通过编译运行可以发现,这种方法是可行的。但又有问题,如果嵌入的汇编指令使用了很多C++中的变量,那么每一个变量都要定义一个指针,随后再通过指针来引用类中的变量,这样程序就会很复杂,也很麻烦,不利于程序的维护,看来这是一个“庸招”。
(3)第三种方法:我们都知道,在每个类中都有this这个指针,指向自己,能不能直接在汇编中用this来引用类中的对象呢?在宏汇编中有一种结构的用法,通过定义一个结构,然后能通过“点”的方法来用结构中的变量,那是不是可以在嵌入的汇编中用“点”的方法来操作类中的对象呢?于是,接下来实验如下代码:
mov eax,this.X
mov this.X,ebx
}
编译一下,好像没问题,可运行时不对。是不是this的指向有问题呢?通过分析编译程序的汇编代码发现编译通过。要知道,一个代码是不是完全正确,一定要在汇编指令中看一看。接下来在调试环境中,查看上段嵌入汇编的真实的意义,语句如下:
66:
//X变量的使用
68:
69:
mov eax,this.X
eax,dword ptr [this]
70:
mov this.X,ebx
dword ptr [this],ebx
不对!第二条语句不是改变的类中的变量,而把this的指针改变了,这样做一定会出现问题。当程序运行这个汇编后,类的this指针发生了改变,这个类一定会不正确。有什么方法可以解决以上问题呢?那是不是可以用一个寄存器存放this的指针,而用this.X的方法来表示X在这个类的堆栈中的偏移呢?于是产生了第四种方法。
(4)第四种方法;我们能不能用一个寄存器间接寻址的方法来访问这个类中的变量呢?有了以上思想的指导,可以试一下以下代码。
_asm {
mov eax,[ecx]this.X
mov [ecx]this.X,ebx
}
编译运行通过,好像没什么问题。是不是这样做就行呢?因为一个代码是不是完全正确,一定要在汇编指令中看一看。接下来在调试环境中,查看上段嵌入汇编的真实意义,语句如下:
52:
//X变量的使用
54:
ecx,dword ptr [this]
55:
eax,[ecx]this.X
eax,dword ptr [ecx]
56:
[ecx]this.X,ebx
dword ptr [ecx],ebx
57:
}
这段程序好像是没有什么问题,但只有一个变量,不知道在多个变量中是否能正确地产生偏移位置,所以,可以多加一个或多个变量试一下运行的结果。于是可以增加一个变量:
int X,Y;
然后在汇编代码段中加入对应的对Y变量的使用方法,增加如下的代码段:
//Y变量的使用
mov ecx,
mov eax,[ecx]this.Y
mov [ecx]this.Y,ebx
接下来,我们看一下生成汇编的代码段:
52:
//X变量的使用
54:
ecx,dword ptr [this]
55:
mov eax,[ecx]this.X
eax,dword ptr [ecx]
56:
mov [ecx]this.X,ebx
dword ptr [ecx],ebx
57:
58:
//Y变量的使用
59:
mov ecx,
0040102A
ecx,dword ptr [this]
60:
mov eax,[ecx]this.Y
0040102D
eax,dword ptr [ecx+4]
61:
mov [ecx]this.Y,ebx
dword ptr [ecx+4],ebx
62:
}
可以看见,[this + 0 ]指向X变量地址,而[this+4]指向Y变量地址,这样就没有错误了。Y为[this+4],是因为int类型占用了四个字节长度。这就是“绝招”了。
通过以上的探索和学习,我们已经能在C++的代码中嵌入汇编指令,但也许有人会问,这是什么年代了,现在都用Java、COM等高级抽象的语言和工具了,汇编还有用吗?
很有用,汇编最大的好处就是可以直接地控制CPU的运算,这样就能很方便地进行I/O和高速的运算,最大地发挥CPU的运算能力。例如,汇编在科学运算中的矩阵运算、游戏和多媒体的处理很有用。
最重要的一点是,汇编可以确定程序出错的真正原因。
最近我在DVD 3的声音的解码中,做了音频处理类,用
它来控制音频的均衡,而对这个类的运算是通过重载运算符来实现的。
这样,DVD中各段音频能通过此类的对象进行操作。
我按以上的方法实现后,发现这样做速度极慢。后来,我把类的重载运算用汇编实现之后,速度一下就提高了上百倍。
通过以上分析的过程,给我们两点启示:
(1)做程序一定要用程序的方法去思考问题,假如以主观的方法来分析问题,C的方法和C++的方法就应该一样,可实际运行时,机器是不会按你的想像去运行程序的,它只会根据自己的机理去运行程序。当遇到这种问题时,最好的方法是看一看机器编译出的实际代码是什么,机器实际是怎么执行的,在运行时干了什么。以上通过输出map文件来查看汇编代码就是这种方法。
当看了map文件,就会知道输出的结果,有的有下划线,有的改变了输出名称,变成以问号开始。回过头来分析在C++中嵌入汇编时,如果光凭主观的想像,则怎么也解决不了这个问题,但如果打开代码研究一下,不就可以轻易地解决了吗?
(2)通过对以上代码的分析,可以发现,当遇到问题时,不能只求表面。很多人遇到不能解决的问题时,总是一遍一遍地去试用各种方法,而不是从代码编译结果出发去解决问题,这样就很容易掉进漩涡中出不来。
我当时解决这个问题时,也是通过分析编译的结果代码,来得到以上方法的。分析问题和解决问题要从本质上去研究。
很多人学习编程时,一下就想编写出很高级的程序,一些VB,PB高手好像什么都能做,但因为没有对程序的本质进行深入的学习,所以有时遇见问题会不知所措。所以,如果希望用一两个月就成为高手的人永远成不了高手,一定要有踏实的根底,一步一步地磨练。所以,本书的很多章节,希望广大读者能深入下去学习,只有深入进去,才能真正地学到一些知识。
注册时间 10:45:09
最后登录 10:47:00
用户头衔:注册会员
状态:我不在线
挺不错的!
应该去买一本!!
注册时间 13:16:52
最后登录 13:31:27
用户头衔:注册会员
状态:我不在线
Linking...
Demo.obj : error LNK2001: unresolved external symbol _API
..\BIN/Demo.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.
离开这里吧。。。。请封号。。删贴。。我先删了一部分。。
注册时间 11:13:45
最后登录 22:06:24
用户头衔:注册会员
状态:我不在线
梁眼界太低,说什么"这就是64位和32位的差别。所以,微软的那些“老头”,四、五十岁的那几个做核心的人,现在正在玩这些东西。你说微软的技术它能不先进吗?是Linux那几个玩家能搞定的吗?"
井底之蛙!!!看他的文笔就像李HZ,受不了了
注册时间 12:00:00
最后登录 19:58:37
用户头衔:注册会员
状态:我不在线
刚买了。还没看完,感觉写得挺好的。
努力ING.......独乐乐不如众乐乐...
注册时间 16:56:34
最后登录 02:08:57
用户头衔:注册会员
状态:我不在线
嘿嘿,他的“开放性思维竟然是说“那是windows的问题,不是我的问题”
不得不BS一下。
Processed in : 0.044000 second(s) (C)2015LinuxSir -

我要回帖

更多关于 微信求高手猜成语 的文章

 

随机推荐