2005年05月30日

winrar注册码(通用注册码,使用于各版本)

将以下内容复制到记事本,另存为Rarreg.key.然后再把记事本复制到RAR安装目录即可

RAR registration data
UKRSPETSMONTAZHPROEKT
License #18 of 18
UID=2e87fd8514caa742d5c9
6412212250d5c9d1ed2b0f2e29c165d3456a745a29ae99784f4b23
e8db3aa9c3a26c2e91e96045952b0cc52cc0afdef7f4a5fc540567
97b794e1b6a40c992c109fd76270a0cc4003d5531ee2fc1b8f2324
24404ae753eb0b1ec5b22cba57170fac20602996b86d34cd7ab5f2
efa581f3cacb5e27bc37fe2b4d2a3c523815fffd7a38492788aa80
1adaf53060fb71d2a07e74e2c2cba90cd585de053efee4d660a38c
289ff66d22fd7aab3cc959784734eef2ad3839d072d50063732734

RAR registration data
YAG
Single PC usage license
UID=1b27346d262e55129dd5
64122122509dd5a18126fa7427a9932c5d348094bd7005b7d9609a
ad0eab17da92f80f594c6035c6ab9048e2c5c62f0238f183d28519
aa87488bf38f5b634cf28190bdf438ac593b1857cdb55a7fcb0eb0
c3e4c2736090b3dfa45384e08e9de05c58609437f4fd152426a2d3
82c0b0a3e934cc1ca20bccd616b4943fb93a2088436fc488faba73
b6bb39c1adfb3788c293c4631f2404b92e66ff102672a12c60e170
b1a6611869da952f35593110eeab0f71c1a07ad857eb3896596091

RAR registration data
Deep Blue
Single PC usage license
UID=606fa0b82435af0071fa
641221225071faa9a9a364a166410a3e5dcff675ed8651e357c257
07202cfb465de70442dc6035c6ab9048e2c5c62f0238f183d28519
aa87488bf38f5b634cf28190bdf438ac593b1857cdb55a7fcb0eb0
c3e4c2736090b3dfa45384e08e9de05c58601d50b949b1fd73915e
4e12d041392c5022a8ee270d44cb293b673ad7d0570fc800b4eee1
e905df1ba4befb05b4c747fef80d439a0d301022fb6edca5607fdc
5cd370ec96b2610c8bf9df6c626694640006eb9875e81145888340

2005年05月28日

               凡事都是虚空
   
    Everything is meaningless.竟是圣经里的话语。 那是我看到的最为震惊的一
句话, 也是我后来觉得最深刻的一句话。中国人很难理解,对在功利教育里熏陶过来,
缺少人格教育的中国学生,更无异于晴天霹雳。 成绩,offer, 学位,这样那样的好处
,每天拼命算计的东西有什么意义?假设你突然死掉,世界将会怎样?世界将一样绚丽
,地球转的一样快,太阳系每天在宇宙中换一个位置。大海还是大海,波涛还是波涛,
一样的花开花落,潮起潮落。你的亲人可能会掉眼泪,但是周围的人在三个月内将你忘
个干净,那是你曾经那么在乎他们怎么看你的一群人啊。如果上帝存在,在他的眼里,
你是多么可怜的小虫子,在活着的短暂岁月里,在最美好的青春里,都不曾快乐过,用
尽心力去聚集一大堆外在和心灵没有关系的小东西,只是出于对未来的没有信心,小小
的心灵在接近熄灭的一天还在发出那个愚蠢的声音,让你忙碌,让你忧虑的声音:我要
,我还要。天底下充满了这样的小虫子,当一个离开了,又有一个来了,做着同样的事
情,汹涌着同样的小小念头,受着同样的煎熬。 于是上帝要感慨了:虚空的虚空,凡事
都是虚空。已有的事,后必再有;已行的事,后必再行。日光之下,并无新事。

    已过的时世代,无人纪念;将来的世代,后来的人也不纪念。

  不要忧虑“不要为明天忧虑,天上的飞鸟,不耕种也不收获,上天尚且要养活它,
田野里的百合花,从不忧虑它能不能开花,是不是可以开得和其它一样美,但是它就自
然的开花了,开得比所罗门皇冠上的珍珠还美。你呢,忧虑什么呢? 人比飞鸟和百合花
贵重多了,上帝会弃你不顾吗?”
 
    为什么活得这么累? 生命本是如此美丽,连飞鸟和野花都可以尽情地享受上天
的恩赐,而这些有高等思维的聪明人,却活活让思维搞得神情郁郁,哀声叹气。

    常有人感叹西方人笑起来那么真实,那么出自内心,探讨起来,又归结到他们
更有钱,他们的社会更发达。可我觉得那不是原因。原因就是他们比中国学生更接近飞
鸟和野花罢了,更接近《阿甘正传》里的弱智罢了。他们更天真,相信那个万能的上帝
会永不遗弃他,所以他们可以少想很多的问题,反而过得更顺利,在团队里表现得更凝
聚,因为过分的私心是无法向大家共同的上帝交代的,他们可以很快做出一个Microsoft
,一 个Dell,但是大家可以看看中国的北大方正,联想,新浪, 管理层一年的地震比
台湾还多, 这么多年来,连冲出亚洲的野心都没有真正实现过。 这难道不是上帝给西方
人带来的好处,耶稣说信我就可以得救,不管这个上帝是不是虚拟的,但他在事实上填
补了人性的巨大空白,人家的Microsoft 就证明了他的存在,正如计算机的虚拟内存,
尽管虚拟,但事实上的作用是巨大的。中国学生总是怀疑这个看不见的上帝是否存在,
更在私下里说,他对我能带来好处吗?其实中国人什么都不信,只信好处,从古时的考
八股起,读书就是为了好处。因此,大家每天活在害怕没有好处的忧郁里,想靠自己小
小的思维,在着巨大的世界系统里去谋取好处,上帝忍了泪水,背过脸去。思维的无奈
我并不主张虚无,尽管我在上一篇文章里尽力去指出物质追求的虚妄。

  正如萨特认为,人生本是本无意义,但是怎样摆脱虚无却是有意义的。王朔的意义
在于砸碎那些没有意义的假崇高,伍迪.艾伦的意义就在于不断指出人生的荒谬。如果一
切都是那么可笑,我们怎样面对每天的24小时? 但是活着就是这么简单,它只是一个过
程,简单而自然地发生,以至于任何干扰和关注都是多余。就像飞鸟掠过天空,野花静
静地开放。能把什么东西叫做现在吗?你能占有什么东西吗?一切的意义只在时间的流
动的河中。就像一团火,哪个燃烧的过程才叫火,一旦过程停止了,火不存在了。人的
思维在作怪,它是一个双面的东西,它不总是带给我们好处,虽然我们对它有那么多自信
。 思维在很多时候严重地干扰了那个自然的生命过程,它在想单个的状态好不好,值不
值得,合不合规范, 能给自己带来什么,所以我们很快变得不快乐,不安稳,再也无法
享受那种自然的喜悦了,正像被摄像的人,他的表情立刻不自然起来。恐怖片里的鬼魂
可能一直不曾出现,人们却开始牙齿打颤,是被自己思维折磨而已。学计算机的朋友肯
定知道操作系统将一个进程悬挂起来的意思。人的那个蠢笨不堪的思维,凭什么要常驻
内存?它那么长期的运转,又真正解决了多少问题?为什么不在必要的时候悬挂它,去
享受生命的自然? 明白这一点将改变你的生活,思维会使你陷入矛盾,很多时候它是多
余的,用心去体会,甚至用毛孔去感受就足够了。当你不再判断,不再分辨,不再比较
,不再权衡,你就立刻、和谐起来。“采菊东篱下,悠然见南山”。那时,还用考虑什
么呢?“此间有真意,欲辩已忘言”,连言语都是多余,因为言 语来自思维。佛陀的捻
花一笑,详和的神情,虽静坐,似乎已飞跃世间一切,他坐在了那个生命的根本之上,
再也没有一丝的不和谐。

    读书的时候,我常常到海边听滔声,坐下来看着太阳落下,那会是我一天最美
好的时间,当太阳没下去,晚霞渐渐褪去颜色,波浪依然轻轻拍打岸边,幕色从四周将
我围过来,静默中我会在心灵里升起喜悦,感觉到冥冥中那个永恒的力量,它在紧紧将
我抱住,天地万物和我一样同在,也被温和地抱着,我将永不孤独,永不伤心,永不绝
望,因为那力量就一直在那里,将永远在那里,我是它的恩赐,我的灵魂从未像那时一
样枝繁叶茂,内心从未像那时一样宁静和谐。我不用去分辨那种力量,是上帝也好,上
天也好,老子说的道也好,有什么关系呢?分辨只是是思维常干的蠢事罢了。所有的心
灵都是一样的,所以我相信所有人都有那个和谐的状态,就像收音机有那个频道一样,
只不过太多人没有调到过。太阳,大海,清风明月,鸟语花香,生生不息的物种,是多
么大的恩赐啊,只在我们断暂的生命里才可以感受到,可是太多的人从不念及。他们将
自己全部地交给了少得可怜的脑细胞,心灵交给了那个拙劣的 CPU, 时时刻刻在做狭窄
不堪的运算和判断,所以才会长时间挣扎焦虑,只看到85分和90分的区别, 5000元月薪
和10万年薪的不同,牛校和烂校的分辨。所以“郁闷”,“无耻”,“倒霉”,“不爽
”,“急”,这样的词汇就开始在嘴边泛滥了,就像破电脑的出错提示一样多。

    耶稣呢,用我们的眼光看,他太失败了,没有妻子,没有儿子,没有房子,没
有财产,没有地位,最后还要被钉死,他只是游走于四方去救助受苦受难的人们,他有
余的眼光总是看到了世界的外面,因为他也到过更高的维度里。

  庄子至今看来还是活得最浪漫最洒脱的中国人,他是超脱的同义词,他也是高维空
间的蚂蚁。

  去读他们,去体会那种来自另外一个维度的智慧的震撼,尽管你可能无法改变无奈
的现实,但是可以深刻地改变自己,尽管无法摆脱沉重的肉身,依旧无选择地活在平面
上,但是,心灵获得了自由。愿意升起你的心灵吗?

    每个人都是独一无二的,而且我们永远只能是自己,卢梭说的,对于整个世界
我微不足道,但是我对于自己确是全部。事实上我们只对于自己重要,如果我死掉了,
没有几个人会在三年后保持对我的记忆,如果我痛苦,没有几个人会有真正的同情,因
为太难了,每个人都无法了解我的意识。 所以我们要独立,活着就是成为自己,那个独
一无二的自己,去寻找自内在的完美与和谐,去实现句那没有选择的话: I am who I
am。Simply because I am not and can not be anyone else.可是我们受教育,教育的
目的就是教我们忘掉自己,去变成一个称为标准的人,不是这样吗?从小学起我们就要
评三好,树标兵,学雷锋,学赖宁。老师总是看到我们的恶习,“你那样子不合行为规
范,不可耻吗?”

    到了大学,我们又自由了多少呢?我们依旧看别人,看典型,看所谓成功者,
我们依旧活在要忘掉自己的标准包围中,去bbs看看,似乎所有人都统一了口吻,GRE
2400,拿了牛校offer, 签了著名外企,找到了ppmm, 牛啊,羡慕啊,爽啊, 历史走到了2
1世纪,北大和清华人只剩下一副面孔了,每年招了很多新生,最后就剩下了一个。

  比较是有意义的吗?作为一个独一无二的存在,作为自己的全部主宰,为什么要什
么都和人家比才可以找到意义?为什么当别人考G的时候,我也一定要考,为什么考不过
2200就要郁?为什么billgates 成功的时候,我也一定要学计算机?可是自己和别人是
多么的不同,些不同难道可以在一些欲念的驱动下轻易的忽略?

  崇拜是有意义的吗?明星是需要那样追捧的吗?中国的那支烂球队是需要那么多关
注的吗?

  当我们倾注希望的时候,他们借此赚到了更多的银子,活得更加嚣张,更加让我们
失望,我们是在给富翁们献爱心,爱心那么多,为什么不献给需要爱心的更多的人们,
为什么不献给自己,独一无二的自己?

SQL语句介绍

说明:复制表(只复制结构,源表名:a 新表名:b)

SQL: select * into b from a where 1<>1
说明:拷贝表(拷贝数据,源表名:a 目标表名:b)

SQL: insert into b(a, b, c) select d,e,f from b;
说明:显示文章、提交人和最后回复时间

SQL: select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b
说明:外连接查询(表名1:a 表名2:b)

SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
说明:日程安排提前五分钟提醒

SQL:  select * from 日程安排 where datediff(‘minute’,f开始时间,getdate())>5

说明:两张关联表,删除主表中已经在副表中没有的信息
SQL: 

delete from info where not exists ( select * from infobz where info.infid=infobz.infid 
说明:–
SQL: 


SELECT A.NUM, A.NAME, B.UPD_DATE, B.PREV_UPD_DATE
  FROM TABLE1,
    (SELECT X.NUM, X.UPD_DATE, Y.UPD_DATE PREV_UPD_DATE
        FROM (SELECT NUM, UPD_DATE, INBOUND_QTY, STOCK_ONHAND
                FROM TABLE2
              WHERE TO_CHAR(UPD_DATE,’YYYY/MM’) = TO_CHAR(SYSDATE, ‘YYYY/MM’)) X,
            (SELECT NUM, UPD_DATE, STOCK_ONHAND
                FROM TABLE2
              WHERE TO_CHAR(UPD_DATE,’YYYY/MM’) =
                    TO_CHAR(TO_DATE(TO_CHAR(SYSDATE, ‘YYYY/MM’) || ‘/01′,’YYYY/MM/DD’) – 1, ‘YYYY/MM’)  Y,
        WHERE X.NUM = Y.NUM (+)
          AND X.INBOUND_QTY + NVL(Y.STOCK_ONHAND,0) <> X.STOCK_ONHAND  B
WHERE A.NUM = B.NUM
说明:–
SQL: 


 


select * from studentinfo where not exists(select * from student where studentinfo.id=student.id) and 系名称=’"&strdepartmentname&"’ and 专业名称=’"&strprofessionname&"’ order by 性别,生源地,高考总成绩
说明:
从数据库中去一年的各单位电话费统计(电话费定额贺电化肥清单两个表来源)
SQL:


SELECT a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, ‘yyyy’) AS telyear,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘01′, a.factration)) AS JAN,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘02′, a.factration)) AS FRI,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘03′, a.factration)) AS MAR,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘04′, a.factration)) AS APR,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘05′, a.factration)) AS MAY,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘06′, a.factration)) AS JUE,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘07′, a.factration)) AS JUL,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘08′, a.factration)) AS AGU,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘09′, a.factration)) AS SEP,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘10′, a.factration)) AS OCT,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘11′, a.factration)) AS NOV,
      SUM(decode(TO_CHAR(a.telfeedate, ‘mm’), ‘12′, a.factration)) AS DEC
