首页
社区
课程
招聘
[推荐]ASM精简优化:组合语言之艺术
发表于: 2008-8-3 22:36 5633

[推荐]ASM精简优化:组合语言之艺术

2008-8-3 22:36
5633
不清楚该发在哪个地儿,想想尚属编程的大范畴,有误请版主不吝移动。

这本书确实奇佳,非常推荐




 想要把程序写好,一定要不断地研究、改进,由错误中学习,由改进中得到经验,
培养出敏锐的观察能力和良好的写作习惯。
    在开始时,这种过程需要付出不少时间,但对一位程序员来说,写程序是终身职业,
能不精益求精吗?
    以下举两个实例,以说明如何研究改进已完成的程序。
  1,指令的运用:
 以下面这段通讯处理程序而论,不仅语法及指令完全正确,执行时也毫无错误,是不是
还可以加以改进呢?
    1-1 按照前面规定,说明项中已用简化的字符串:
 SND-传送  RCV-接收  LET-左
        RGT-右    VER-直    HOR-横
    1-2 程序员代号为'C'。
    1-3 段名省略。

    1: CSND0:
    2:  MOV DX,03FDH      ; 输出埠
    3:  MOV AL,80H
    4:  OUT DX,AL       ; 输出指令
    5:  MOV DX,03F8H      ; LSB 速度控制
    6:  MOV AL,06H       ; 速度=19200/秒
    7:  OUT DX,AL
    8:  MOV DX,03F9H      ; MSB 速度控制
    9:  MOV AL,0       ; 速度=19200/秒
   10:  OUT DX,AL
   11:  MOV DX,03FBH      ; 行控制暂存器
   12:  MOV AL,03H       ; NO PARITY,1
                                      ; STOP,8
   13:  OUT DX,AL
   14:  MOV DX,03FCH      ; 通讯控制
   15:  OUT DX,AL
   16:  MOV DX,03F9H      ; 中断有效
   17:  MOV AL,0
   18:  OUT DX,AL
   19: CSND1:
   20:  MOV DX,03FDH      ; 状态暂存器
   21:  IN AL,DX
   22:  TEST AL,10H       ; 是否可接收?
   23:  JNZ CRCV0       ; 可
   24:  TEST AL,20H       ; 信道已清否?
   25:  JZ CSND1       ; 8250未清
   26:  MOV AH,1       ; 键盘有输入?
   27:  INT 16H
   28:  CMP AL,07H       ;  ='CTRL+G'
   29:  JE CEND       ; 是,完毕
   30:  MOV DX,03F8H
   31:  OUT DX,AL       ; 送输入字符
   32:  JMP CSND1
   33: CRCV0:         ; 接收
   34:  MOV DX,03FCH      ; 通讯控制
   35:  MOV AL,08H       ; 暂停中断
   36:  OUT DX,AL
   37:  MOV DX,3F8H
   38:  IN AL,DX       ; 收字符
   39:  MOV AH,0EH
   40:  INT 10H       ; 萤屏显示
   41:  MOV DX,03FCH
   42:  MOV AL,0BH
   43:  OUT DX,AL       ; 继续接受
   44:  JMP CSND1       ; 循环工作
   45: CEND:
   46:  RET        ; 完成

 本段程序共 84 个字符,非常精简,但仍然有节省的余地,要点在DX的数值上。
 DX值由 03F8H到 03FDH,可知 DH 之值不变,只需改变 DL 即可。每改变DX一次,需要
三个字符,如仅变DL,只需两个字符。这一指令共享了十一次,除第一次有必要外,其
它十次就可以省下10个字符。
 再要斤斤计较,还可以榨出二个字符来,在5至8条中,若用INC  DX 只需要一个字符。
 此外,31,32及43 ,44是浪费的作法,只要在第18条加一标号,就可以省却两个字符输