FROM (SELECT a.userper, a.tel, a.standfee, b.telfeedate, b.factration
        FROM TELFEESTAND a, TELFEE b
        WHERE a.tel

 一般文件操作 API

CreateFile
打开文件
要对文件进行读写等操作,首先必须获得文件句柄,通过该函数可以获得文件句柄,该函数是通向文件世界的大门。

ReadFile
从文件中读取字节信息。
在打开文件获得了文件句柄之后,则可以通过该函数读取数据。

WriteFile
向文件写入字节信息。
同样可以将文件句柄传给该函数,从而实现对文件数据的写入。

CloseHandle
关闭文件句柄。
打开门之后,自然要记得关上。

GetFileTime
获取文件时间。
有三个文件时间可供获取:创建时间、最后访问时间、最后写时间。
该函数同样需要文件句柄作为入口参数。

GetFileSize
获取文件大小。
由于文件大小可以高达上数G(1G需要30位),因此一个32位的双字节类型无法对其精确表达,因此返回码表示低32位,还有一个出口参数可以传出高32位。
该函数同样需要文件句柄作为入口参数。

GetFileAttributes
获取文件属性。
可以获取文件的存档、只读、系统、隐藏等属性。
该函数只需一个文件路径作为参数。

SetFileAttributes
设置文件属性。
能获取,自然也应该能设置。
可以设置文件的存档、只读、系统、隐藏等属性。
该函数只需一个文件路径作为参数。

GetFileInformationByHandle
获取所有文件信息
该函数能够获取上面所有函数所能够获取的信息,如大小、属性等,同时还包括一些其他地方无法获取的信息,比如:文件卷标、索引和链接信息。
该函数需要文件句柄作为入口参数。

GetFullPathName
获取文件路径,该函数获取文件的完整路径名。
需要提醒的是:只有当该文件在当前目录下,结果才正确。如果要得到真正的路径。应该用GetModuleFileName函数。

CopyFile
复制文件
注意:只能复制文件,而不能复制目录

MoveFileEx
移动文件
既可以移动文件,也可以移动目录,但不能跨越盘符。(Window2000下设置移动标志可以实现跨越盘符操作)

DeleteFile
删除文件

GetTempPath
获取Windows临时目录路径

GetTempFileName
在Windows临时目录路径下创建一个唯一的临时文件

SetFilePoint
移动文件指针。
该函数用于对文件进行高级读写操作时。


 文件的锁定和解锁

LockFile
UnlockFile
LockFileEx
UnlockFileEx

以上四个函数用于对文件进行锁定和解锁。这样可以实现文件的异步操作。可同时对文件的不同部分进行各自的操作。

 文件的压缩和解压缩

LZOpenFile
打开压缩文件以读取

LZSeek
查找压缩文件中的一个位置

LZRead
读一个压缩文件

LZClose
关闭一个压缩文件

LZCopy
复制压缩文件并在处理过程中展开

GetExpandedName
从压缩文件中返回文件名称。

以上六个函数为32位 API 中的一个小扩展库,文件压缩扩展库中的函数。文件压缩可以用命令 compress 创建。


 文件内核对象

    32位 API 提供一个称为文件映像的特性,它允许将文件直接映射为一个应用的虚拟内存空间,这一技术可用于简化和加速文件访问。

CreateFileMapping
创建和命名映射

MapViewOfFile
把文件映射装载如内存

UnmapViewOfFile
释放视图并把变化写回文件

FlushViewOfFile
将视图的变化刷新写入磁盘

希望通过以上几个常用的 API 函数,能快速的提高文件操作过程函数的编写。

文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类,常用的有Win32 API的CreateFile()、WriteFile()、ReadFile()和MFC提供的CFile类等。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的,本文下面将针对这种Windows核心编程技术展开讨论。

  内存映射文件

  内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。

  内存映射文件并不是简单的文件I/O操作,实际用到了Windows的核心编程技术–内存管理。所以,如果想对内存映射文件有更深刻的认识,必须对Windows操作系统的内存管理机制有清楚的认识,内存管理的相关知识非常复杂,超出了本文的讨论范畴,在此就不再赘述,感兴趣的读者可以参阅其他相关书籍。下面给出使用内存映射文件的一般方法:

  首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、通过CloseHandle()关闭前面创建的文件映射对象和文件对象。

  内存映射文件相关函数

  在使用内存映射文件时,所使用的API函数主要就是前面提到过的那几个函数,下面分别对其进行介绍:

HANDLE CreateFile(LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile); 

  函数CreateFile()即使是在普通的文件操作时也经常用来创建、打开文件,在处理内存映射文件时,该函数来创建/打开一个文件内核对象,并将其句柄返回,在调用该函数时需要根据是否需要数据读写和文件的共享方式来设置参数dwDesiredAccess和dwShareMode,错误的参数设置将会导致相应操作时的失败。

HANDLE CreateFileMapping(HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName); 

  CreateFileMapping()函数创建一个文件映射内核对象,通过参数hFile指定待映射到进程地址空间的文件句柄(该句柄由CreateFile()函数的返回值获取)。由于内存映射文件的物理存储器实际是存储于磁盘上的一个文件,而不是从系统的页文件中分配的内存,所以系统不会主动为其保留地址空间区域,也不会自动将文件的存储空间映射到该区域,为了让系统能够确定对页面采取何种保护属性,需要通过参数flProtect来设定,保护属性PAGE_READONLY、PAGE_READWRITE和PAGE_WRITECOPY分别表示文件映射对象被映射后,可以读取、读写文件数据。在使用PAGE_READONLY时,必须确保CreateFile()采用的是GENERIC_READ参数;PAGE_READWRITE则要求CreateFile()采用的是GENERIC_READ|GENERIC_WRITE参数;至于属性PAGE_WRITECOPY则只需要确保CreateFile()采用了GENERIC_READ和GENERIC_WRITE其中之一即可。DWORD型的参数dwMaximumSizeHigh和dwMaximumSizeLow也是相当重要的,指定了文件的最大字节数,由于这两个参数共64位,因此所支持的最大文件长度为16EB,几乎可以满足任何大数据量文件处理场合的要求。

LPVOID MapViewOfFile(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap);

  MapViewOfFile()函数负责把文件数据映射到进程的地址空间,参数hFileMappingObject为CreateFileMapping()返回的文件映像对象句柄。参数dwDesiredAccess则再次指定了对文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。虽然这里一再对保护属性进行重复设置看似多余,但却可以使应用程序能更多的对数据的保护属性实行有效控制。MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中,文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB。当然,也可以通过如下代码来动态获取当前操作系统的分配粒度:

SYSTEM_INFO sinf;
GetSystemInfo(&sinf);
DWORD dwAllocationGranularity = sinf.dwAllocationGranularity;

  参数dwNumberOfBytesToMap指定了数据文件的映射长度,这里需要特别指出的是,对于Windows 9x操作系统,如果MapViewOfFile()无法找到足够大的区域来存放整个文件映射对象,将返回空值(NULL);但是在Windows 2000下,MapViewOfFile()只需要为必要的视图找到足够大的一个区域即可,而无须考虑整个文件映射对象的大小。

  在完成对映射到进程地址空间区域的文件处理后,需要通过函数UnmapViewOfFile()完成对文件数据映像的释放,该函数原型声明如下:

BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);

  唯一的参数lpBaseAddress指定了返回区域的基地址,必须将其设定为MapViewOfFile()的返回值。在使用了函数MapViewOfFile()之后,必须要有对应的UnmapViewOfFile()调用,否则在进程终止之前,保留的区域将无法释放。除此之外,前面还曾由CreateFile()和CreateFileMapping()函数创建过文件内核对象和文件映射内核对象,在进程终止之前有必要通过CloseHandle()将其释放,否则将会出现资源泄漏的问题。

  除了前面这些必须的API函数之外,在使用内存映射文件时还要根据情况来选用其他一些辅助函数。例如,在使用内存映射文件时,为了提高速度,系统将文件的数据页面进行高速缓存,而且在处理文件映射视图时不立即更新文件的磁盘映像。为解决这个问题可以考虑使用FlushViewOfFile()函数,该函数强制系统将修改过的数据部分或全部重新写入磁盘映像,从而可以确保所有的数据更新能及时保存到磁盘。
使用内存映射文件处理大文件应用示例

  下面结合一个具体的实例来进一步讲述内存映射文件的使用方法。该实例从端口接收数据,并实时将其存放于磁盘,由于数据量大(几十GB),在此选用内存映射文件进行处理。下面给出的是位于工作线程MainProc中的部分主要代码,该线程自程序运行时启动,当端口有数据到达时将会发出事件hEvent[0],WaitForMultipleObjects()函数等待到该事件发生后将接收到的数据保存到磁盘,如果终止接收将发出事件hEvent[1],事件处理过程将负责完成资源的释放和文件的关闭等工作。下面给出此线程处理函数的具体实现过程:

……
// 创建文件内核对象,其句柄保存于hFile
HANDLE hFile = CreateFile("Recv1.zip",
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);

// 创建文件映射内核对象,句柄保存于hFileMapping
HANDLE hFileMapping = CreateFileMapping(hFile,NULL,PAGE_READWRITE,
0, 0×4000000, NULL);
// 释放文件内核对象
CloseHandle(hFile);

// 设定大小、偏移量等参数
__int64 qwFileSize = 0×4000000;
__int64 qwFileOffset = 0;
__int64 T = 600 * sinf.dwAllocationGranularity;
DWORD dwBytesInBlock = 1000 * sinf.dwAllocationGranularity;

// 将文件数据映射到进程的地址空间
PBYTE pbFile = (PBYTE)MapViewOfFile(hFileMapping,
FILE_MAP_ALL_ACCESS,
(DWORD)(qwFileOffset>>32), (DWORD)(qwFileOffset&0xFFFFFFFF), dwBytesInBlock);
while(bLoop)
{
// 捕获事件hEvent[0]和事件hEvent[1]
DWORD ret = WaitForMultipleObjects(2, hEvent, FALSE, INFINITE);
ret -= WAIT_OBJECT_0;
switch (ret)
{
// 接收数据事件触发
case 0:
// 从端口接收数据并保存到内存映射文件
nReadLen=syio_Read(port[1], pbFile + qwFileOffset, QueueLen);
qwFileOffset += nReadLen;

// 当数据写满60%时,为防数据溢出,需要在其后开辟一新的映射视图
if (qwFileOffset > T)
{
T = qwFileOffset + 600 * sinf.dwAllocationGranularity;
UnmapViewOfFile(pbFile);
pbFile = (PBYTE)MapViewOfFile(hFileMapping,
FILE_MAP_ALL_ACCESS,
(DWORD)(qwFileOffset>>32), (DWORD)(qwFileOffset&0xFFFFFFFF), dwBytesInBlock);
}
break;

// 终止事件触发
case 1:
bLoop = FALSE;

// 从进程的地址空间撤消文件数据映像
UnmapViewOfFile(pbFile);

// 关闭文件映射对象
CloseHandle(hFileMapping);
break;
}
}

  在终止事件触发处理过程中如果只简单的执行UnmapViewOfFile()和CloseHandle()函数将无法正确标识文件的实际大小,即如果开辟的内存映射文件为30GB,而接收的数据只有14GB,那么上述程序执行完后,保存的文件长度仍是30GB。也就是说,在处理完成后还要再次通过内存映射文件的形式将文件恢复到实际大小,下面是实现此要求的主要代码:

// 创建另外一个文件内核对象
hFile2 = CreateFile("Recv.zip",
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);

// 以实际数据长度创建另外一个文件映射内核对象
hFileMapping2 = CreateFileMapping(hFile2,
NULL,
PAGE_READWRITE,
0,
(DWORD)(qwFileOffset&0xFFFFFFFF),
NULL);

// 关闭文件内核对象
CloseHandle(hFile2);

// 将文件数据映射到进程的地址空间
pbFile2 = (PBYTE)MapViewOfFile(hFileMapping2,
FILE_MAP_ALL_ACCESS,
0, 0, qwFileOffset);

// 将数据从原来的内存映射文件复制到此内存映射文件
memcpy(pbFile2, pbFile, qwFileOffset);

//从进程的地址空间撤消文件数据映像
UnmapViewOfFile(pbFile);
UnmapViewOfFile(pbFile2);

// 关闭文件映射对象
CloseHandle(hFileMapping);
CloseHandle(hFileMapping2);

// 删除临时文件
DeleteFile("Recv1.zip");

  结论

  经实际测试,内存映射文件在处理大数据量文件时表现出了良好的性能,比通常使用CFile类和ReadFile()和WriteFile()等函数的文件处理方式具有明显的优势。本文所述代码在Windows 98下由Microsoft Visual C++ 6.0编译通过。

第十三课 内存映射文件


——————————————————————————–

本课中我们将要讲解内存映射文件并且演示如何运用它。您将会发现使用内存映射文件是非常简单的。

理论:
如果您仔细地研究了前一课的例子, 就会发现它有一个严重的缺陷:如果您想读的内容大于系统分配的内存块怎么办?如果您想搜索的字符串刚好超过内存块的边界又该如何处理?对于第一个问题,您也许会说,只要不断地读就不解决了吗。至于第二个问题,您又会说在内存块的边界处做一些特别的处理,譬如放上一些标志位就可以了。原理上确实是行得通,但是这随问题复杂程度加深而显得非常难以处理。其中的第二个问题是有名的边界判断问题,程序中许许多多的错误都是由此引起。想一想,如果我们能够分配一个能够容纳整个文件的大内存块该多好啊,这样这两个问题不都迎刃而解了吗?是的,WIN32的内存映射文件确实允许我们分配一个装得下现实中可能存在的足够大的文件的内存。

利用内存映射文件您可以认为操作系统已经为您把文件全部装入了内存,然后您只要移动文件指针进行读写即可了。这样您甚至不需要调用那些分配、释放内存块和文件输入/输出的API函数,另外您可以把这用作不同的进程之间共享数据的一种办法。运用内存映射文件实际上没有涉及实际的文件操作,它更象为每个进程保留一个看得见的内存空间。至于把内存映射文件当成进程间共享数据的办法来用,则要加倍小心,因为您不得不处理数据的同步问题,否则您的应用程序也许很可能得到过时或错误的数据甚至崩溃。本课中我们将主要讲述内存映射文件,将不涉及进程间的同步。WIN32中的内存映射文件应用非常广泛,譬如:即使是系统的核心模块—PE格式文件装载器也用到了内存映射文件,因为PE格式的文件并不是一次性加载到内存中来的,譬如他它在首次加载时只加载必需加载的部分,而其他部分在用到时再加载,这正好可以利用到内存映射文件的长处。实际中的大多数文件存取都和PE加载器类似,所以您在处理该类问题时也应该充分利用内存映射文件。

内存映射文件本身还是有一些局限性的,譬如一旦您生成了一个内存映射文件,那么您在那个会话期间是不能够改变它的大小的。所以内存映射文件对于只读文件和不会影响其大小的文件操作是非常有用的。当然这并不意味着对于会引起改变其大小的文件操作就一定不能用内存影射文件的方法,您可以事先估计操作后的文件的可能大小,然后生成这么大小一块的内存映射文件,然后文件的长度就可以增长到这么一个大小。 我们的解释够多的了,接下来我们就看看实现的细节:

调用CreateFile打开您想要映射的文件。
调用CreateFileMapping,其中要求传入先前CreateFile返回的句柄,该函数生成一个建立在CreateFile函数创建的文件对象基础上的内存映射对象。
调用MapViewOfFile函数映射整个文件的一个区域或者整个文件到内存。该函数返回指向映射到内存的第一个字节的指针。
用该指针来读写文件。
调用UnmapViewOfFile来解除文件映射。
调用CloseHandle来关闭内存映射文件。注意必须传入内存映射文件的句柄。
调用CloseHandle来关闭文件。注意必须传入由CreateFile创建的文件的句柄。
例子:
下面的例子允许用户通过“打开文件”对话框来打开一个文件,然后用内存映射文件来打开该文件,如果成功,窗口的标题条会显示打开的文件的名称,您可以通过选择“File/Save”菜单项来把换名保存。该程序将会把打开的文件的内容存到新文件中去。注意,这整个过程您根本就没有用到GlobalAlloc这样的分配内存的函数。

.386
.model flat,stdcall
WinMain proto :D WORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib

.const
IDM_OPEN equ 1
IDM_SAVE equ 2
IDM_EXIT equ 3
MAXSIZE equ 260

.data
ClassName db "Win32ASMFileMappingClass",0
AppName  db "Win32 ASM File Mapping Example",0
MenuName db "FirstMenu",0
ofn   OPENFILENAME <>
FilterString db "All Files",0,"*.*",0
             db "Text Files",0,"*.txt",0,0
buffer db MAXSIZE dup(0)
hMapFile HANDLE 0                            ; Handle to the memory mapped file, must be
                                                                    ;initialized with 0 because we also use it as
                                                                    ;a flag in WM_DESTROY section too

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hFileRead HANDLE ?                               ; Handle to the source file
hFileWrite HANDLE ?                                ; Handle to the output file
hMenu HANDLE ?
pMemory DWORD ?                                 ; pointer to the data in the source file
SizeWritten DWORD ?                               ; number of bytes actually written by WriteFile