出的指令。另外,还有35及39两条指令,应该合并,一次即将AX设妥,于是,又省下了
一个字符。
        先令 DH=3
    1: CSEND0:
    2:  MOV DL,0FDH       ; 输出埠
    3:  MOV AL,80H
    4:  OUT DX,AL       ; 输出指令
    5:  MOV DL,0F8H       ; LSB 速度控制
    6:  MOV AL,06H       ; 速度=19200/秒
    7:  OUT DX,AL
    8:  INC DX       ; MSB 速度控制
    9:  SUB AL,AL       ; 速度=19200/秒
   10:  OUT DX,AL
   11:  MOV DL,0FBH       ; 行控制暂存器
   12:  MOV AL,DH       ;NO PARITY,1
          ; STOP,8
   13:  OUT DX,AL
   14:  INC DX       ; 通讯控制
   15:  OUT DX,AL
   16:  MOV DL,0F9H       ; 中断有效
   17:  SUB AL,AL
   18: CSNDA:
   19:  OUT DX,AL
   20: CSND1:
   21:  MOV DL,0FDH       ; 状态暂存器
   22:  IN AL,DX
   23:  TEST AL,10H       ; 是否可接收?
   24:  JNZ CRCV0       ; 可
   25:  TEST AL,20H       ; 信道已清否?
   26:  JZ CSND1       ; 8250未清
   27:  MOV AH,1       ; 键盘有输入?
   28:  INT 16H
   29:  CMP AL,07H       ;  ='CTRL+G'
   30:  JE CEND       ; 是,完毕
   31:  MOV DL,0F8H
   32:  JMP CSNDA       ; 送输入字符
   33: CRCV0:         ; 接收
   34:  MOV DL,0FCH       ; 通讯控制
   35:  MOV AX,0E08H      ; 暂停中断
   36:  OUT DX,AL       ; 及显示
   37:  MOV DL,0F8H
   38:  IN AL,DX       ; 收字符
   39:  INT 10H       ; 萤屏显示
   40:  MOV DL,0FCH
   41:  MOV AL,0BH
   42:  JMP CSNDA       ; 循环工作
   43: CEND:
   44:  RET        ; 完成
 看来似乎这样太小气,可是所谓艺术,就要具备丝毫不苟且的态度,再说由84个字符变
成66个字符,省了近百分之廿,而且,速度也快了。这种程序原本就很精简,只有训练
有素,追求完美的程序员,才做得到。
 另一种做法,便是将重复的过程写成回路,约可节省廿几个字符。但是,由于时间定律
限制,通讯程序颇重时效,回路是否值得,尚要多方面分析,不可轻率决定。

  2,回路的实例:
 前面曾经讨论过,程序的效率,经常决定于回路的处理方式及其技巧。其对空间上影响
比较小,但是良好的设计理念,常使速度上有高达十倍,甚至百倍的差异,读者想必已
经知道,但是如何能应用已知的技巧,来改进设计的程序呢?
 回路是利用计数器,反复进行相同的程序作业,这种程序,目的就是为了节省空间,相
对地,时间上难免有所损失。
        因此,在设计回路时,必须先行考虑清楚: 空间的节省与时间的交换是否值得?
 其次,则要充份掌握回路的特色,要用得恰到好处,不可掉以轻心。
        原则上,在回路中,指令要用得精简,流程要非常明确,尤其重要的是,应力
求避免在回路中使用缓冲器,最好充份利用暂存器。如果时间效率极为重要,则不妨放
弃回路方式。
        有一个显示程序,目的是要将 16*16点阵字形送到屏幕上。对象是Hercules 6
40*400的图形卡,计分四区交互传送,这是另一个「高科技」界的新鲜奇事,在IBM PC
推出时,最高密度的图形态,只有 640X200点阵,那是迁就电视屏幕的扫描方式,先送
单线的水平讯号,再送双线,故分两区。Hercules卡为了加高密度,应用Interlace 技
术,又在单双水平扫描线中各加了一行,遂成了四区。
        Hercules很适宜中文的显示,如用 16X16字形,正好显示25行,每行40字,与
英文完全兼容。若希望有一状态显示栏,则可用 15X15字形,留出24条线供做状态栏。
        遗憾的是在最需要中文的国内,却偏爱CGA,EGA 等密度不足的显示设备。不但
售价偏高,功能也不足,弄得不伦不类。
        最理想的还是VGA 显示,计有 640X480之屏幕点阵,不仅空间大,在存贮器中,