.code
start:
        invoke GetModuleHandle, NULL
        mov    hInstance,eax
        invoke GetCommandLine
        mov CommandLine,eax
        invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
        invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND
    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInst
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,OFFSET MenuName
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,\
                ADDR AppName, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
               CW_USEDEFAULT,300,200,NULL,NULL,\
    hInst,NULL
    mov   hwnd,eax
    invoke ShowWindow, hwnd,SW_SHOWNORMAL
    invoke UpdateWindow, hwnd
    .WHILE TRUE
        invoke GetMessage, ADDR msg,NULL,0,0
        .BREAK .IF (!eax)
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
    .ENDW
    mov     eax,msg.wParam
    ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .IF uMsg==WM_CREATE
        invoke GetMenu,hWnd                       ;Obtain the menu handle
        mov  hMenu,eax
        mov ofn.lStructSize,SIZEOF ofn
        push hWnd
        pop  ofn.hWndOwner
        push hInstance
        pop  ofn.hInstance
        mov  ofn.lpstrFilter, OFFSET FilterString
        mov  ofn.lpstrFile, OFFSET buffer
        mov  ofn.nMaxFile,MAXSIZE
    .ELSEIF uMsg==WM_DESTROY
        .if hMapFile!=0
            call CloseMapFile
        .endif
        invoke PostQuitMessage,NULL
    .ELSEIF uMsg==WM_COMMAND
        mov eax,wParam
        .if lParam==0
            .if ax==IDM_OPEN
                mov  ofn.Flags, OFN_FILEMUSTEXIST or \
                                OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
                                OFN_EXPLORER or OFN_HIDEREADONLY
                                invoke GetOpenFileName, ADDR ofn
                .if eax==TRUE
                    invoke CreateFile,ADDR buffer,\
                                                GENERIC_READ ,\
                                                0,\
                                                NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
                                                NULL
                    mov hFileRead,eax
                    invoke CreateFileMapping,hFileRead,NULL,PAGE_READONLY,0,0,NULL
                    mov     hMapFile,eax
                    mov     eax,OFFSET buffer
                    movzx  edx,ofn.nFileOffset
                    add      eax,edx
                    invoke SetWindowText,hWnd,eax
                    invoke EnableMenuItem,hMenu,IDM_OPEN,MF_GRAYED
                    invoke EnableMenuItem,hMenu,IDM_SAVE,MF_ENABLED
                .endif
            .elseif ax==IDM_SAVE
                mov ofn.Flags,OFN_LONGNAMES or\
                                OFN_EXPLORER or OFN_HIDEREADONLY
                invoke GetSaveFileName, ADDR ofn
                .if eax==TRUE
                    invoke CreateFile,ADDR buffer,\
                                                GENERIC_READ or GENERIC_WRITE ,\
                                                FILE_SHARE_READ or FILE_SHARE_WRITE,\
                                                NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,\
                                                NULL
                    mov hFileWrite,eax
                    invoke MapViewOfFile,hMapFile,FILE_MAP_READ,0,0,0
                    mov pMemory,eax
                    invoke GetFileSize,hFileRead,NULL
                    invoke WriteFile,hFileWrite,pMemory,eax,ADDR SizeWritten,NULL
                    invoke UnmapViewOfFile,pMemory
                    call   CloseMapFile
                    invoke CloseHandle,hFileWrite
                    invoke SetWindowText,hWnd,ADDR AppName
                    invoke EnableMenuItem,hMenu,IDM_OPEN,MF_ENABLED
                    invoke EnableMenuItem,hMenu,IDM_SAVE,MF_GRAYED
                .endif
            .else
                invoke DestroyWindow, hWnd
            .endif
        .endif
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor    eax,eax
    ret
WndProc endp

CloseMapFile PROC
        invoke CloseHandle,hMapFile
        mov    hMapFile,0
        invoke CloseHandle,hFileRead
        ret
CloseMapFile endp

end start
 

分析:
                    invoke CreateFile,ADDR buffer,\
                                                GENERIC_READ ,\
                                                0,\
                                                NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
                                                NULL

当用户选择打开文件时,我们调用CreateFile来打开。注意我们指定GENERIC_READ(一般的读)来表示我们打开的文件只能够读出,把dwShareMode设成0,表示我们不想其他进程在我们操作文件时来存取该文件。

                    invoke CreateFileMapping,hFileRead,NULL,PAGE_READONLY,0,0,NULL

我们调用CreateFileMapping来在打开的文件的基础上生成内存映射文件。CreateFileMapping的语法如下:

CreateFileMapping proto hFile:DWORD,\
                                         lpFileMappingAttributes:DWORD,\
                                         flProtect:DWORD,\
                                         dwMaximumSizeHigh:DWORD,\
                                         dwMaximumSizeLow:DWORD,\
                                         lpName:DWORD

您应当知道该函数并没有必要把整个文件映射到内存中去,您可以用该函数来只映射文件的一部分。您可以在参数dwMaximumSizeHigh和dwMaximumSizeLow中指定内存映射文件的大小,如果您指定的值大于实际的文件,则实际的文件将增长到指定的大小,如果想要映射的内存大小正好和文件的实际大小相等,则把两个参数中都设成为0。您可以设定lpFileMappingAttributes为NULL,让WINDOWS赋予该内存映射文件于缺省的安全属性。
flProtect定义了内存映射文件的保护属性,我们指定它为PAGE_READONLY来规定该内存映射文件只能够读。注意该属性不能和CreateFile中指定的属性相矛盾,否则就不能生成内存映射文件。
lpName指定内存映射文件的名称,如果您想要该内存映射文件同时可以供其它的进程使用,就必须给它取个名称。不过在我们的例子中,只有我们的进程使用该内存映射文件故我们忽略该参数。

                    mov     eax,OFFSET buffer
                    movzx  edx,ofn.nFileOffset
                    add      eax,edx
                    invoke SetWindowText,hWnd,eax

如果函数CreateFileMapping调用成功,我们把窗口的标题条换成被打开文件的名称。保存在缓冲区中的文件名是带有路径的全文件名,所以为了只显示文件名我们需要利用OPENFILENAME结构体中的成员nFileOffset的值来找到文件名的起始地址。

                    invoke EnableMenuItem,hMenu,IDM_OPEN,MF_GRAYED
                    invoke EnableMenuItem,hMenu,IDM_SAVE,MF_ENABLED

为了避免用户一次性打开多个文件,我们让“打开文件”菜单项呈灰色显示,使得打开文件的菜单项失效。函数EnableMenuItem可以用来改变菜单项的属性。 之后用户可能保存文件或者直接关闭应用程序。如果用户选择关闭应用程序,则事先必须关闭内存映射文件和打开的文件, 代码如下:

    .ELSEIF uMsg==WM_DESTROY
        .if hMapFile!=0
            call CloseMapFile
        .endif
        invoke PostQuitMessage,NULL

在上面的代码段中,当WINDOWS的消息处理过程接收到WM_DESTROY消息后,它首先检测hMapFile值是否为0。如果不为0则表示相关的文件未关闭,这样就需要调用CloseMapFile来关闭它们。

CloseMapFile PROC
        invoke CloseHandle,hMapFile
        mov    hMapFile,0
        invoke CloseHandle,hFileRead
        ret
CloseMapFile endp

上述过程调用是用来关闭内存映射文件和原来打开的文件的,这样可以使得程序退出时没有资源泄漏。如果用户选择保存文件的话,就弹出一个“保存文件”对话框,当用户输入了新文件的名称后,我们调用CreateFile函数来创建新文件—输出文件。

                    invoke MapViewOfFile,hMapFile,FILE_MAP_READ,0,0,0
                    mov pMemory,eax

在输出文件创建后我们调用MapViewOfFile来映射希望映射到内存中的部分。该函数的语法如下:

MapViewOfFile proto hFileMappingObject:DWORD,\
                                   dwDesiredAccess:DWORD,\
                                   dwFileOffsetHigh:DWORD,\
                                   dwFileOffsetLow:DWORD,\
                                   dwNumberOfBytesToMap:DWORD

dwDesiredAccess用来指定我们想对文件进行的操作。在我们例子中,我们只想读,故指定标志FILE_MAP_READ。
dwFileOffsetHigh 和 dwFileOffsetLow 用来指定打开文件中欲映射的起始偏移位置。我们的例子中想映射整个的文件,故指定它们的值为0。
dwNumberOfBytesToMap 用来指定欲映射的字节数,如果想映射整个的文件,设定该值为0。
调用MapViewOfFile后,我们希望的部分就已经映射到内存中去了。您将得到一个指向起始内存块的指针。

                    invoke GetFileSize,hFileRead,NULL

调用该函数可以得到文件的大小,其值通过eax传送,如果文件的长度超过4G,那么文件长度DWORD的高值部分(也即超过4G的部分)保存在FileSizeHighWord中。因为我们估计一般的文件将没有这么大,故忽略该值。

                    invoke WriteFile,hFileWrite,pMemory,eax,ADDR SizeWritten,NULL

把内存映射文件中的数据写到输出文件中去。

                    invoke UnmapViewOfFile,pMemory

写完后,我们解除映射。

                    call   CloseMapFile
                    invoke CloseHandle,hFileWrite

关闭内存映射文件和输出文件的句柄。

                    invoke SetWindowText,hWnd,ADDR AppName

恢复窗口的标题条到应用程序的名称。

                    invoke EnableMenuItem,hMenu,IDM_OPEN,MF_ENABLED
                    invoke EnableMenuItem,hMenu,IDM_SAVE,MF_GRAYED

恢复“打开文件”和“保存文件”菜单项使的可以重新开始新的打开、编辑和保存循环。
 
 
第十二课 内存管理和文件输入/输出


——————————————————————————–

本课中我们将学习基本的内存管理和文件输入/输出操作方面的知识。另外我们还将用上课学的通用对话框作为我们的显示“设备”。

理论:

从用户的角度来看,WIN32的内存管理是非常简单和明了的。每一个应用程序都有自己独立的4G地址空间,这种内存模式叫做“平坦”型地址模式,所有的段寄存器或描述符都指向同样的起始地址,所有的地址偏移都是32位的长度,这样一个应用程序无须变换选择符就可以存取自己的多达4G的地址空间。这种内存管理模式是非常简洁而便于管理的,而且我们再不用和那些令人讨厌的“near”和“far”指针打交道了。
在W16下有两种主要类型的API:全局和局部。“全局”的API 分配在其他的段中,这样从内存角度来看他们是一些“far”(远)函数或者叫远过程调用,“局部”API只要和进程的堆打交道,所以把它们叫做“near”(近)函数或者近过程调用。而在WIN32中,这两种内存模式是相同的,无论您调用GlobalAlloc还是LocalAlloc,结果都是一样。
至于分配和使用内存的过程都是一样的:

调用GlobalAlloc函数分配一块内存,该函数会返回分配的内存句柄。
调用GlobalLock函数锁定内存块,该函数接受一个内存句柄作为参数,然后返回一个指向被锁定的内存块的指针。
您可以用该指针来读写内存。
调用GlobalUnlock函数来解锁先前被锁定的内存,该函数使得指向内存块的指针无效。
调用GlobalFree函数来释放内存块。您必须传给该函数一个内存句柄。
在WIN32中您也可以用“Local”替代内存分配API函数带有“Global”字样的函数中的“Global”,也即用LocalAlloc、LocalLock等。
在调用函数GlobalAlloc时使用GMEM_FIXED标志位可以更进一步简化操作。使用了该标志后,Global/LocalAlloc返回的是指向已分配内存的指针而不是句柄,这样也就不用调用Global/LocalLock来锁定内存了,释放内存时只要直接调用Global/LocalFree就可以了。不过在本课中我们只使用传统的方法,因为其它地方有许多的源代码是用这种方法写的。

WIN32的文件输入/输出API和DOS下的从外表上看几乎一样(译者注:也许不管内部实现多么不同,可以想象所有的文件系统暴露给应用程序编写者的接口的功能应该基本相同),不同的只是把DOS下的中断方式处理文件输入/输出变成了对API函数的调用。以下是基本的步骤:
 

调用CreateFile函数生成一个文件,该函数可以应用在多方面,除了磁盘文件外,我们还可以用来打开通讯端口、管道、驱动程序或控制台。如果成功的话,会返回指向文件或设备的句柄。然后可以使用该句柄去完成对文件或设备操作。
调用SetFilePointer来把文件指针移到想读写的地方。.
然后调用ReadFile 或 WriteFile来完成实际的读写。这些函数会自己处理文件和内存之间的数据传送,这样免得您自己去做分配内存等繁杂的琐事。
调用CloseHandle来关闭文件。该函数接受一个先前打开的文件句柄。
内容:

下面的代码段演示了:打开一个“打开文件”对话框,用户可以选择打开一个文本文件,然后在一个编辑控件中打开该文本文件的内容,另外用户还可以编辑该文本文件的内容并选择保存。

.386
.model flat,stdcall
option casemap:none
WinMain proto :D WORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib

.const
IDM_OPEN equ 1
IDM_SAVE equ 2
IDM_EXIT equ 3
MAXSIZE equ 260
MEMSIZE equ 65535

EditID equ 1                            ; ID of the edit control

.data
ClassName db "Win32ASMEditClass",0
AppName  db "Win32 ASM Edit",0
EditClass db "edit",0
MenuName db "FirstMenu",0
ofn   OPENFILENAME <>
FilterString db "All Files",0,"*.*",0
             db "Text Files",0,"*.txt",0,0
buffer db MAXSIZE dup(0)

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndEdit HWND ?                               ; Handle to the edit control
hFile HANDLE ?                                   ; File handle
hMemory HANDLE ?                            ;handle to the allocated memory block
pMemory DWORD ?                            ;pointer to the allocated memory block
SizeReadWrite DWORD ?                   ; number of bytes actually read or write

.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke GetCommandLine
    mov CommandLine,eax
    invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
    invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND
    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInst
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,OFFSET MenuName
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
           CW_USEDEFAULT,300,200,NULL,NULL,\
           hInst,NULL
    mov   hwnd,eax
    invoke ShowWindow, hwnd,SW_SHOWNORMAL
    invoke UpdateWindow, hwnd
    .WHILE TRUE
        invoke GetMessage, ADDR msg,NULL,0,0
        .BREAK .IF (!eax)
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
    .ENDW
    mov     eax,msg.wParam
    ret
WinMain endp

WndProc proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .IF uMsg==WM_CREATE
        invoke CreateWindowEx,NULL,ADDR EditClass,NULL,\
                   WS_VISIBLE or WS_CHILD or ES_LEFT or ES_MULTILINE or\
                   ES_AUTOHSCROLL or ES_AUTOVSCROLL,0,\
                   0,0,0,hWnd,EditID,\
                   hInstance,NULL
        mov hwndEdit,eax
        invoke SetFocus,hwndEdit
;==============================================
;        Initialize the members of OPENFILENAME structure
;==============================================
        mov ofn.lStructSize,SIZEOF ofn
        push hWnd
        pop  ofn.hWndOwner
        push hInstance
        pop  ofn.hInstance
        mov  ofn.lpstrFilter, OFFSET FilterString
        mov  ofn.lpstrFile, OFFSET buffer
        mov  ofn.nMaxFile,MAXSIZE
    .ELSEIF uMsg==WM_SIZE
        mov eax,lParam
        mov edx,eax
        shr edx,16
        and eax,0ffffh
        invoke MoveWindow,hwndEdit,0,0,eax,edx,TRUE
    .ELSEIF uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .ELSEIF uMsg==WM_COMMAND
        mov eax,wParam
        .if lParam==0
            .if ax==IDM_OPEN
                mov  ofn.Flags, OFN_FILEMUSTEXIST or \
                                OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
                                OFN_EXPLORER or OFN_HIDEREADONLY
                invoke GetOpenFileName, ADDR ofn
                .if eax==TRUE
                    invoke CreateFile,ADDR buffer,\
                                GENERIC_READ or GENERIC_WRITE ,\
                                FILE_SHARE_READ or FILE_SHARE_WRITE,\
                                NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
                                NULL
                    mov hFile,eax
                    invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
                    mov  hMemory,eax
                    invoke GlobalLock,hMemory
                    mov  pMemory,eax
                    invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,NULL
                    invoke SendMessage,hwndEdit,WM_SETTEXT,NULL,pMemory
                    invoke CloseHandle,hFile
                    invoke GlobalUnlock,pMemory
                    invoke GlobalFree,hMemory
                .endif
                invoke SetFocus,hwndEdit
            .elseif ax==IDM_SAVE
                mov ofn.Flags,OFN_LONGNAMES or\
                                OFN_EXPLORER or OFN_HIDEREADONLY
                invoke GetSaveFileName, ADDR ofn
                    .if eax==TRUE
                        invoke CreateFile,ADDR buffer,\
                                                GENERIC_READ or GENERIC_WRITE ,\
                                                FILE_SHARE_READ or FILE_SHARE_WRITE,\
                                                NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,\
                                                NULL
                        mov hFile,eax
                        invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
                        mov  hMemory,eax
                        invoke GlobalLock,hMemory
                        mov  pMemory,eax
                        invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMemory
                        invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite,NULL
                        invoke CloseHandle,hFile
                        invoke GlobalUnlock,pMemory
                        invoke GlobalFree,hMemory
                    .endif
                    invoke SetFocus,hwndEdit
                .else
                    invoke DestroyWindow, hWnd
                .endif
            .endif
        .ELSE
            invoke DefWindowProc,hWnd,uMsg,wParam,lParam
            ret
.ENDIF
xor    eax,eax
ret
WndProc endp
end start

——————————————————————————–

 

分析:
        invoke CreateWindowEx,NULL,ADDR EditClass,NULL,\
                   WS_VISIBLE or WS_CHILD or ES_LEFT or ES_MULTILINE or\
                   ES_AUTOHSCROLL or ES_AUTOVSCROLL,0,\
                   0,0,0,hWnd,EditID,\
                   hInstance,NULL
        mov hwndEdit,eax

处理 WM_CREATE消息时,我们创建一个编辑控件。请注意,我们把该控件大小的有关参数都设成0,因为我们稍后将重新设置该编辑控件的大小,使得其覆盖父窗口的整个客户区。
注意:本例中我们没有必要调用ShowWindow来显示编辑控件,因为在创建时在其风格中已设置了WS_VISIBLE标志位,在创建父窗口时也可以使用这个小技巧。

;==============================================
;        Initialize the members of OPENFILENAME structure
;==============================================
        mov ofn.lStructSize,SIZEOF ofn
        push hWnd
        pop  ofn.hWndOwner
        push hInstance
        pop  ofn.hInstance
        mov  ofn.lpstrFilter, OFFSET FilterString
        mov  ofn.lpstrFile, OFFSET buffer
        mov  ofn.nMaxFile,MAXSIZE

创建完编辑控件后,我们初始话ofn变量的成员。因为稍后在保存文件时还要使用该结构体变量,所以此处只初始化要用到的公共部分。WM_CREATE 消息的处理部分是进行这种初始化的绝佳之处。

    .ELSEIF uMsg==WM_SIZE
        mov eax,lParam
        mov edx,eax
        shr edx,16
        and eax,0ffffh
        invoke MoveWindow,hwndEdit,0,0,eax,edx,TRUE

当主窗口的客户区部分大小改变时,我们的应用程序将接收到WM_SIZE 消息。当然该窗口第一次显示时,我们也将接收到该消息。要接收到该消息,主窗口必须有CS_VREDRAW和CS_HREDRAW风格。我们应该把缩放编辑控件的动作放到此处。我们要把编辑控件变成和我们的窗口客户区一样大,所以先得要得到父窗口客户区的大小。这些值包含在参数lParam中,lParam的高字部分是客户区的高,底字部分是客户区的宽。然后我们调用MoveWindow函数来重新调整编辑控件的大小,该函数不仅能够移动窗口的位置,而且能够改变窗口的大小。

            .if ax==IDM_OPEN
                mov  ofn.Flags, OFN_FILEMUSTEXIST or \
                                OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
                                OFN_EXPLORER or OFN_HIDEREADONLY
                invoke GetOpenFileName, ADDR ofn

当用户选择了File/Open菜单项时,我们填充ofn的其他成员,然后调用GetOpenFileName函数显示一个“打开文件”对话框。

                .if eax==TRUE
                    invoke CreateFile,ADDR buffer,\
                                GENERIC_READ or GENERIC_WRITE ,\
                                FILE_SHARE_READ or FILE_SHARE_WRITE,\
                                NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
                                NULL
                    mov hFile,eax

如果用户选择了一个文件时,我们调用CreateFile函数来打开。我们设置标志位来让该函数的文件能够读写。文件打开后我们把返回的文件句柄保存在一个全局变量中以便以后使用。CreateFile函数应用非常广泛,其原型如下:

CreateFile proto lpFileName:DWORD,\
                           dwDesiredAccess:DWORD,\
                           dwShareMode:DWORD,\
                           lpSecurityAttributes:DWORD,\
                           dwCreationDistribution:DWORD\,
                           dwFlagsAndAttributes:DWORD\,
                           hTemplateFile:DWORD

dwDesiredAccess 指定想要进行的操作。

0  打开文件查询它的属性。
GENERIC_READ   打开文件读
GENERIC_WRITE  打开文件写.
dwShareMode 指定文件的共享模式。

0  不让其他进程共享,即当您打开该文件后,其他进程欲打开该文件时将失败。
FILE_SHARE_READ  允许其他进程读。
FILE_SHARE_WRITE  允许其他进程写。
lpSecurityAttributes 该属性在WIN95下无效。
dwCreationDistribution 指定欲生成的文件在其已存在和未存在时应做的动作。

CREATE_NEW 生成一个新文件。如果文件已存在则失败。
CREATE_ALWAYS 无论文件是否存在都生成一个新文件。
OPEN_EXISTING 打开存在的文件。如果文件不存在则失败。
OPEN_ALWAYS 打开文件,如果该文件不存在则生成,这和在dwCreationDistribution 中设置 CREATE_NEW标志位一样。
TRUNCATE_EXISTING打开文件。打开时该文件的长度裁减到零(也即完全不要原来的文件了)。这要求调用进程必须有GENERIC_WRITE的权利,如果指定的文件不存在,该函数返回失败。
dwFlagsAndAttributes 指定文件的属性。

FILE_ATTRIBUTE_ARCHIVE 该文件具有一般的归档文件的属性。用户可以用该标志位来标记文件的删除和备份。
FILE_ATTRIBUTE_COMPRESSED 文件或目录是压缩的。对于文件来说是压缩其中的所有数据,而对于目录来说新生成的子目录和文件都要压缩。
FILE_ATTRIBUTE_NORMAL 该文件没有一般的属性集。该标志位只能单独使用。
FILE_ATTRIBUTE_HIDDEN 该文件是隐藏文件,当浏览一般的文件目录时将不显示它。
FILE_ATTRIBUTE_READONLY 该文件是只读文件。应用程序可以读其中的内容,但不可以写。
FILE_ATTRIBUTE_SYSTEM 该文件是系统文件。
                    invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
                    mov  hMemory,eax
                    invoke GlobalLock,hMemory
                    mov  pMemory,eax

文件打开后,我们将分配一块内存供随后的API 函数ReadFile 和 WriteFile使用。我们使用标志GMEM_MOVEABLE来使得WINDOWS总是把内存块移到可靠的内存中去,GMEM_ZEROINIT告诉WINDOWS把刚刚分配的内存置为零。如果GlobalAlloc调用成功的话,会在eax中返回内存块的句柄,我们把该句柄传给GlobalLock函数以得到指向内存块的指针。

                    invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,NULL
                    invoke SendMessage,hwndEdit,WM_SETTEXT,NULL,pMemory

使内存块可用后,我们调用ReadFile函数从文件中读数据。对于第一次打开的文件,文件的指针放在偏移0处,像本例中我们从偏移0处往前读。ReadFile的第一个参数是文件句柄,第二个参数是指向内存块的指针,接下来的参数是要读的数据的长度,第四个参数是一个指向DWORD型的参数的指针,它用来存放实际读的数据的长度。读完了后,我们把这些内容存放到编辑控件中,这要用消息传递来完成,我们把消息WM_SETTEXT传给编辑控件,其中的参数lParam中包含指向内存块的指针。到此处,编辑控件就可以在它的客户区显示文件的内容了。

                    invoke CloseHandle,hFile
                    invoke GlobalUnlock,pMemory
                    invoke GlobalFree,hMemory
                .endif

我们不再需要让文件打开了,因为我们的目的是把修改后的数据保存到另一个文件而不是先前的那一个文件中去。所以我们可以调用CloseHandle来关闭文件。接下来我们解锁内存块,再释放它。实际上我们可以暂不释放内存块,而在以后的操作中重新利用。我们为了演示的原由,选择了释放它。

                invoke SetFocus,hwndEdit

当打开文件对话框显示在屏幕上时,输入的焦点切换到了该对话框上。所以在该对话框关闭后,我们必须把焦点切换到编辑控件上。 现在打开文件的阶段结束了,用户可以编辑他们打开的文件了。当用户想把修改后的内容保存到磁盘上时,必须选择File/Save菜单项,这时会显示一个保存文件对话框。显示保存文件对话框其实和打开打开文件对话框基本一样。您甚至可以认为他们的不同只是函数名称不一样而已。此处可以复用大多数ofn变量先前设置的成员的值。

                mov ofn.Flags,OFN_LONGNAMES or\
                                OFN_EXPLORER or OFN_HIDEREADONLY

本例中我们将生成一个新文件,所以一定不能有 OFN_FILEMUSTEXIST 和 OFN_PATHMUSTEXIST标志位。dwCreationDistribution 参数应当有CREATE_NEW标志位。 接下来的代码和打开问对话框基本一样。最后调用:

                        invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMemory
                        invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite,NULL

现在我们把修改后的数据从编辑控件中写回内存块,再从内存块写回新文件

1.林彪不抽煙不喝酒只活了63嵗
2.周恩來只喝酒不抽煙,活了73嵗
3.毛澤東只抽煙不喝酒,活了83嵗
4.鄧小平即抽煙又喝酒,活了93嵗
5.而張學良老將軍吃喝嫖賭樣樣都來,活了103嵗
 現在還不抽煙不喝酒的同志們  要汲取歷史上沉痛的教訓阿

vice versa  反之亦然

likewise   同样的

要写好程式,首先应熟记8088指令的时钟脉冲(Clock )及指令长度,一般组合语言手册中,都详列了与各指令相关的资料。「工欲善其事,必先利其器」,此之谓也。
    本节所讨论的,是一般程式师容易忽略的细节,所有的例子都是从我所看过的一些程式中摘录下来的。看来没什么大了不起,可是程式的效率,受到这些小地方的影响很大。更重要的是,任何一个人,只要有「小事不做,小善不为」的习惯,我敢断言,这个人不会有什么大成就!
    我最近才查到 Effective Address (EA) 的时钟值,我觉得没有必要死记。原则上,以暂存器为变数,做间接定址时为5个时钟,用直接定址则为6个;若用了两组变数,则为7至9个,三组则为11或12个。
    为了便于叙述,下面以“T”表「时钟脉冲」; “B”表字元。其中
    时钟脉冲T = 1 / 振荡频率

一、避免浪费速度及空间

    组合语言的效率建立在指令的运用上,如果不用心体会下列指令的有效用法,组合语言的优点就难以发挥。
  1,    CALL    ABCD
        RET
    这种写法,是没有用心的结果,共用了 4B,23T+20T,完全相同的功能,如:
        JMP     ABCD  或 
        JMP     SHORT ABCD
    却只要 2-3B,15T。
        此外,上述的CALL XXXX 是调用子程式的格式,在直觉认知上,与JMP XXXX完全不同。对整体设计而言,是不可原谅的错误,侦错的时候,也很难掌握全盘的理念。 尤其是在精简程式的时候,很可能会遇到 ABCD 这个子程式完全独立,是则把这段程式直接移到 ABCD 前,不仅能节省空间,而且使程式具有连贯性,易读易用。

  2,    MOV     AX,0
    同样,这条指令要 3B,4T,如果用:
        SUB     AX,AX 或 
        XOR     AX,AX
    只要 2B,3T, 唯一要注意的是,后者会影响旗号,所以不要用在有旗号判断的指令前面。
     在程式写作中,经常需要将暂存器或缓冲器清为0,有效的方法,是使某暂存器保持为0,以便随时应用。  因为,MOV [暂存器],[暂存器] 只要 2B,2T, 即使是清缓冲器,也比直接填0为佳。
     只是,如何令暂存器保持0,则要下一番功夫了。
     还有一种情况,就是在一回路中,每次都需要将 AH 清0,此时对速度要求很严,有一个指令 CBW 原为将一 个字元转换为双字元,只需 1B,2T 最有效率。可是应该注意,此时 AL 必须小于 80H,否则 AH 将成为负数。
  3,    ADD     AX,AX
    需要 2B,3T不如用:
        SHL     AX,1
    只要2B,2T。

  4,    MOV     AX,4
    除非这时 AH 必为0,否则,应该用:
        MOV     AL,4
    这样会少一个字元。

  5,    MOV     AL,46H
        MOV     AH,0FFH
    为什么不写成:
        MOV     AX,0FF46H
    不仅省了一个字元,四个时钟,而且少打几个字母!

  6,    CMP     CX,0
    需要 4B,4T, 但若用:
     OR      CX,CX
    完全相同的功能,但只要 2B,3T。再若用:
        JCXZ    XXXX
    则一条指令可以替代两条,时空都省。不幸这条指令限用于CX ,对其他暂器无效。

  7,    SUB     BX,1
    这更不能原谅,4B,4T无端浪费。
        DEC     BX
    现成的指令,1B,2T为何不用?
        如果是
        SUB     BL,1 
    也应该考虑此时 BH 的情况,若可以用
         DEC     BX
    取代,且不影响后果,亦不妨用之。

  8,    MOV     AX,[SI]
        INC     SI
        INC     SI
    这该挨骂了,一定是没有记熟指令,全部共4B,21T。
        LODSW
    正是为这个目的设计,却只要 1B,16T。

  9,    MOV     CX,8
        MUL     CX
        写这段程式之时应先养成习惯,每遇到乘、除法,就该打一下算盘。因为它们太浪费时间。8位元的要七十多个时钟,16位元则要一百多。所以若有可能,尽量设法用简单的指令取代。
        SHL     AX,1
        SHL     AX,1
        SHL     AX,1
     原来要 5B,137T,现在只要 6B,6T。如果CX能够动用的话,则写成:
      MOV     CL,3
      SHL     AX,CL
     这样更佳,而且CL之值越大越有利。用CL作为计数专 用暂存器,不仅节省空间,且因指令系在 CPU中执行,速 度也快。可是究竟快了多少? 我们做了些测试,以 SHL为例,在10MHZ 频率的机器上,作了3072 ×14270次,

所测得时间为:
    指  令 :SHL   AX,CL         SHL   AX,n
          CL = 0 , 23 秒     n = 0 , 无效
      CL = 1 , 27 秒     n = 1 , 14 秒
          CL = 2 , 32 秒     n = 2 , 28 秒
          CL = 3 , 36 秒     n = 3 , 42 秒
          CL = 4 , 40 秒     n = 4 , 56 秒
          CL = 5 , 44 秒     n = 5 , 71 秒
          CL = 6 , 49 秒     n = 6 , 85 秒
          CL = 7 , 54 秒     n = 7 , 99 秒
        由此可知,用CL在大于2时即较分别执行有效。
        此外,亦可利用回路做加减法,但要算算值不值得,且应注意是否有调整余数的需要。

10,    MOV     WORD PTR BUF1,0
        MOV     WORD PTR BUF2,0
        MOV     WORD PTR BUF3,0
        MOV     BYTE PTR BUF4,0
        ..
        我见过太多这种程式,一见就无名火起! 在程式中,最好经常保留一个暂存器为0,以便应付这种情况。即使没有,也要设法使一暂存器为0,以节省时、空。
        SUB     AX,AX
        MOV     BUF1,AX
        MOV     BUF2,AX
        MOV     BUF3,AX
        MOV     BUF4,AL

     14B,59T取代了 24B,76T,当然值得。只是,还是不 如事先有组织,考虑清楚各个缓冲器间的应用关系。以前面举的例来说,假定各缓冲器内数字,即为其实际位置关系,则可以写成:
         MOV     CX,3   
  如已知 CH 为0,则用: 
    MOV     CL,3
        SUB     AX,AX
        MOV     DI,OFFSET BUF1
        REP     STOSW
        STO注意用词!   
    这段程式越长越占便宜,现在用10B,37T,一样划算。