只有一区,应用非常灵活。
        下面,我们先介绍 Hercules 的显示方法,同时探讨回路的处理方式。
    1: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    2: ;HERCULES 中文显示处理程序。      ;
    3: ;输入参数:SI=点阵字形,DI=屏幕位置。     ;
    4: ;          DS =CG,ES= 0B800H(屏幕段)。   ;
    5: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    6: CDSP0:
    7:      MOV     CX,16    ;高16点
    8: CDSP1:
    9:      MOVSW     ;移至屏幕上
   10:      ADD     DI,1FFEH    ;加一区,每区=2000H
   11:      JNS     CDSP2    ;未超越区限,继续
   12:      ADD     DI,8050H    ;超越区限,换区加行
   13: CDSP2:
   14:      LOOP    CDSP1
   15:      RET
 程序到此结束,相当精简,技巧在第10至12条区限的检测方式。一般做法是在检查区限
时,用:
      ADD     DI,1FFEH    ; 加区值
      CMP     DI,8000H    ; 最大区限值
      JB      CDSP2    ; 未超过
      SUB     DI,8000H    ; 减去区限
 如此则多了一条4字符的指令,加上4个时钟脉冲,做16次回路就损失64个时钟脉冲值。
在全屏幕显示时,以1,000 个字来算,为数就不少了。
 当然,取消了回路速度还可以加快,其结果,则要增加130 个字符,时间则快了 272个
时钟脉冲,是否值得,就要看实际需要而定了。
 另一个方法,要增加2个字符,但可快上36个时钟脉冲,其法在第11条上:
   11:      JS      CDSP3
   12:CDSP2:
   13:      LOOP    CDSP1
   14:      RET
   15:CDSP3:
   16:      ADD     DI,8050H
   17:      JMP     CDSP2
 再换一个方法,如果先使 BX 为1FFEH,DX为8050H,则在原程序中,将第10条及12条分
别改为:
   10:      ADD     DI,BX
   12:      ADD     DI,DX
 这一来,时钟脉冲快了2个,16次则快更多,如果再加上取消回路,其意义更大。空间
原增加 130字符,现仅94字符,时间则省下 304个时钟脉冲。如果全屏幕显示了1,000
个字,在8MHZ频率下,将会加快 1/25 秒的速度。
 在回路中,如果讲求时间效益,应极力避免使用PUSH及POP ,因PUSH需15个时钟脉冲,
而POP 则要12个,两者相加是27个时钟脉冲,非常不值得。
 解决方法之一是:设法将欲保留之值贮存在没有用到的寄存器中;再若是固定的常数,
也不妨在每次要用时重新置入,只不过是4个时钟而已。最麻烦是变量值,除了在设计
模块之前,妥当地安排外,别无良策。


    我所见过的各种组合程序虽不算多,但至少有百余个了。毛病最多的当然是缺乏完
整的规划,其次则是信马游缰,一份不折不扣的流水帐!明明大门口在东边,程序硬要
朝西,直到游完了大观园,天黑了,才出东门!
    这种程序我收集了一大叠,可是举来做例子,却心有余而力不足。原因无他,实在
不耐烦照抄一遍,一见到就头痛!
    计算机最强的功能,便是处理繁杂重复的工作,为什么一般程序员居然存心与计算
机争风吃醋呢?不说别的,光把程序输入到计算机中,就要花上几个月宝贵的光阴,真
值得这样做吗?
    有一份程序,足足有四十多页,我只略作调整,便缩小到十页,处理速度则快了五
倍。为什么会差这样远呢?很简单,有些人不喜欢用大脑,久而久之,习惯成自然,大
脑就生了铁锈!除了等因奉此,什么都不会想了。
    要想做一个优秀的程序员,第一个条件是不能偷懒,第二个条件则要有分析观察的
习惯,第三个也是最重要的,则是要有追求完美的精神。程序员要像艺术家,不论是自
己的或是别人的程序,都要一而再、再而三地玩味改良。
    我曾见过一个扫地的妇人,她不管在哪里,见不得有任何脏乱。这种人才值得尊敬,
这种精神是伟大的,与她的职业丝毫无关!
    程序写得不够精简,有三个原因,第一个是程序员无能,这种程序能够写完,可以
运行,已算相当难得了;第二个原因是不懂技巧,硬桥硬马的干, 不知什么是效率,也
不知道如何达成。自己写的程序都不见得看得懂,遑论他人的?第三则是根本缺乏敬业
精神,敷衍塞责,这种人我最瞧不起。
    写程序之初,如果把任务了解清楚,然后分析因素,分割模块。所有类似的情况都