11,子程式之连续调用:
        CALL    ABCD
        CALL    EFGH
        如果 ABCD,EFGH 都是子程式,且调用的次数甚多,则上述调用的方式就有待商榷了。因为连续两次调用,不仅时间上不划算,空间也浪费。
        若ABCD一定与EFGH连用,应将ABCD放在EFGH之前:
        ABCD: 
            ..
        EFGH: 
            ..
        像这样,只要调用ABCD就够了,但这种情形多半是程式师的疏忽所致,如两个子程式必需独立使用,而上述连续调用的机会超过两次以上,则应该改为:
        CALL    ABCDEF
        而ABCDEF则应为:
        ABCDEF: 
               CALL    ABCD
        EFGH: 
            ..
        这样的写法速度不会变慢,而空间的节省则与调用的次数成正比。

12,常有些程式,当从缓冲器中取资料时,必须将暂存器高位置为0。如:
        SUB     AH,AH
        MOV     AL,BUFFER
     这时应该将 BUFFER 先设为:
        BUFFER  DB  ?,0
     然后用: 
        MOV     AX,WORD PTR BUFFER
        如此,不但速度快了,空间也省了。

13,有时看来多了一个指令,但因为指令的特性,反而更为精简。如:
    OR    ES:[DI],BH
    OR    ES:[DI+1],BL
    这样需要8B,32T,如果改用下面的指令:
    XCHG    BL,BH
    OR    ES:[DI],BX
    XCHG    BH,BL
    则需7B,28T。

14,PUSH  及 POP  是保存暂存器原值的指令,都只需一个字元,但却很费时间。  PUSH  占 15T,POP 占12T,除非不得已,不可随便使用。有时由于子程式说明不清楚,程式师为了安全,又懒得检查,便把暂存器统统堆在堆栈上。尤其是在系统程式或子程式中,经常有到堆栈上堆、取的动作。实际上,花点功夫,把暂存器应用查清楚,就可以增进不少效率。    要知道,系统程式及某些子程式常常应用,有关速度的效率甚大,如果掉以轻心,就是不负责任! 保存原值的方法很多,其中较有效率的是放到一些不用的暂存器里。以我的经验,堆栈器用途最少,正好用作临时仓库。但最好的办法,还是把程式中暂存器的应用安排得合情合理,不要浪费,以免堆得太多。 还有一种方法,是在该子程式中,不用堆栈的手续,但另设一个入口,先将暂存器堆起,再来调用不用堆栈的子程式。这两个不同的入口,可以分别提供给希望快速处理,或需要保留暂存器原值者调用。
      当然,更简单有效的方法,则是说明本段程式中某些暂存器将被破坏,而由调用者自行保存之。

二、程式要条理通顺

  1,在比较判断的过程中,邻近值不必连比。
        CMP     AL,0
        JE      ABCD0
        CMP     AL,1
        JE      ABCD1
        CMP     AL,2
        JE      ABCD2
        ..
    应为:
        CMP     AL,1
        JNE     ABCD0
    ABCD1: 
        ..
    在标题为ABCD0 中,再作:
        JA      ABCD2
    这种做法端视时间效益而定,似此 ABCD1之速度最快。

  2,未经慎思的流程:
        ADD     AX,4
    ABCD:
        STOSW
        ADD     AX,4
        ADD     DI,2
        LOOP    ABCD
        ..
    稍稍动点脑筋,就好得多了:
    ABCD:
        ADD     AX,4
        STOSW
        INC     DI
        INC     DI
        LOOP    ABCD
        ..

  3,错误的处理方式:
        MOV     BX,SI
    ABCD:
        MOV     BX,[BX]
        OR      BX,BX
        JZ      ABCD1
        MOV     SI,BX
        JMP     ABCD
    ABCD1:
        LODSW
        ..
    上例应该写成:
        MOV     BX,SI
    ABCD:
        LODSW
        OR      AX,AX
        JZ      ABCD1
        MOV     SI,BX
        JMP     ABCD
    ABCD1:
        ..

  4,错误的流程:
        TEST    AL,20H
        JNZ     ABCD
        CALL    CDEF[BX]
        JMP     SHORT ABCD1
    ABCD:
        CALL    CDEF[BX+2]
    ABCD1:
        ..
应该写成:  
        TEST    AL,20H
        JZ      ABCD
        INC     BX
        INC     BX
    ABCD:
        CALL    CDEF[BX]
    ABCD1:
        ..

  5,下面是时间的损失:
        PUSH    DI
        MOV     CX,BX
        REP     STO注意用词!
        POP     DI
        PUSH,POP 很费时间,应为:
        MOV     CX,BX
        REP     STO注意用词!
        SUB     DI,BX
        同理,很多时候稍稍想一下,就可省下一些指令:
        PUSH    CX
        REP     MOV注意用词!
        POP     CX
        SUB     DX,CX
    为什么不干脆些?
        SUB     DX,CX
        REP     MOV注意用词!

  6,有段程式,很有规律,但却极无效率:
    X1:
        TEST    AH,1
        JZ      X2
        MOV     BUF1,BL
    X2:
        TEST    AH,2
        JZ      X3
        MOV     BUF2,DX     ; 凡双数用DX,单数用BL
    X3:
        TEST    AH,4
        JZ      X4
        MOV     BUF3,BL
    X4:
        ..                  ; 以下各段与上述程式相似
    X8:
        ..
        这种金玉其表的程式,最没有实用价值,改的方法应由缓冲器着手,先安排成序列,由小而大如:
        BUF1    DB  ?
        BUF2    DW  ?
        BUF3    DB  ?
        BUF4    DW  ?
        ..
    然后,程式改为:
        MOV     DI,OFFSET BUF1      ; 第一个缓冲器
        MOV     AL,BL
        MOV     CX,4        
    X1:
        SHR     AH,1
        JZ      X2
        STO注意用词!
    X2:
        SHR     AH,1
        JZ      X3
        MOV     [DI],DX
        INC     DI
        INC     DI
    X3:
        LOOP    X1

  7,回路最怕千回百转,不畅不顺,如:
        SUB     AH,AH
    ABCD:
        CMP     AL,BL
        JB      ABCD1
        SUB     AL,BL
        INC     AH
        JMP     ABCD
    ABCD1:
        ..
      以上 ABCD1这个入口是多余的,下面就好得多:
        MOV     AH,-1
    ABCD:
        INC     AH
        SUB     AL,BL
        JA      ABCD
        ADD     AL,BL       ; 还原
        ..

  8,当处理字码时,需要字母的序数,有这样的写法:
        CMP     AL,60H  
        JA      ABCD1 
        SUB     AL,40H      ; 大写字母
    ABCD:
        ..
    ABCD1:
        SUB     AL,60H      ; 小写字母
        JMP     ABCD
        要知道字母码的特色在于大写为 40H 至4AH,小写为60H 至6AH ,以上程式,其实只要一个指令就可

以了:
        AND     AL,1FH
    简单明瞭!

  9,大多数的程式在程式师自己测试下很少发生错误,而一旦换一另个人执,就会发现错误百出。 其原因在于写程式者已经假定了正确的情况,当然不会以明知为错误的方式操作。可是换了一个人,没有先入为主的成见,很可能输入了「不正确」的资料,结果是问题丛生。  要知道真正的使用者,绝非设计者本人,在操作过程中,按键错误在所难免。这种错误应该在程式中事先加以检查,凡是输入资料有「正确、错误」之别者,错误性资料一定要事先加以排除。 这样做看起来似乎程式不够精简,可是正确的重要性远在精简之上。一旦发生了错误,再精简的程式也没有使 用价值。 此外,在程式中常有加、减的运算,这时也应该作正确性检查,否则会发生上述同样的问题。

三、指令应用要灵活

    有一段很简单的程式,其写作的方法甚多,但是指令应用的良窳,会使得程式的效率相去天上地下,难以估计。
    这段程式的用途,是要将一段资料中,英文字符大、小写相互转换。当然,转换的选择要由使用者决定,在下面程式且略去使用介面,假设已得知转换的方式。
    设资料在 DS:SI中,资料长度=CX ,大写转小写时BL=0,反之,则BL=1。
    我见过一种写法,简直无法原谅:
    1OOP1:
    2:        CALL    CHANGE
    3:        JC    LOOP11
    4:        ADD    AL,20H
    5:        JMP    SHORT LOOP12
    6OOP11:
    7:        SUB    AL,20H
    8OOP12:
    9:        MOV    [SI-1],AL
   10:        LOOP    LOOP1
   11:        RET
   12: CHANGE:
   13:        LOD注意用词!
   14:        OR    BL,BL
   15:        JZ    CHANGS
   16:        CMP    AL,61H
   17:        JB    CHARET
   18:        CMP    AL,7AH
   19:        JA    CHARET
   20:        STC
   21: CHARET:
   22:        RET
   23: CHANGS:
   24:        CMP    AL,41H
   25:        JB    CHARET
   26:        CMP    AL,5AH
   27:        JA    CHARET
   28:        CLC
   29:        RET
    这种程式错在把由12到29的程式写得太长,共 25B,有共用的价值,于是作为子程式调用。
    试想一下,每一笔资料,都要调用一次,浪费四个字元事小,但每次要费 23+20个时钟脉冲,资料多时,不啻为天文数字。更何况这段程式写得极差,在回路中,又多浪费了几十个时钟。关于这一点,下面会继续讨论。
    照上面这段程式,略加改进,写法如下:
    1: CHANGE:
    2:        LOD注意用词!
    3:        OR    BL,BL
    4:        JZ    CHANGS
    5:        CMP    AL,61H
    6:        JB    CHARET
    7:        CMP    AL,7AH
    8:        JA    CHARET
    9:        SUB    AL,20H
   10: CHANG0:
   11:        MOV    [SI-1],AL
   12: CHANG1:
   13:        LOOP    CHANGE
   14:     RET
   15: CHANGS:
   16:        CMP    AL,41H
   17:        JB    CHANG1
   18:        CMP    AL,5AH
   19:        JA    CHANG1
   20:        ADD    AL,20H
   21:        JMP    CHANG1
    这样的写法还是不佳,因为在回路中,用常数与暂存器比较,速度较暂存器相比为慢。应该先将需要比较的值,放在暂存器DH,DL 中,改进如次:
    1:        MOV    AH,20H
    2:        MOV    DX,7A61H
    3:        OR    BL,BL
    4:        JZ    CHANGE
    5:        MOV    DX,5A41H
    6: CHANGE:
    7:        LOD注意用词!
    8:        CMP    AL,DL
    9:        JB    CHANG1
   10:        CMP    AL,DH
   11:        JA    CHANG1
   12:        XOR    AL,AH
   13:        MOV    [SI-1],AL
   14: CHANG1:
   15:        LOOP    CHANGE
   16:     RET
    以上这段程式,空间小,速度快,每笔资料,平均仅需不到40个时钟值,以10 MHZ计,十万笔资料,约需半秒钟!
请注意程式中所用的技巧,由2至6的分支法,就比下面这种写法为佳:
    1:        OR    BL,BL
    2:        JZ    CHAN1 
    3:        MOV    DX,5A41H
    4:      JMP    SHORT CHANGE
    5: CHAN1:
    6:        MOV    DX,7A61H
    7: CHANGE:
    这种分支也可以由另一种技巧所取代,即预设法。事先将所需用的参数放在固定的缓冲区中,此时取用即可:
           MOV  DX,BWCOM   ; 比较之预设值 
    这样程式又简单些了:
    1:       MOV    AH,20H
    2:        MOV    DX,BWCOM
    3: CHANGE:
    4:        LOD注意用词!
    5:        CMP    AL,DL
    6:        JB    CHANG1
    7:        CMP    AL,DH
    8:        JA    CHANG1
    9:        XOR    AL,AH
   10:        MOV    [SI-1],AL
   11: CHANG1:
   12:        LOOP    CHANGE
   13:     RET

    以上介绍为变数法技巧,即将所要比较的值,放在暂存器中。由于暂存器快速、节省空间,因此程式效率高。更重要的一点,是程式本身的弹性大,只要应用方式统一,事先把参数设妥,即可共用。

回路中的指令

    回路最重要的是速度,因为本段程式,将在计数器的范围之内,连续执行下去。如果不小心浪费了几个时钟值,在回路的累积下,很可能使程式成为牛步。
    要想把回路写好,一定要记清楚每个指令的执行时钟,以便选择效率最高者。同时,要知道哪些指令可以获得相同的处理效果,才能有更多的选择。
    其次,在回路中,最忌讳用缓冲器,不仅占用空间大,处理速度慢,而且不能灵活运用,功能有限。另外也应极力避免常数,尽量设法经由暂存器执行,用得巧妙时,常会将整个程式的效率提高百十倍。
    还有便是少用 PUSH,POP,DIV,MUL和 CALL 等浪费时钟的指令。除此之外,小心、谨慎,深思、熟虑,才是把回路写好的不二法门。
    在前例中,把比较常数的指令换为比较暂存器,便是很好的证明。如果用常数,两段程式决不可能共用,时、空都无谓地浪费了。
    以下再举数例,乍看这似乎有些吹毛求疵,但是仔细计算一下所浪费的时间,可能就笑不出声了。
兹假定以下回路需处理五万字元的资料,频率为 10MHZ,其情况为:
    1OOP1:
    2:          LOD注意用词!
    3:        XOR    AL,[DI]
    4:        STO注意用词!
    5:        LOOP    LOOP1
    本程式计数器等于50,000,每次需
    12T+14T+11T+17T=55T 个时钟脉冲
若以50,000次计,需时 47*50,000/10,000,000 秒,即约四分之一秒。
    只要稍稍将指令调整一下,为:
    1OOP1:
    2:             LODSW
    3:        XOR    AX,[DI]
    4:        STOSW
    5:        LOOP    LOOP1
    这样计数器只要25,000次,每次
    16T+18T+15T+17T=66T
    则25,000次需时 66*25,000/10,000,000 秒,约六分之一秒,比前面的程式快了二分之一。
    同理,在回路中加回路,而每个回路需 17T,也是很大的浪费。倘若加调用 CALL 指令,则需 23T+20T=43T,浪费得更多,读者不可不慎。
    当某一段程式用得很频繁时,理应视作子程式,例如下面的 LODAX:
    1OOP1:
    2:        CALL    LODAX
    3:        LOOP    LOOP1
    4:        RET
    5ODAX:
    6:        LODSW
    7:        XOR    AX,[DI]
    8:        STOSW
    9:        RET
    其实这是贪小失大,仅四个字元的程式,竟用三个字元的调用指令去交换,是绝对得不偿失的。
    再如同下面的程式,颇有值得商榷之处。
    1OOP1:
    2:        MOV    DX,NUMBER1
    3:        MOV    CX,NUMBER2
    4:    LOOP2:
    5:        PUSH    CX
    6:        MOV    CX,DX
    7OOP3:
    8:        LODSW
    9:        XOR    AX,[DI]
   10:        STOSW
   11:        LOOP    LOOP3
   12:        INC     DI
   13:        INC     DI
   14:        POP    CX
   15:        LOOP    LOOP2
   16:        RET
    第二个回路是多余的,这是高阶语言常用的观念,对组合语言完全不适用。
    稍加改动,不损上面程式原有的条件,得到:
    1OOP1:
    2:        MOV    DX,NUMBER1
    3OOP2:
    4:        MOV    CX,NUMBER2
    5OOP3:
    6:        LODSW
    7:        XOR    AX,[DI]
    8:        STOSW
    9:        LOOP    LOOP3
   10:        INC     DI
   11:        INC     DI
   12:        DEC     DX
   13:        JNZ    LOOP2
   14:        RET
这样回路少了一个,程式中将5,6,14,15 各条中原来为15T+2T+12T+17T=46T的时间,省为12,13,14条的

2T+16T+17T=35T。

分支处理

    比较资料后,作条件分支 (Conditional Jump ),是程式中不可避免的手续。程式一长,分支距离超过 128个字元,条件分支就无法到达。当然,精简程式有时可以避免这种情形,但却不尽然。
    处理条件分支的技术很多,其效率端视情况而定。最要紧的是事先规划,要比较些什么?在何种情况下?分支到哪里?做些什么工作等等。
    不仅是写程式,人的各种能力,都可以由工作的方式判断出来。智慧高的人,很快就能抓住重点,再分门别类,钜细无遗的理出完整的系统。经过良好训练的专家,则能根据一套法规,逐步地整理归纳,也能推出合情合理的结果来。
    老实说,电脑程式的写作技术还没有到成熟的阶段,当今所有的从业人员,都只能算是「拓荒者」,并没有真正的「专家学者」。充其量,像我个人一样,比别人机会好些,天天得以与电脑为伍,多一点经验而已。
    因此,目前写程式几乎可以说没有可资遵循的法规,海阔天空,爱怎样写,就怎样写,只要能够使用,程式卖得出去,赚了大钱,就会被人视为大师。
    只是这种情况维持不了多久了,初民的壁画,仅具有历史意义。今天的程式师,如果不认清现实,立刻觉醒,多致力于法规的制定,电脑将永远是个不成熟的孩子。一旦这些法规经得住考验,为未来的专家学者奠定基础,那才能真正的被视为大师。 我不讳言我们正朝着这个方向努力,但是,我却不认为做得到。因为电脑的硬体设计在今后的十年内,必然会有重大的突破,谁都难以预测会有什么结果。软体的制作观念虽然不可能有很大的改变,却难免会受到影响。只有各位年轻朋友,你们成长在电脑时代,肯多一分耕耘,必有收获!
    下面,且介绍一些我对条件分支的处理技巧:

一、资料的分类

  1,位元分类:
        在本书第四章第五节所举的,由输入码作为输出字形的处理依据之例,就是采用位元分类的例证。
        但凡以资料位元作为共同的分类讯息,而且各类皆有独特的处理方式者,皆应以其位元为顺序,用间

接定址或分支技巧,作为程式处理之手段。

  2,字元分类:
        每一个字元具有 256种排列组合,设若有 128种以内的分类项目,应该取双数分类,否则须用连续分类。
        分类之值,立即可以用间接定址执行。但须注意,各分类的入口标题应先行定义。由于定义必须用到双字元,所以,凡采用连续分类者,其值应乘二。

  3,间隔分类:
        在有些情况下,原有资料不容许重新安排,而且其中若干资料已具备分类之特性,这种情况,我们称之为间隔分类。
        在处理此类资料时,应该先将可以作分类处理的资料提取出来,并视为字串,定义在一缓冲区内。当须要类比时,可利用「比对字串」 (SCAS) 的指令以求得其定义位置,再作间接定址。设有
        4700H,4900H,4F00H,5100H,4A2DH,4EABH
    等键盘输入数据。设上述值在AX中,需要作特殊处理,分别进入COD1至COD6等子程式。
    11将资料定义在缓冲器 ABC中,程式则定义在DEF:
      ABC    DW   4700H,4900H,4F00H,5100H,4A2DH,4E2BH
      DEF    DW   COD1,COD2,COD3,COD4,COD5,COD6
    12使DI=ABC,CX=6:
        MOV     DI,OFFSET ABC
        MOV     CX,6
    13由比对字串后,判断是否AX中有上述之值,如有,则用间接定址的方式执行之。
        REPNZ   SCASW               ; 比对六组字串
        JCXZ    NOTHING             ; 没有所比之字串
        SUB     DI,OFFSET ABC+2     ; 得到比对位置值
        CALL    CSEF[DI]          ; 或作JMP
          上述之DEF 如果放在DG段中,还可以节省一字元,并可加快速度:
        CALL    DEF[DI]

二、程式的结构

    若在程式规划之初,未先做好准备工作,临时想用前述的方法,并非绝不可能。但是,东添一点,西补一段,这种程式不仅会导致测试的麻烦,更可能影响未来的维护和调整。
    因此,每当瞭解了工作任务后,需要作间接定址的部份,最好能集中在一个模组内。万一性质不同必须分割,也应该将间接定址的程式,置放在模组的起头处。
    这样做的好处很多,一方面便于扩充功能,每次增加定址因素时,不必在程式中寻来找去,立刻可以安排妥当。其次,这种定址的需求,必然与整体功能有关,而且定义表相当于一个目录,把纲领放在前面,按图索骥,一目瞭然。更重要的,是可以表现出程式结构的层次,层次处理是网状流程中最难以掌握的一环,不可不慎。
    还有,就是各子程式的标题安排,其位置的先后应以功能的集中性为准。这样做的好处是,如果有可以共用的程式段,很容易就可合并为一,节省空间。

三、次序与条件「真」「假」

    条件分支的「时钟数」有二个可能,条件符合时,执行分支为 16T,不符合则为 4T ,且继续下一指令。两者相差有四倍之多,我们正该利用这一特点,速度重要的条件,都应该设为主流程,否则为分流程。  尤其是在需要高速的回路中,分支处理得好坏,效率相去甚远。这种分支需要平时多加小心,培养出良好的习惯。
    CDEF: 
        CMP    AL,’?’
        JZ     ABCD       ; 各比较符号中,’?’ 者最少
        LOOP   CDEF       ; NZ条件仅需4T速度较快
    ABCD: 
        ..

四、JMP 与 JMP SHORT

    当程式师专心写作或侦错之时,常无法瞻前顾后。然而侦错完毕程式无误时,最好彻底检查一下所有的JMP 指令,经常会大有斩获!
    因JMP 需要三字元,而JMP SHORT 只要两个,其条件是所跳越的位址不能超过128 字元。
    在程式编译时,若向上JMP 的距离在 128字元以内,编译器会自动译为两字元。往下则不然,如在128 字元内,会再多加一个 NOP指令,不仅浪费一字元且多了两个时钟。
    因此,细心检查一下,凡是向下跳,在128 字元以内,皆应改为JMP SHORT 才是。

编程规范-程序员们都应该这样写代码基本要求 2005-5-11 01:56 AM
【楼主】
编程规范-程序员们都应该这样写代码基本要求

1.1 程序结构清析,简单易懂,单个函数的程序行数不得超过100行。
1.2 打算干什么,要简单,直接了当,代码精简,避免垃圾程序。
1.3 尽量使用标准库函数和公共函数。
1.4 不要随意定义全局变量,尽量使用局部变量。
1.5 使用括号以避免二义性。

2.可读性要求
2.1 可读性第一,效率第二。
2.2 保持注释与代码完全一致。
2.3 每个源程序文件,都有文件头说明,说明规格见规范。
2.4 每个函数,都有函数头说明,说明规格见规范。
2.5 主要变量(结构、联合、类或对象)定义或引用时,注释能反映其含义。
2.7 常量定义(DEFINE)有相应说明。
2.8 处理过程的每个阶段都有相关注释说明。
2.9 在典型算法前都有注释。
2.10 利用缩进来显示程序的逻辑结构,缩进量一致并以Tab键为单位,定义Tab为 6个字节

2.11 循环、分支层次不要超过五层。
2.12 注释可以与语句在同一行,也可以在上行。
2.13 空行和空白字符也是一种特殊注释。
2.14 一目了然的语句不加注释。
2.15 注释的作用范围可以为:定义、引用、条件分支以及一段代码。
2.16 注释行数(不包括程序头和函数头说明部份)应占总行数的 1/5 到 1/3 。

3. 结构化要求

3.1 禁止出现两条等价的支路。
3.2 禁止GOTO语句。
3.3 用 IF 语句来强调只执行两组语句中的一组。禁止 ELSE GOTO 和 ELSE RETURN。
3.4 用 CASE 实现多路分支。
3.5 避免从循环引出多个出口。
3.6 函数只有一个出口。
3.7 不使用条件赋值语句。
3.8 避免不必要的分支。
3.9 不要轻易用条件分支去替换逻辑表达式。

4. 正确性与容错性要求

4.1 程序首先是正确,其次是优美
4.2 无法证明你的程序没有错误,因此在编写完一段程序后,应先回头检查。
4.3 改一个错误时可能产生新的错误,因此在修改前首先考虑对其它程序的影响。
4.4 所有变量在调用前必须被初始化。
4.5 对所有的用户输入,必须进行合法性检查。
4.6 不要比较浮点数的相等,
如: 10.0 * 0.1 == 1.0 , 不可靠
4.7 程序与环境或状态发生关系时,必须主动去处理发生的意外事件,如文件能否逻辑锁定、打印机是否联机等。
4.8 单元测试也是编程的一部份,提交联调测试的程序必须通过单元测试。

5. 可重用性要求

5.1 重复使用的完成相对独立功能的算法或代码应抽象为公共控件或类。
5.2 公共控件或类应考虑OO思想,减少外界联系,考虑独立性或封装性。
5.3 公共控件或类应建立使用模板。


.1适用范围

本标准适用于利用Visul C++ ,Borland C++进行软件程序开发的人员.。

.2变量命名

命名必须具有一定的实际意义,形式为xAbcFgh,x由变量类型确定,Abc、Fgh表示连续意义字符串,如果连续意义字符串仅两个,可都大写.如OK.

具体例程:

BOOL类型 bEnable;

ch * char chText
c * 类对象 cMain(对象实例)
h * Handle(句柄) hWnd
i * int
n * 无符号整型
p * 指针
sz,str * 字符串
w WORD
x,y 坐标

Char或者TCHAR类型 与Windows API有直接联系的用szAppName[10]形式否则用FileName[10]形式,单个字符也可用小写字母表示;

Int类型 nCmdShow;
LONG类型 lParam;
UINT类型 uNotify;
DWORD类型 dwStart;
PSTR类型 pszTip;
LPSTR类型 lpCmdLine
LPTSTR类型 lpszClassName;
LPVOID类型 lpReserved
WPARAM类型 wParam,
LPARAM类型 lParam
HWND类型 hDlg;
HDC类型 hDC;
HINSTANCE类型 hInstance
HANDLE类型 hInstance,
HICON类型 hIcon;
int iTmp
float fTmp
DWORD dw*
String , AnsiString str *
m_ 类成员变量 m_nVal, m_bFlag
g_ 全局变量 g_nMsg, g_bFlag

局部变量中可采用如下几个通用变量:nTemp,nResult,I,J(一般用于循环变量)。

其他资源句柄同上

.3常量命名和宏定义

常量和宏定义必须具有一定的实际意义;
常量和宏定义在#include和函数定义之间;
常量和宏定义必须全部以大写字母来撰写,中间可根据意义的连续性用下划线连接,每一条定义的右侧必须有一简单的注释,说明其作用;
资源名字定义格式:

菜单:IDM_XX或者CM_XX
位图:IDB_XX
对话框:IDD_XX
字符串:IDS_XX
DLGINITIALOG_XX
ICON:IDR_XX

.4函数命名

函数原型说明包括引用外来函数及内部函数,外部引用必须在右侧注明函数来源: 模块名及文件名, 如是内部函数,只要注释其定义文件名;

第一个字母必须使用大写字母,要求用大小写字母组合规范函数命名,必要时可用下划线间隔,示例如下:

void updateDB_Tfgd (TRACK_NAME); //Module Name :r01/sdw.c
void PrintTrackData (TRACK_NAME); //Module Name :r04/tern.c
void ImportantPoint (void); //Module Name :r01/sdw.c
void ShowChar (int , int , chtype); //Local Module
void ScrollUp_V (int , int); //Local Module

.5结构体命名

结构体类型命名必须全部用大写字母,原则上前面以下划线开始;结构体变量命名必须用大小写字母组合,第一个字母必须使用大写字母,必要时可
用下划线间隔。对于私有数据区,必须注明其所属的进程。全局数据定义只需注意其用途。

示例如下:

typedef struct
{
char szProductName[20];
char szAuthor[20];
char szReleaseDate[16];
char szVersion[10];
unsigned long MaxTables;
unsigned long UsedTables;
}DBS_DATABASE;

DBS_DATABASE GdataBase;

6 控件的命名:
用小写前缀表示类别:
fm 窗口
cmd 按钮
cob combo,下拉式列表框
txt 文本输入框
lab labal,标签
img image,图象
pic picture
grd Grid,网格
scr 滚动条
lst 列表框
frm fram

7注释

原则上注释要求使用中文;

文件开始注释内容包括:公司名称、版权、作者名称、时间、模块用途、背景介绍等,复杂的算法需要加上流程说明;
函数注释包括:输入、输出、函数描述、流程处理、全局变量、调用样例等,复杂的函数需要加上变量用途说明;
程序中注释包括:修改时间和作者、方便理解的注释等;

引用一: 文件开头的注释模板

/******************************************************************
** 文件名:
** Copyright (c) 1998-1999 *********公司技术开发部
** 创建人:
** 日 期:
** 修改人:
** 日 期:
** 描 述:
**
** 版 本:
**—————————————————————————–
******************************************************************/

引用二: 函数开头的注释模板
/*****************************************************************
** 函数名:
** 输 入: a,b,c
** a—
** b—
** c—
** 输 出: x—
** x 为 1, 表示…
** x 为 0, 表示…
** 功能描述:
** 全局变量:
** 调用模块:
** 作 者:
** 日 期:
** 修 改:
** 日 期:
** 版本
****************************************************************/

引用三: 程序中的注释模板
/*———————————————————-*/
/* 注释内容 */
/*———————————————————-*/

8 程序

a. 程序编码力求简洁,结构清晰,避免太多的分支结构及太过于技巧性的程序,尽量不采用递归模式。
b. 编写程序时,亦必须想好测试的方法,换句话说,"单元测试" 的测试方案应在程序编写时一并拟好。
c. 注释一定要与程序一致。
d. 版本封存以后的修改一定要将老语句用/* */ 封闭,不能自行删除或修改,并要在文件及函数的修改记录中加以记录。
e. 程序中每个block 的开头 "{" 及 "}" 必须对齐,嵌套的block 每进一套,缩进一个tab,TAB 为4个空格,block类型包括if、for、while、do等关键字引出
的。
f. 对于比较大的函数,每个block 和特殊的函数调用,都必须注明其功能,举例如下:

count.divisor = 1193280 / freq; // compute the proper count
OutByte((unsigned short)67, (unsigned char)182); // tell 8253 that acount is coming
OutByte((unsigned short)66, count. c[0]); // send low-order byte
OutByte((unsigned short)66, count. c[1]); // send high-order byte
×××××××××××××××××××××××××××××××××××××××

bcb,delphi中的变量命名:

遵循匈牙利命名法,命名必须有意义,制定如下规定

窗体: 以大写的W开始,如About版权窗体, 命名为WAbout
文件:以大写的F开始,如About版权窗体,文件命名为FAbout.cpp
按钮(Button):如退出按钮,命名为btnExit
……
基类: 加base标记,如报表基类,窗体命名为:WBaseRep, 文件命名为FBaseRep.cpp

  a       Array                                 数组
  b       BOOL (int)                            布尔(整数)
  by     Unsigned Char (Byte)                  无符号字符(字节)
  c       Char                                  字符(字节)
  cb     Count of bytes                        字节数
  cr     Color reference value                 颜色(参考)值
  cx     Count of x (Short)                    x的集合(短整数)
  dw    DWORD   (unsigned long)                 双字(无符号长整数)
   f       Flags   (usually multiple bit values)   标志(一般是有多位的数值)
   fn      Function                              函数
   g_      global                                全局的
   h       Handle                                句柄
   i       Integer                               整数
   l       Long                                  长整数
   lp      Long pointer                          长指针
   m_      Data member of a class                一个类的数据成员
   n       Short int                             短整数
   p       Pointer                               指针
   s       String                                字符串
   sz      Zero terminated String                以0结尾的字符串
   tm      Text metric                           文本规则
   u       Unsigned int                          无符号整数
   ul      Unsigned long (ULONG)                 无符号长整数
   w       WORD (unsigned short)                 无符号短整数
   x,y     x, y coordinates (short)              坐标值/短整数
   v       void                                  空

2005年05月22日


——————————————————————————–
《大腕》经典对白