合并到一处,再以变量代替,统一执行。这原本是份内的工作,前述的情况根本不可能
发生!
    问题是发生了以后怎么办呢?我建议最好重写,如果一定要改,只好采用程序合并
的技巧,浓缩一下。
    合并的目的是为了增进效率,而合并的方法则因情况不同而异,就像人生了病,必
须先查出病因,否则无法下药。我试着以所知道的一些例证,简要地解说如后。

一、过程的合并:

    要做过程的合并,首先要查明下列各点:
  1,首先找出过程类似的,全部移到一堆,如果找不到,那就没救了。
 然而,这种程序要就是太小,根本不可能有类似的情况,再不就是写作时杂乱无章,信
马游缰。分明有类似的过程,但没有共通的原则,无从浓缩。当然,也可能有些程序,
因工作量及处理的细节太多,以致无法浓缩。

  2,在类似的程序中,找寻相异的指令或流程,再若没有,那就是重复了,正宜合并。

  3,把相异的指令或流程用变量取代,或将不同程序之入口放在寄存器里。

  4,将各程序在应用该流程前,设好变量及使用的暂存器。

  5,合并相似的程序段,不同处应用变量取代之。

    下面举一实例,系一绘图程序之片断,兹改变原用标题,并将分散在各处若干不同
之段,列述如下:
  189: MASK PROC NEAR
  190:  MOV DX,3C4H
  191:  MOV AL,2
  192:  OUT DX,AL
  193:  MOV DX,3C5H
  194:  MOV AL,PCOLOR
  195:  OUT DX,AL
  196:  RET
  197: MASK ENDP
  ...
  380:  MOV DX,03CEH
  381:  MOV AL,3
  382:  OUT DX,AL
  383:  MOV AL,18H
  384:  INC DX
  385:  OUT DX,AL
  386:  RET
  ...
  490:  MOV DX,3CEH
  491:  MOV AL,3
  492:  OUT DX,AL
  493:  MOV DX,3CFH
  494:  MOV AL,0H
  495:  OUT DX,AL
  496:  RET
  ...
  589: CROSS PROC NEAR
  590:  MOV DX,3C4H
  591:  MOV AL,2
  592:  OUT DX,AL
  593:  INC DX
  594:  MOV AL,0FH
  595:  OUT DX,AL
  596:  RET
  597: CROSS ENDP
  ...

    这样的段落有十多处,看来每个都略有不同,似乎不能合并。然而仔细分析,显然
是程序员训练不够,把一个非常有规则的程序,安排得非常紊乱,以致到这个地步。
    我们先归纳问题,决定如何合并。第一,上述各段程序,应该统一作为子程序;第
二,全部变量只有四个,其中两个是传送值,两个是输出入埠。后者有连续关系,等于
只有一个。因此,在调用此子程序前,应先令DX为输出入埠,再将变量装入AX中,一次
调用即可。此子程序如下:
  300: SUB:
  301:  OUT DX,AL
  302:  INC DX
  303:  MOV AL,AH
  304:  OUT DX,AL
  305:  RET
    这样简短的子程序,有无必要,端视时空的效益而定。不论怎样整理,都远比原来
的要好。
    另外有种情况,更为可怕,就是在键盘输入后,用流程方式,一一比较输入码,再
一一分别处理。
    比如说,为了检查游标键的左、右、上、下等八个方向的移动,以便作相应的处理,
程序居然写成:
  100: PP1: MOV AH,0
  101:  INT 16H
  102:  CMP AX,4800H    ;↑键
  103:  JNE NEXT1
  104:  CALL MOVDATA     ;SET BUFFERS
  105:  CALL SETDLT     ;SET INCREMENT
  106: NXT01:
  107:  CALL DOTUP
  108:  LOOP NXT01
  109:  CALL XORDOT     ;SET NEW DOT
  110:  CALL XYDISP     ;DISP NEW XXX,YYY
  :  JMP PP1
  112: NEXT1:
  113:  CMP AX,5000H    ;↓键
  114:  JNE NEXT2
  115:  CALL MOVDATA     ;SET BUFFERS
  116:  CALL SETDLT     ;SET INCREMENT
  117: NXT02:
  118:  CALL DOTDOWN
  119:  LOOP NXT02
  120:  CALL XORDOT     ;SET NEW DOT
  121:  CALL XYDISP     ;DISP NEW XXX,YYY
  122:  JMP PP1
  123: NEXT2:
  124:  CMP AX,4B00H    ;←键
  125:  JNE NEXT3
  ...
    这段程序总共要检查八次,才能确定是否有游标移动以及哪个游标在移动。然后,