一定得选最好的黄金地段
雇法国设计师
建就得建最高档次的公寓
电梯直接入户
户型最小也得四百平米
什么宽带呀,光缆呀,卫星呀
能给他接的全给他接上
楼上边有花园(儿),楼里边有游泳池
楼子里站一个英国管家
戴假发,特绅士的那种
业主一进门(儿),甭管有事(儿)没事(儿)都得跟人家说
may i help you sir (我能为您作点什么吗?)
一口地道的英国伦敦腔(儿)
倍(儿)有面子
社区里再建一所贵族学校
教材用哈佛的
一年光学费就得几万美金
再建一所美国诊所(儿)
二十四小时候诊
就是一个字(儿)——贵
看感冒就得花个万八千的
周围的邻居不是开宝马就是开奔驰
你要是开一日本车呀
你都不好意思跟人家打招呼
你说这样的公寓,一平米你得卖多少钱
我觉得怎么着也得两千美金吧
两千美金 那是成本
四千美金起
你别嫌贵 还不打折
你得研究业主的购物心理
愿意掏两千美金买房的业主
根本不在乎再多掏两千
什么叫成功人士 你知道吗?
成功人士就是买什么东西
都买最贵的 不买最好的
所以,我们做房地产的口号(儿)就是
不求最好 但求最贵

——————————————————————————–

《大腕》经典对白之—-网虫版

一定得选最快的ISP,
租条最快的线路,
聊天得聊得象个模样,
光缆直接到床头,
带宽最小也得10个G,
什么卧室呀,厨房呀,厕所呀,
能给他接的全给他接上,
厕所里搁一笔记本,墙上再挂一100寸的等离子显示器,
边上再放一个音响,
5+1,带数码的那种,
你一进厕所,甭管有事没事,都得蹲一下,顺便上QQ聊上几句,
Hi,mm,I miss u
一副地道的痞子腔(儿),
倍(儿)有面子,
实在闲得无聊,想晃点网友的话,
再在电信建个网站,
服务器用386的,
重新启动一次得花个把礼拜,
再自己做个网页,
一年没一个人进去看的那种,
就是一个字—–慢,
显示个首页都得就得花个万八千小时的,
浏览过这个网站的,不是跳楼的就是抹脖子的,
你要是自然死亡呀,
你都不好意思开追悼会,
你说这样的网站,一年能害死多少人?
我觉得怎么着也得两千吧,
两千?那是零头,
一万两千起,
你别嫌死的人多,还真有那么一帮不怕死的,
你得了解上网人的心理,
愿意上网的主,
他根本不在乎多慢;
什么叫网虫你知道吗?
网虫就是甭管什么网,
只要是网都想上去看看,不管鱼网还是电网;
所以,我们建网站的宗旨就是
不求最爽,但求最慢!

——————————————————————————–

大腕》找老婆版

  一定得选最有身份的
  怎么也得美国正经学校硕士毕业
  找就找罪漂亮最有身段儿的
  在学校就是校花
  身高最少也得1米65
  什么钢琴呀,跳舞呀,卡拉OK呀
  一般人能会的全会
  还特有脾气
  动不动就象训三孙子一样训你一把
  要找这么一主儿
  你得早上5:00起床准备早餐,晚上还得早早回来做饭
  做好饭就站楼门口
  匝围裙,特恭敬那种
  等她一进门(儿),甭管有事(儿)没事(儿)都得跟人家说
  may i help you dear (我能为您作点什么吗?)
  一口地道的英国伦敦腔(儿)
  倍(儿)有面子
  有事没事的就出去玩
  住总统套房
  一年光住HOTEL就几万美金
  老婆这排场那应酬的
  一天二十四小时花钱
  就是一个字(儿) 贵
  光香水儿就得花个万八千的
  现在这行市你不这么着你被人瞧不起
  周围的邻居不是泡影星就是彪歌星
  你要是找一日本妞呀
  你都不好意思跟人家打招呼
  你说这样的老婆,一天你得达多少钱
  我觉得怎么着也得两千美金吧
  两千美金 那是结婚十年后
  四千美金起
  你别嫌贵 还不打折
  你得研究单身汉的择偶心理
  愿意一天掏两千美金供老婆的主
  根本不在乎再多掏两千
  什么叫犯贱 你知道吗?
  就是找老婆时
  都找最难养的 不找最好养的
——————————————————————————–

《大腕》(考研版)

  一定得报北大清华
  找最热门的系
  考就考mm听了抛媚眼的专业
  工作干脆甭打算找
  每天复习时间最少也得二十四小时
  什么毕设呀,实习呀,gf呀
  能糊弄的全给它糊弄过去
  枕头底下掖着张锦芯,马桶边上摆着任汝芬
  手里捏着根签字笔
  三块钱,博实买的那种
  但凡碰见能写字的地方,有空就得练作文
  There’’s no denying the fact that…
  一口地道的朱太祺腔儿
  倍儿有面子
  包里再揣十沓笔记
  一定得找出题老师讲的
  光复印费就得几百
  再订一本专业课试题
  v打有狗那年就有的卷子都搜罗来
  就是一个字儿,全
  英语阅读是重点
  周围的傻缺不是错三个就是错四个
  你要是错上五个呀
  你都不好意思跟人家说你考研
  你说这样的准备,到头你得考多少分?
  我觉得怎么着也得三百五吧?
  三百五?那是瞎忙活
  四百分起
  你还别嫌高这才刚够复试
  你得研究如今的考研形势
  能逼着你考出三百五的专业
  根本不在乎再多折腾你五十
  什么叫成功人士你知道吗?
  成功人士就是不管答哪道题
  都答最准的不答最对的
  所以,我们考研的口号儿就是
  不求最对但求最准
——————————————————————————–

PDA版

一定要选最新的操作系统
安一个全钛金属外壳
作就要作最贵的掌上电脑
机器里外全是接口 
怎么也得十来个吧
什么红外,蓝牙,记忆棒呀 
能安的全都给安上
CPU怎么也得是P4级别的 还得是inter专门为咱们设计的
每次开机的时候,7寸彩屏上蹦出一个美国小子
穿夹克 戴个近视镜的那个
不管你点那儿,有事没事他就给你来一句
WELLCOME TO MICROSOFT
真正的三维环绕立体声
倍儿有面子
配上两根纯金的多功能触笔
还能当手电筒用
光这根笔的设计费就得几万美金
再安上无线宽带100M网卡
二十四小时随地上网
网费就是一个字儿--贵!
发条短信就得十块八块的
你要还是用CF猫电话上网
都没法在QQ上给人聊天
你说这种掌上电脑一台得买多少钱?
“我觉的得怎么着也得五千吧?”
五千?!那是成本!
标配一万,全配一万八
你还别嫌贵,还不打折
你得研究蜥蜴迷的心理
什么叫真正的蜥蜴迷你知道吗?
真正的晰蜴发烧友是机器越贵,越要攒钱去买! 
“为什么呀?”
废什么话呀,你知道联想天玑为什么火不了吗?!
所以,我们作PDA的厂家的口号就是
不求产品最好,但求产品最贵

——————————————————————————–

大腕》之泡妞版


一定得选最漂亮的女孩
特有气质的那种
泡就泡最骄傲最矜持的女孩
情书直接送到她手中
字数最少也得比资本论多八九倍
什么雀巢啊,德芙啊,梦特娇啊
能送的都给她送过去
早上递热牛奶,晚上奉鲜橙汁
包里还藏着一支玫瑰
带着水滴,倍儿精神的那种
每天堵着她
甭管她身边有人没人都得说:
“见君日短,思君日长”
一口地道的巴黎流氓腔
倍儿动人  
她的生日再开个烛光晚宴
酒水用外卖的
光小费就得给百八十块
再作完全陶醉状
二十四小时都跟着她
就是一个字,爱
眨个眼都得看她几百回
来得人不是傍着马子就是跨着小蜜的
你要是带个同学来你都不好意思跟人家打招呼
你说这样的泡妞法
一年能泡多少个吧
我觉得怎么也得六七个吧
六七个!那是灾年
起码十二个 ,
你别嫌多,还不撞车
你得研究咱们的素质程度
真正博爱的
根本不在乎一月换一个
什么叫爱情杀手你知道吗?
爱情杀手就是泡什么妞都只泡最好的,不泡好泡的
所以我们泡妞的口号是:
不求好泡,但泡最好!

——————————————————————————–

大腕儿》电脑版
  一定得选最好的CPU
  (我说的可是美金呀)
  雇法国设计师
  做就得做最高档次的外壳儿
  电源直接入户
  机型最轻的也得四十千克
  什么珍珠呀,翡翠呀,钻石呀
  能给他镶的全都给他镶上
  壳儿上面有白金, 壳儿里面有玛瑙石
  BIOS里加一段汇编程序
  看不懂 特别长的那种
  用户一开机,甭管爱听不爱听都得跟人家说
  “嘎哈玩应,有啥事妖?”
  一口地到的辽宁锦州咸菜味儿
  倍儿有面子
  机子里在加一道真空夹层
  材料用NASA的
  一年光维护费就得几万美金
  再建一所备用电站
  二十四小时的候着
  就是一个字儿—贵
  拆个箱的就得花个万八千的
  周围的邻居不是用索尼就是配爱娃
  你要是接一台湾音箱呀
  你都不好意思跟人家打招呼
  你说就这样的机器 一台你得卖多少钱呀?
  (我觉得怎么着也得两万美金吧)
  两万美金! 那是成本
  四万美金起!
  你还别嫌贵 还不打折
  你的研究客户的购机心理
  愿意掏两万买机子的客户 根本不在乎再多掏两万
  什么叫土人你知道吗?
  土人就是买什么东西都买最贵的 不买最好的!
  所以 我们做SELLS的口号就是
  不求最好 但求最贵

——————————————————————————–

大腕》宿舍版
  一定得选最高的宿舍楼,
  雇自己的设计师,
  建就建最便宜的宿舍楼,
  六层直接到顶,
  户型最大也就就五,六平米,
  什么网线啊,电话啊,电视啊,
  能接的都不给他们接上。
  楼上有石棉瓦,
  楼里有臭厕所,
  楼里再站一胖楼长,
  带眼镜特没文化的那种,
  学生一近门,
  甭管有事没事都得跟人家说:你哪的,干吗来了,
  一口地道的河南农村腔,
  甭没文化,学校里再建个高档餐厅,
  菜用早市的,光吃米饭就得五,六十块。
  再建一个美国酒吧,
  24小时开着,就是一个字,贵。
  喝个饮料就得花个二,三十的。
  周围的同学不是拿8850,就是拿8088,
  你要是拿个3310都不好意思跟人家打招呼!
  你说这样的宿舍一年得收多少钱?
  
  我觉着怎么也得两千人民币吧……
  
  两千人民币!!!!
  那是成本2千美金起!
  你还别嫌贵,
  还不打折,
  你得研究学生的住宿心理,
  愿意掏两千人民币住校内的学生根本就掏不起七千住校外的。
  什么叫nb学生你知道吗?
  nb学生就是什么时候都只住校外的,不住校内的!
  所以我们建宿舍的口号是:不求nb,但求一般。


——————————————————————————–


大腕》之泼硫酸版
  一定得是动物园嗅觉最灵敏的畜生
    还得是珍贵野兽
    害,就来最厉害的手段
    直接用硫酸
    让黑熊痛苦起来感觉特惨
    什么黑熊呀,马熊呀,氢氧化钠呀
    能来的全给这些动物来一遍
    目标事先筛选,行动干净利索
    在动物园先看好撤退路线
    特职业,特麻溜的那种
    一旦被逮着,甭管是警察还是群众都得跟人家说
    I’m the student of Tsinghua……
    一口地道的北京乡下腔(儿)
    倍(儿)有派头
    说话还得打几个磕巴
    光得瑟就老半天
    再说些痛苦后悔的话
    就是一个字(儿) ——悔
    周围的人不是骂他畜生的就是骂他没人性的
    你要是说他神经有问题
    你都不好意思跟人家打招呼
    你说这样的学生,北京一年往清华送多少?
    我觉得怎么着也得二三十个吧
    二三十个 那是少说
    最少一倍
    你别嫌多, 还没算后门
    你得研究领导的选人心理
    愿意选北京学生的领导
    根本不在乎他们会不会把动物都放倒
    什么叫择优录取 你知道吗?
    择优录取就是要什么学生
    都只要最黑的 不要最好的
    所以,我们录取的口号(儿)就是
    不求最好 但求最黑

——————————————————————————–


中国向日本扔原子弹之《大腕版》

一定得选最好的黄金地段
雇中国科学家
造就得造最大最狠的
飞机直接投过去
杀伤力最小也得上千万平米
什么红外制导呀,自动跟踪呀,卫星定位呀 ,
能给他接的全给他接上
上边还得写上字,“看你还牛B”
上面还要贴几张照片
象家爵那样,特狠的那种
以后日本人一看见中国人甭管有事,没事,都得说:爷爷!我服了
一口地道的日本三孙子腔(儿)
倍(儿)想抽丫的
原子弹上再绑几颗导弹
导弹用伊拉克的
就是不爆炸砸也得砸死几千人
再派恐怖组织包围
二十四小时待命
就是一个字(儿) 狠
光吓就得吓死万八千的
日本的军队不是逃跑就是倒戈
你要是说你还是日本人啊
你都能被人用鼻屎弹死

你说这样的战争,得弄死丫多少人
我觉得怎么着也得两千多万吧
两千多万, 那是动手前吓死的
四千多万起
你别嫌多 还不打折
你得研究中国人的反日心理
觉得杀两千万还不够的中国人
根本不在乎再多杀两千万
什么叫日本人 你知道吗?
日本人就是怕什么
都怕最狠的 不怕最仁的
所以,我们弄死日本人口号(儿)就是
不求最仁, 但求最狠 !

——————————————————————————–


大腕》之春节联欢晚会版
  
     一定要选最弱智的人来当导演
    最好是一白痴
    排,就要排一出特折腾人的晚会
    信号直接入户
    整人怎么也得整四个小时
    什么姜球球啊,小朱啊,老倪啊
    能整死观众的都给他请来
    观众席里有托儿,耳麦里有提示
    舞台中间来一水池
    有喷泉、特唬人的那种
    主持人一上台,甭管有事没事都跟人家说
    观众朋友,我想死你们拉
    一口地道的北京腔
    倍儿蛊惑人心
    舞台上再来一电视屏幕
    光赞助就几万美金
    再建个更大的屏幕
    插播厂家拜年的广告
    就是一个字——烦
    被烦死的观众上医院抢救一下就万儿八千的
    周围的病友不是被相声气的,就是被小品气的
    您要只是听歌听累的,你都不好意思跟人打招呼
    您说这样的晚会,一句广告多少钱
    我觉得怎么也得几十万人民币吧
    几十万人民币,那是字幕
    一百万人民币,还不打折
    您得研究广告厂商的心理
    能掏几十万人民币的,根本不在乎多掏几十万
    什么叫怨大头,你知道吗?
    怨大头就是不管做什么广告
    都做最烦的,不做最好的
    所以,我们办晚会的宗旨就是
    不求最好,但求最烦

——————————————————————————–

大腕》网络勾狼版
 
    一定得起最吸引人的名字
    跟着琼瑶小说学
    上站就上男人最多的聊天室
    直接悄悄话招呼
    动作指令背的倍熟
    什么//kiss、//love、//ai
    能用全都给他用上
    套出他信箱,再让他QQ加你,
    QQ的头像要选好,
    大眼睛,特纯情那种,
    有事没事都要说,
    你怎么才来呀,我自己好无聊呀。
    还要叫加个亲嘴符号:*
    倍儿煽情
    时不常跟他二人世界
    要用耳麦的聊天
    一天光说‘我想你了’就得几十遍
    再把照片发给他
    再把照片发给他
    就是一个字 靓
    光照片都让他流口水好几斤
    别人都是明星照,写真集
  
    你要是弄个一寸免冠
    你都不好意思跟人家说你有照片
    你说这样的美女,一天能勾多少人
    “我觉得怎么着也得三五十吧”
    三五十,那时为你杀妻灭子的
    150起,”
    你还别嫌多,还不算说“你好”的
    你就得研究上网的男人的心理
    愿意上网找MM的主
    要的就是找漂亮的
    什么叫没事撑的 你知道吗?
    没事撑的就是同时找8个MM聊,
    都是找最美的,不是找最爱的
    所以,我们上网勾狼的口号就是
    不求最爱,但求最美。。。