还要一一检查其它变化,共有十八种有效码。我实在佩服这种程序员,不但有无比的耐
性,还有非凡的想象力,居然能把一段极为简单平凡的程序,写得这样的精彩动人!
    如果是我,我会写得毫无趣味:
  100: PP1: SUB AH,AH
  101:  INT 16H
  102:  OR AL,AL
  103:  JNZ PP1     ;AL 非0无效
  104:  MOV BH,AL
  105:  MOV BL,AH
  106:  SUB BL,47H     ;最小之字标键
  107:  JLE PP1     ;非处理范围
  108:  SHL BX,1
  109:  CALL FUNC[BX]
  110:  JMP PP1
    这是主流程,程序短,速度快,维护容易,一眼看过去,有什么错误立刻分明。
 ...
 1000: FUNC DW NEXT02     ;↖
 1001:  DW NEXT0     ;↑
 1002:  DW NEXT04     ;↗
 1003:  DW PT     ;无效
 1004:  DW NEXT2     ;←
 1005:  DW PT     ;无效
 1006:  DW NEXT4     ;→
 1007:  DW PT       ;无效
 1008:  DW NEXT12     ;↙
 1009:  DW NEXT1     ;↓
 1010:  DW NEXT14     ;↘
 ...
    因为这是子程序,加一段、减一段容易非常。
    即使是子程序,也有很大的考究,就以前段来说,在 104至110 之间,就值得三思。
  104:  CALL MOVDATA     ;SET BUFFERS
  105:  CALL SETDLT     ;SET INCREMENT
  106: NXT01:
  107:  CALL DOTUP
  108:  LOOP NXT01
  109:  CALL XORDOT     ;SET NEW DOT
  110:  CALL XYDISP     ;DISP NEW XXX,YYY
    首先,104 和105 会重复多次,109 及110 亦然,为什么不合并为一呢?这也是很
常见的程序合并手法,两次调用合为一次,速度及空间都较为经济。
    在子程序 SETDLT 之前,先调用一次 MOVDATA,另XYDISP也是一样,首先备妥:
 3000: SETDATA:
 3001:  CALL MOVDATA     ;假设本程序有他用
 3002: SETDLT:
 3003:       ...
 ...
 3100: XYDIDOT:
 3101:  CALL XORDOT     ; 同上
 3102: XYDISP:
 3103:  ...
 ...
    再来设计NEXT0 的子程序:
  110: NEXT0:
  :  CALL SETDATA
  112: NXT01:
  113:  DOTUP  应搬至此,无需设为子程序。
  ...
  120:  LOOP NXT01
  121:  JMP XYDIDOT     ; 如有必要,可先
                                    ; 设好参数
    这样合并一下,效果决不止高上十倍,等到真正学会了程序的技巧,写作时速度也
可以提高数倍。

二、分支的处理:

    分支是程序中不可避免的手段,使用得好,整个程序气势一贯,有行云流水之妙。
    前面的例子根本不具分支的条件,故不能算是分支不良,而是程序员观念错误。
    下面再举一例,由于分支不良,以致程序支离破碎。这是一则计算拋物线的快速程