——————————————————————————–

战争版

一定得用最好的装备
要打就打死人少的战争,做到零伤亡
飞机就用B52,什么集束弹,贫铀弹,钻地弹
能装的都给它装上
再派上三五百辆M1A2,跟上几千个特种兵
天上再挂上一卫星,像你们家哥伦比亚号不打就自个儿往下掉那种
甭管安理会同意不同意
反正就一个字“打”,倍儿有面子!
再拉上一帮记者,专门儿挑好的写
盟友要选像“英、日、澳”的,狗一般听话又有钱
再选一邻国当基地,不同意就用钱砸!
光一港口就得花个几十亿……
打完了再建一新政权,专门儿为美国服务
还得吓唬其它国家,就一个字“狠”
周围邻居不是亲北约就是亲美的
你要是亲本拉登的,你都不好意思跟人家打招呼
你说打这仗,该让英日澳出多少钱?
我觉得怎么着也得200亿美金吧?
200亿?那是物资援助!400亿起!您别嫌贵,还不打折儿!
你得研究英日澳的战争心理
人家第一次海湾战争肯花200亿
这次根本就不在乎再多花200亿
嘛叫流氓你知道吗?你知道吗?
流氓就是不管打什么仗,不管道理,只看利益
所以我们打仗口号就是:不求有理,但求有利!

——————————————————————————–

登山版

一定得选最好的装备
雇百多个民工
去就得去最高的山
一次直接冲顶
装备最少也得四百公斤
什么GPS呀,氧气筒呀,卫星电话呀
能带的全给他带上
队伍前面有向导
队伍后面有后勤车
队伍里面还得有一队长
戴墨镜,特牛B的那种
队伍一宿营,甭管有事(儿)没事(儿)都得跟队员说
“大家辛苦了”
一口地道的西藏腔(儿)
倍(儿)有面子
队伍里再带几辆修理车
底盘用定做的
一天光油钱就得几千美金
再带一直升机
二十四小时能用的
就是一个字(儿) 快
买瓶矿泉水就得来回万八千公里的
同行的驴友不是用始祖鸟就是Gregory
你要是用一Moutain Hardware
你都不好意思跟人家打招呼
你说这样的队伍,一天你得出多少钱?
我觉得怎么着也得两千美金吧
两千美金 那是成本
四千美金起
你别嫌贵 还不打折
你得研究驴友的炫耀心理
愿意掏两千美金买包的驴友
根本不在乎再多掏两千
什么叫老驴 你知道吗?
老驴就是买什么东西
都买最贵的,不买最好的
所以,我们组队的口号(儿)就是
不求最好,但求最贵!

——————————————————————————–


大腕》经典台词的 CS改版!

一定得选沙二这张图

兄弟几个一起进

当就当悍匪

买枪一定得快

速度最次的也得比条子快上几秒

什么重甲啊、套雷啊、沙鹰啊 能带的全带上。

中门有狙击手 A门有冲锋队 B洞洞口站一悍匪,

寄红头巾长大胡子那种

条子刚进洞 甭管炸的着炸不着都得扔颗雷大吼一声:Fire in the Hole!

一口地道的沙漠土匪腔 倍儿有男人味

扔完接着回去买 什么闪眼啊、炸子啊全伺候着,光买雷就得花个几千块

甭看哥们跟个二到贩子似的 跑上跑下,就为一个字“爽” 拿雷都得炸死七八个。

冲A门的兄弟,你要是拿把mp5你都不好意思和哥几个一起冲!

你说这样的战队一关得杀多少 我觉得怎么也得六七十个吧 六七十!!!!!!!

那是是碰上鼠标不好使!

最少也的得办 100多口子

你还别不服 哥几个还都不作弊

你得研究玩CS人的心理 玩CS玩的好的根本就不愿意在局域网切

什么叫牛X高手你知道吗?

高手就是玩CS只上互连网废老外不在网吧杀菜鸟

所以我们玩CS的口号就是:不当高手只虐菜鸟

——————————————————————————–

网络游戏版

什么叫网络游戏,

  一定得选最好的枪手写文章吹

  雇最好的广告策划

  搞就搞最高档次的网游

  人物种族最少十好几个

  场景最少整它百八十个

  什么种族啊,帮会呀,结婚呀

  能给他划拉上的全部一个不拉

  游戏得抓人,得让他比抽白面儿还过瘾,

  升级,先快后慢,最后得让他想跳楼,

  上手慢没问题,掏钱我卖您新手包儿不就齐了

  玩家一建帐号,甭管有事(儿)没事(儿)都得跟人家说

  May i help you sir (我能为您作点什么吗?)I’M GM.

  一口地道的英国伦敦腔(儿)

  倍(儿)有面子

  主业搞得花哨点儿,

  美工用最好的,

  24小时滚动枪手文章,心得,

  研究报告和进展,

  就是一个字儿,抓人,

  看看你就得给我折进去,

  再弄俩美女玩家,

  影影抄抄的艺术照一摆,

  让他们这帮小东西感觉"网游自有颜如玉",

  自要玩了咱这游戏,连心理带生理的快感都有可能一块堆儿解决了,

  游戏卡,还有外挂,

  那没事儿,我悬赏打击啊,

  卡您不正好多体会体会我这游戏的意境么,

  你说这样的网游,一个月你得收多少钱,

  我觉得怎么着也得20块钱吧

  20块钱 那是成本

  30块钱起

  你别嫌贵 还不打折

  你得研究玩主的购物心理

  愿意掏20人民币的玩家

  根本不在乎再多掏10块包月

  什么叫网络痴呆儿 你知道吗?

  网络痴呆儿就是玩什么游戏不论

  都买最贵的 不买最好的

  所以,我们做网络游戏的口号(儿)就是

  不求最好 但求最累

——————————————————————————–

抗日版

一定要选最好的袭击目标,
雇塔里班恐怖设计大师,
什么东京呀横滨呀京都呀,
能炸的全都给他炸了,
干就干惊天动地的恐怖事业,
用中国最新型的轰炸机,
个头比航母还大三倍,
导弹呀,核弹头呀,能挂的全给他挂上,
原子弹最少也得挂百八十个,
你要是挂普通炸弹,你都不好意思跟人家打招呼,
轰炸机机长用阿富汗人,头上蒙一头巾,
上面加一头圈,特野蛮的那种,
到了日本上空,甭管有事没事,
他都得跟人家日本人说:“MayIhelpyousir”。
一口地道的阿拉伯腔,倍有面子,
你说这样的轰炸机一次能炸死多少日本人?
我觉的怎么也得十来万吧,十来万?那还得是当场死亡,
到医院死亡的还不算,你还别嫌多,还不打折,
就一字:狠!你得揣摸日本人的心理,
经受过原子弹打击的日本人,
根本就不在乎再死个十万八万的,
什么叫日本人你知道吗,
真正的日本人就是不挨炸难受,
炸了倒痛快!
所以我们对日恐怖主义的口号就是“不求最准,但求最狠!”

——————————————————————————–

酸人版


一定得选最华丽浪漫的辞藻
引用流行酸曲中的酸词
写就得写最高尚的主题
直奔人的灵魂深处
什么情爱呀,伤痛呀,风花雪月呀
能用上的全都给它用上
矢恋了就追问“人生”
高兴了就赞美起“真情”
论坛里站一个特杆网友
头像表情特丰富,眼睛特放电的那种
只要电力充足,
甭管有事(儿)没事(儿)都发帖说 “I love U baby”
一口地道的伦敦腔
倍(儿)有面子
论坛里还有高级VIP俱乐部 入会要多发帖
一年光打个“爱”字就得几千次 再建一语音聊天室"
一天二十四小时都可以聊
就是一个字(儿)——酸
下场阴雨就得发个帖抒发个万八千字的
进来的网友不是反对F4就是支持F4的
你要是不知道F4是四个轮(儿)的破车呀
你都不好意思跟人家打招呼
你说感情这么丰富的人,发个帖至少要打多少个字
“我觉得怎么着也得一千个字(儿)吧 ?
一千个字(儿) 那还处在感情酝酿阶段)
两千字(儿)起 你别嫌长 还没可删的字(儿 )
你得研究网友们的读帖心理 愿意看一千字(儿)的主儿
根本不在乎再多一千 什么叫情痴 你知道吗?
情痴就是爱什么东西 都爱最酸的 不爱最好的|`
“为什么呀?”
废什么话呀
你知道为什么大陆歌曲比不过港台吗
所以,我们论坛的口号(儿)就是 不求最好 但求最酸
——————————————————————————–


传奇版

一定要选地牢里刷得最多的黄金地段,

雇个旺财MM,

而且要雇装备最好级别最高的。

怎么也得40级以上,

什么三眼呀、灵魂呀、天尊呀,

能带上的都给她带上。

看见有人来,

甭管有事没事,都得跟人喊:

让就一个字,我就说一次,别让俺用行动来表示。

一口地道的山东腔儿,

倍儿有面子。

建个行会,

收个人最低也得35级,

而且还得有好装备。

行会成员里不是裁决就是龙纹,

你要是拿个无机菜刀什么的,

你都不好意思跟人打招呼。

有事没事就开个行会战,

每个人只药水费就得四五十万,

而且24小时供应,

就是一个字儿:拽!

你说这样的号,一年你得花多少钱啊﹖

我觉得怎么着也得一万元吧?

一万元?那是35级以前!

两万元起!

你还别嫌贵,只多不少!

你得研究超级玩家的心理,

愿意花188元买超级包重新练起的主儿,

根本不在乎多掏一万!

什么叫超级玩家你知道吗?

就是买装备时,

只要名牌,不要变态极品!

——————————————————————————–


大腕精彩对白(足球版)


一定得选我们自己的主场
雇国际级黑哨

玩就得玩最高档次的对手

点球直接入网

红牌最少也得两张

什么越位呀,假摔呀,黄牌呀

能给他判的全给他判

场上边有主裁,场下边有边裁

主席台上坐一姓郑的鸟汉,

画文身,特流氓的那种

对手一进门儿,甭管有事儿没事儿都得跟人家说

I WILL KILL YOU,BABY

一口地道的汉城痞子腔儿

倍儿有面子

足联里再选一畜生主席,黑人不带眨眼的

一年光回扣就得几百万美金

再搞一东南亚第四官员,从来就是这样吹

就是一个字儿——黑

争个头球就得花个红黄牌的

周围的球迷不是拿手枪就是扛鸟铳

你要是空手看球呀

你都不好意思跟人家打招呼

你说这样的比赛,能踢出什么成绩

我觉得怎么着也得8强吧

8强?!那是客气

四强起

你别生气,还不定冠军

你得韩国人民的看球心理

愿意掏两千美金看球的主

根本不在乎什么公平

什么叫伪球迷你知道吗?

伪球迷就是

吹什么东西都吹赢了的,不吹良心的

所以,我们做东道主的口号就是

不求最强,但求最黑!

——————————————————————————–


MU版大腕经典对白


练级时一定得选石头掉得最多的黄金


  雇个智力MM,

  而且要雇装备最好级别最高的,

  怎么也得350级以上,

  什么卓越装备啊,加血翅膀啊

  能带上的都给她带上。

  看见有人来,

  甭管有事没事,都得跟人喊:

  让就一个字,我就说一次,别让俺用


  一口地道的东北腔儿,

  倍有面子,

  建个行会,

  收个人最低也得350级,
而且还得有好装备

  行会成员拿的不是卓越破坏就是卓越玄冰

  你要是拿对什么+11破坏什么的,

  你都不好意思跟人打招呼。

  有事没事就开个行会战,

  每个人送药水费就 得4,5千万

  而且24小时供应

  就是一个字:拽!

  这样的号,你一年 得花多少钱啊?

  我觉得怎么着也得 1万块吧?

  一万块?那是30 0级以前!

  5万元起!

  你还别嫌贵,只多 没少!

  你得研究超级玩家 的心理,

  愿意拿万个八千买 装备的主儿,

  根本不在乎多掏几 万!

  什么叫超级玩家你 知道不?

  就是买装备时,

  只要最贵,不要最好!


——————————————————————————–


大腕对白(军事版)

一定要选最新技术的
这么也得好过阿里伯克
造就造最先进,最nb的
水手全是各大学的校花
排水量少说也要两万八千吨
色么vls,神盾,粒子束,射频武器
一般军舰能有的全有,没有的也要有。
还特横
动不动就把不顺眼的国家修理一下
要找这么一主
早上八点出航打台湾,五点下班前还能灭了日本。
打完印度就停在夏威夷
全舰灯火通明,火控雷达主动声纳全打开像没事那种
一进公海见到美军甭管有事没事发报:
“停车上舰检查”
倍(儿)有面子
闲着就出去撒野
横在马六甲海峡
一年光交际费就几亿美金
军舰这排场那是应酬的
一天24小时花钱
就一个字(儿)贵
光喷个隐形漆就花个上亿
现在这行情你不这么你被人瞧不起
周围的邻居不是“金刚”就是“宙斯盾”
你要是造个“旅大”呀
你都不好意思跟人家打招呼
你猜这样的军舰,你得花多少钱?
我觉得怎么着也得要20亿美金吧?
20亿,那是设计费
80亿起
你还别显贵 还不改设计
你的研究我们爱国贼的心理
坚持花200亿美金造航母的主
根本不在乎再多掏80亿
什么叫“爱国者”,你知道吗
就是在网上吹牛时
都吹看了多少书,不在乎学会了多少。

——————————————————————————–


“大腕”经典对白之外挂版(原创)

一定要选外挂最多的游戏玩,开足了马力,做就做最bt的挂机者!

天天要求挂机,最少也要挂百八十个游戏,什么奇迹啊,传奇啊,石器时代啊,能挂的都挂着。

24小时连轴挂,还顺带上学校的机子。

只要是能上网的,都挂上,属于那种全职种子的挂er。

gm出现,甭管有事(儿)没事(儿)都得跟人家说:练及中请勿打搅!

一脸地道的无辜样,倍(儿)可怜!

游戏里每个人都加你为好友,游戏排名置顶的是你的大号,小号,小小号,连小区大妈都
知道你的id,

吊线了马上起床挂,二十四小时候着,就一个字,累!

光电费就得花个万八千的。

别的挂er挂1个2个号的,你要是只挂3个,你都不好意思上
游戏和大家打招呼!

别人挂1及/天的速度,你至少也来个20及/天的速度,不然都不好意思和
人说你用的是收费外挂。

你说,这样的挂er一个月要花多少钱,(旁:我觉得怎么着也得1000多
块吧?)

1000多块?!那是一天!一个月30000多块!别嫌多,还是保守估计!

你得研究咱优秀外挂人的心理,能花三万多块还被封个半死的,根本就不在
乎花多少钱!

什么是优秀外挂人,你知道吗?

优秀外挂人就是不管什么游戏,都用外挂,最好收费vip版本的,不
用那个试用的,

所以,我们优秀外挂人的口号(儿)就是:不怕花钱,只要挂!
——————————————————————————–


大腕》(大学生版):
把所有的课:什么公共、专基、专业,能逃的都给它逃了,
再拉上几个哥们,逃就逃个集体的,
班里有兄弟顶着,根本不怕他点名,
实在不行弄一假条,半透明的那种,
只要你多开药,不管你有病买病都能开个发烧、休息三天,
一手正宗的校医笔记,倍儿权威!
其他人逃的都是什么C语言、信号系统之类的,
你要就逃一选修你都不好意思和人家打招呼。
出去打cs。光网吧就得去几家,就一个字:拽!
你说这么逃期末得挂几科?
我瞧至少得两科吧!
两科?那是旷考的。
四科起,你还别嫌多!
这还不算选修。你得研究旷课人的心理,
挂得起两科的就不在乎再多挂两科。
什么叫现代大学生,就是不怕考试不能通过,
就怕现在不能旷课!所以我们的口号就是:
不求通过,但求旷课!

——————————————————————————–