序,妙在没有用乘除法,也没有任何函数。其中有几段是这样的:
  100: BEG00:
  101:         CMP BP,BUFY
  102:? JLE BE7
  103:     OR CX,CX
  104:       JG BE20
  105:         MOV AX,BP
  106:? SHL AX,1
  107:     DEC AX
  108:     JL BE10
  109: BE2:
  110:       CALL BE1
  :         JC BEG00
  112:    CALL BE3
  113:      JMP BEG00
  ...
  120: BE14:
  121:? LODSW
  122:     CMP AH,1FH
  123:       JGE BE141
  124:         LOOP BE14
  125:   POP DI
  126:    POP CX
  127:      MOV SI,DI
  128:        JMP BE142
  129: BE141:
  130:         POP DI
  131:    POP CX
  132:    MOV SI,DI
  ...
  150: BE10:
  151:    CALL BE1
  152:      JMP BEG00
  153: BE20:
  154:        MOV AX,CX
  155:? SUB AX,DX
  156:     SHL AX,1
  157:       DEC AX
  158:         JLE BE2
  159:? CALL BE3
  160:     JMP BEG00
  161: BE1:
  162:? INC DX
  163:     ADD CX,DX
  164:       ADD CX,DX
  165:         INC CX
  166:      ADD DI,BUFX
  167:     CMP DI,BX
  168:       JLE BE1RET
  169:         CALL BE01
  170:         SUB DI,BX
  171: BE1RET:
  172:     RET
  ...
  190: BE01:
  191:? MOV AL,1
  192:     CMP [SI+1],AL
  193:       JNZ BE011
  194:         INC BYTE PTR [SI+1]
  195:        RET
  ...
  200: BE141:
  ...
    全部程序并不大,不过一百多条指令,但是稍加改进,却可以省却廿多条指令,速
度也会加快。重点在于106 到113 的分支错误,以致于多出BE10 BE20 BE3 BE01等段程
序出来。
    照理,BE1 BE3 BE01都不该另设子程序,BE14也应改写,如此,整个程序就完全不
同了。
    原来由 105为:
  105:         MOV AX,BP  ;★无必要
  106:        SHL AX,1  ;★无必要
  107:         DEC AX  ;★无必要
  108:         JL BE10
  109: BE2:
  110:       CALL BE1  ;★合并后,无需调用
  :         JC BEG00  ;★另作分支
  112:     CALL BE3  ;★也无必要调用
  113:       JMP BEG00
    现改为:
  107: BE1:        ;原为DEC AX分支处理
  108:        INC DX  ;原161子程序作主流程
  109:         ADD CX,DX
  110:    ADD CX,DX
  :      INC CX
  112:        ADD DI,BUFX
  113:    CMP DI,BX
  114:      JLE BE11
  115: ;       CALL BE01  ;本子程序重写如下:
  116:         CMP BYTE PTR[SI+1],1
  117:       JNE BE1A
  118:         INC BYTE PTR[SI+1]
  119: BE1A:
  120:         SUB DI,BX
  121:         JC BEG00  ;原
  122:         ...    ;原BE3 程序
  ...
    又 125条三个指令也是分支错误,白白浪费。
  120: BE14:
  121:     LODSW
  122:       CMP AH,1FH
  123:         JGE BE141
  124:    LOOP BE14
  125:      POP DI  ;★可以省略
  126:        POP CX  ;★可以省略
  127:    MOV SI,DI  ;★可以省略
  128:      JMP BE142  ;★可以省略
  129: BE141:
  130:        POP DI
  131:         POP CX
  132:    MOV SI,DI    ;127移到此
  133:      JNZ BE142  ;128移到此
  134:        ...

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (5)
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
2
过时导致错误很多
2008-8-4 03:24
0
雪    币: 424
活跃值: (10)
能力值: ( LV9,RANK:850 )
在线值:
发帖
回帖
粉丝
3
压榨花生油过多导致自己眼睛迷糊头脑混乱
2008-8-4 04:54
0
雪    币: 242
活跃值: (443)
能力值: ( LV11,RANK:188 )
在线值:
发帖
回帖
粉丝
4
难道现在的这些设计就是正确的?
未必,现代的概念和此书中冲突的90%,平心而论,确实是概念错了
过时,确实有一些地方过时了,但它蕴含的各种思想没有过时,而是发扬光大了
再者,此书里面那种程序编辑者的精神非常推崇,自认只有报以佩服的资格

回LS,正如书中所述,计算机硬件进步是提供了更好的平台以提供更好的服务,若为了便捷与一己之利随意code,熟直言,如是则不可称呼你为程序编辑者!
2008-8-4 10:29
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
5
没有对LZ不敬的意思,书在当时是好书,现在也能给人启发,过时是就事论事,LZ也不必激动。
2008-8-4 15:21
0
雪    币: 424
活跃值: (10)
能力值: ( LV9,RANK:850 )
在线值:
发帖
回帖
粉丝
6
此文的作者楼主的精神非常推崇,自认只有报以佩服的资格了,我自然就不是程序员
2008-8-4 20:51
0
游客
登录 | 注册 方可回帖
返回
//