2006年11月11日

–据悉,国内金融界从中国国情出发,已经开始共建金融权威认证中心(CA)。这项计划由银行卡信息交换总中心负责筹建,经金融信息化领导小组正式批准,今年3月已开始向国内外厂商发出招标通告。CA只是电子商务信息安全大系统中的一个环节,这里我们将向读者全面系统介绍网络安全中的一个重要概念及技术。

–随着Internet技术的发展,Intranet服务、电子商务、网络银行成为近年来的几大热点,国内也有越来越多的机构、企业涉足这些领域。这样,信息安全问题就提到日程上来。几年来,在Internet上涌现出了许多新的安全技术和安全规范,公开密钥基础设施(PKI:PublicKeyInfrastructure)即是其中一员。  

 PKI的基本组成

–PKI是一种遵循标准的密钥管理平台,它能够为所有网络应用透明地提供采用加密和数字签名等密码服务所必需的密钥和证书管理。PKI必须具有认证机关(CA)、证书库、密钥备份及恢复系统、证书作废处理系统、客户端证书处理系统等基本成分,构建PKI也将围绕着这五大系统来构建。

–1.认证机关

–CA是证书的签发机构,它是PKI的核心。众所周知,构建密码服务系统的核心内容是如何实现密钥管理,公钥体制涉及到一对密钥,即私钥和公钥,私钥只由持有者秘密掌握,无须在网上传送,而公钥是公开的,需要在网上传送,故公钥体制的密钥管理主要是公钥的管理问题,目前较好的解决方案是引进证书(certificate)机制。

–证书是公开密钥体制的一种密钥管理媒介。它是一种权威性的电子文档,形同网络计算环境中的一种身份证,用于证明某一主体(如人、服务器等)的身份以及其公开密钥的合法性。在使用公钥体制的网络环境中,必须向公钥的使用者证明公钥的真实合法性。因此,在公钥体制环境中,必须有一个可信的机构来对任何一个主体的公钥进行公证,证明主体的身份以及他与公钥的匹配关系。

CA正是这样的机构,它的职责归纳起来有:  

验证并标识证书申请者的身份;   

确保CA用于签名证书的非对称密钥的质量;  

确保整个签证过程的安全性,确保签名私钥的安全性;   

证书材料信息(包括公钥证书序列号、CA标识等)的管理;   

确定并检查证书的有效期限;  

确保证书主体标识的唯一性,防止重名;发布并维护作废证书表;  

对整个证书签发过程做日志记录;   

向申请人发通知。

–其中最为重要的是CA自己的一对密钥的管理,它必须确保其高度的机密性,防止他方伪造证书。CA的公钥在网上公开,整个网络系统必须保证完整性。

–证书的主要内容如右表所示。

–其中,CA的数字签名保证了证书(实质是持有者的公钥)的合法性和权威性。   

主体(用户)的公钥可有两种产生方式:用户自己生成密钥对,然后将公钥以安全的方式传送给CA,该过程必须保证用户公钥的可验证性和完整性;   

CA替用户生成密钥对,然后将其以安全的方式传送给用户,该过程必须确保密钥对的机密性、完整性和可验证性。该方式下由于用户的私钥为CA所知,故对CA的可信性有更高的要求。

–用户A可通过两种方式获取用户B的证书和公钥,一种是由B将证书随同发送的正文信息一起传送给A,另一种是所有的证书集中存放于一个证书库中,用户A可从该地点取得B的证书。

–CA的公钥可以存放在所有节点处,方便用户使用。  

 表证书的主要内容

–上表中的"用途"是一项重要的内容,它规定了该证书所公证的公钥的用途。公钥必须按规定的用途来使用。一般地,公钥有两大类用途:用于验证数字签名。消息接收者使用发送者的公钥对消息的数字签名进行验证。用于加密信息。消息发送者使用接收者的公钥加密用于加密消息的密钥,进行数据加密密钥的传递。

–相应地,系统中需要配置用于数字签名/验证的密钥对和用于数据加密/脱密的密钥对,这里分别称为签名密钥对和加密密钥对。这两对密钥对于密钥管理有不同的要求:   

签名密钥对

–签名密钥对由签名私钥和验证公钥组成。签名私钥具有日常生活中公章、私章的效力,为保证其唯一性,签名私钥绝对不能够作备份和存档,丢失后只需重新生成新的密钥对,原来的签名可以使用旧公钥的备份来验证。验证公钥需要存档,用于验证旧的数字签名。

–用作数字签名的这一对密钥一般可以有较长的生命期。  

加密密钥对

–加密密钥对由加密公钥和脱密私钥组成。

–为防止密钥丢失时丢失数据,脱密私钥应该进行备份,同时还可能需要进行存档,以便能在任何时候脱密历史密文数据。加密公钥无须备份和存档,加密公钥丢失时,只须重新产生密钥对。

–加密密钥对通常用于分发会话密钥,这种密钥应该频繁更换,故加密密钥对的生命周期较短。

–不难看出,这两对密钥的密钥管理要求存在互相冲突的地方,因此,系统必须针对不同的用途使用不同的密钥对,尽管有的公钥体制算法,如目前使用广泛的RSA,既可以用于加密、又可以用于签名,在使用中仍然必须为用户配置两对密钥、两张证书,其一用于数字签名,另一用于加密。

–2.证书库

–证书库是证书的集中存放地,它与网上"白页"类似,是网上的一种公共信息库,用户可以从此处获得其他用户的证书和公钥。

–构造证书库的最佳方法是采用支持LDAP协议的目录系统,用户或相关的应用通过LDAP来访问证书库。系统必须确保证书库的完整性,防止伪造、篡改证书。

–3.密钥备份及恢复系统—-如果用户丢失了用于脱密数据的密钥,则密文数据将无法被脱密,造成数据丢失。为避免这种情况的出现,PKI应该提供备份与恢复脱密密钥的机制。

–密钥的备份与恢复应该由可信的机构来完成,例如CA可以充当这一角色。值得强调的是,密钥备份与恢复只能针对脱密密钥,签名私钥不能够作备份。

–4.证书作废处理系统

–证书作废处理系统是PKI的一个重要组件。同日常生活中的各种证件一样,证书在CA为其签署的有效期以内也可能需要作废,例如,A公司的职员a辞职离开公司,这就需要终止a证书的生命期。为实现这一点,PKI必须提供作废证书的一系列机制。作废证书有如下三种策略:   

作废一个或多个主体的证书;   

作废由某一对密钥签发的所有证书;  

作废由某CA签发的所有证书。

–作废证书一般通过将证书列入作废证书表(CRL)来完成。通常,系统中由CA负责创建并维护一张及时更新的CRL,而由用户在验证证书时负责检查该证书是否在CRL之列。CRL一般存放在目录系统中。

–证书的作废处理必须在安全及可验证的情况下进行,系统还必须保证CRL的完整性。

–5.PKI应用接口系统

–PKI的价值在于使用户能够方便地使用加密、数字签名等安全服务,因此一个完整的PKI必须提供良好的应用接口系统,使得各种各样的应用能够以安全、一致、可信的方式与PKI交互,确保所建立起来的网络环境的可信性,同时降低管理维护成本。

–为了向应用系统屏蔽密钥管理的细节,PKI应用接口系统需要实现如下的功能:   

完成证书的验证工作,为所有应用以一致、可信的方式使用公钥证书提供支持;   

以安全、一致的方式与PKI的密钥备份与恢复系统交互,为应用提供统一的密钥备份与恢复支持;   

在所有应用系统中,确保用户的签名私钥始终只在用户本人的控制之下,阻止备份签名私钥的行为;  

根据安全策略自动为用户更换密钥,实现密钥更换的自动、透明与一致;   

为方便用户访问加密的历史数据,向应用提供历史密钥的安全管理服务;  

为所有应用访问统一的公用证书库提供支持;  

以可信、一致的方式与证书作废系统交互,向所有应用提供统一的证书作废处理服务;  

完成交叉证书(见后)的验证工作,为所用应用提供统一模式的交叉验证支持;

支持多种密钥存放介质,包括IC卡、PC卡、安全文件等。

–最后,PKI应用接口系统应该是跨平台的。  

PKI的功能

–归纳起来,PKI应该为应用提供如下的安全支持:   

证书与CA

–PKI应实现CA以及证书库、CRL等基本的证书管理功能。  

密钥备份及恢复证书、密钥对的自动更换

–证书、密钥都有一定的生命期限。当用户的私钥泄露时,必须更换密钥对;另外,随着计算机速度日益提高,密钥长度也必须相应地增长。因此,PKI应该提供完全自动(无须用户干预)的密钥更换以及新证书的分发工作。  

交叉签证

–每个CA只可能覆盖一定的作用范围,即CA的域,例如,不同的企业往往有各自的CA,它们颁发的证书都只在企业范围内有效。当隶属于不同CA的用户需要交换信息时,就需要引入交叉证书和交叉验证,这也是PKI必须完成的工作。  

加密密钥和签名密钥的分隔

–如前所述,加密和签名密钥的密钥管理需求是相互抵触的,因此PKI应该支持加密和签名密钥的分隔使用。  

 支持对数字签名的不可抵赖

–任何类型的电子商务都离不开数字签名,因此PKI必须支持数字签名的不可抵赖性,而数字签名的不可抵赖性依赖于签名私钥的唯一性和机密性,为确保这一点,PKI必须保证签名密钥与加密密钥的分隔使用。  

密钥历史的管理

–每次更新加密密钥后,相应的解密密钥都应该存档,以便将来恢复用旧密钥加密的数据。每次更新签名密钥后,旧的签名私钥应该妥善销毁,防止破坏其唯一性;相应的旧验证公钥应该进行存档,以便将来用于验证旧的签名。这些工作都应该是PKI自动完成的。  

PKI的性能要求

–作为网络环境的一种基础设施,PKI必须具有良好的性能,一般地,对PKI的性能有如下要求:

透明性和易用性

–这是对PKI的最基本要求,PKI必须尽可能地向上层应用屏蔽密码服务的实现细节,向用户屏蔽复杂的安全解决方案,使密码服务对用户而言简单易用,同时便于单位、企业完全控制其信息资源。  

可扩展性

–证书库和CRL必须具有良好的可扩展性。

互操作性

–不同企业、单位的PKI实现可能是不同的,这就提出了互操作性要求。要保证PKI的互操作性,必须将PKI建立在标准之上,这些标准包括加密标准、数字签名标准、HASH标准、密钥管理标准、证书格式、目录标准、文件信封格式、安全会话格式、安全应用程序接口规范等。  

 支持多应用 –PKI应该面向广泛的网络应用,提供文件传送安全、文件存储安全、电子邮件安全、电子表单安全、Web应用安全等保护。

支持多平台

–PKI应该支持目前广泛使用的操作系统平台,包括Windows、UNIX、MAC等
 

  1.了解上司的为人。

    如果你不了解上司的为人、喜好、个性,只顾挥汗如雨的埋头苦干,工作再怎么出色也是得不到上司的赏识和认同的。在工作中多多留意一下领导的言谈举止,品味一下他的为人,不但可以减少相处过程中不必要的摩擦,还可以促进相互之间的沟通,为自己的晋升扫清障碍。

    2.注意等级差别。

    由于家庭环境和社会影响,现在的年轻人很容易只把上司看为公司团队中与自己相同的一分子。特别是上司与自己年龄相仿,在工作交往中便更加无所顾忌。平时直呼其名,随便开玩笑,若有了什么不满也不分场合直抒胸臆。但是上司不是你的朋友,他在乎他的权威和地位,他需要别人的承认。如果你的上司还有上司,你如此“礼遇”他,他会很没面子。就算他是你的朋友,在公司也最好把你们的关系单纯为简单的上下级关系。

    3.永远不要在上司面前流泪。

    泪水容易给人这样一个信息:她是柔弱的,她的承受力太差了。如果你在上司面前流眼泪,原先打算提拔你的上司也可能会认为你不能胜任你的工作。

    4.忠诚。

    你可以能力有限,你可以处事不够圆滑,你可以有些诸如丢三拉四的小毛病,但你绝对不可以不忠诚。忠诚是上司对员工的第一要求。不要试图搞小动作,你的上司能有今天的位置说明他绝非等闲之辈,你智商再高,手段再高明,在他的经验阅历面前也不过是小儿科。

    5.及时完成工作。

    职员的天职就是工作。如果没有完成上司交给你的任务,不管有什么客观因素,别试图在心存不快的上司面前解释什么,没有做好本职工作,任何理由都不是理由,因为上司关心的只是事情的结果。工作没做好,你的解释只会让他更加反感。如果确实是上司的安排有问题,你可以事后委婉地提出,但千万不要把它作为拖延工作的理由。

    6.敏感上司的动机。

    上司的不同命令下达方法可能暗含不同的目的:吩咐,意即要求下属严格执行,不得另行提出建议及加上自己的判断;请托,给予下属若干自由裁量的空间,大方向不得更改;征询,欲使下属产生强烈的意愿和责任感,对他极为青睐;暗示,面对能力强的下属,有意培养对方的能力。所以,当你接受一个任务时,用点心,不要辜负领导的美意,错失良机。

    7.小处不可随便。

    注意自己的言谈举止和工作中的细节问题,越是随意的场合越要加以小心;当事人无心,旁观者有意。很多上司都信奉“见微知著”的四字箴言,认为这些生活中的旁枝末节将暴露一个人太多的秘密——潜意识的行为是很难伪装的。比如文案的摆放显示了你做事的条理性和缜密度,发言的音量说明了你的自信心如何,酒会上行为是否得体暴露了你的个人修养与自制力,等等。

    8.不要委曲求全。

    涉世之初,我们被告诫:“忍一时风平浪静,退一步海阔天空”,总想委曲求全,即使被领导冤枉骂得狗血淋头也固守沉默是金,大不了背地里痛哭一场。可委曲能求全吗?NO!一方面你的“大度”掩盖了公司内部真正存在的问题,另一方面会让上司误解你的能力甚至是人品,你的无言使他对自己的判断更加深信不疑:她理亏所以她无话可说。既然于公于私都无益,你还不如找机会解释清楚,比如写封信。

    9.要有团队精神。

    没有一个上司喜欢害群之马,因为是他所服务的团队给了他威严、权利和成就感。没有整个团队的成长,他的事业就失去了依托。不要只想着怎样讨上司喜欢,要和你的同事融洽相处,别搞个人主义,孤胆英雄只有在影视作品里才有人赏识,在现实生活中却让所有的人嫉恨。如果你不打算做个自由职业者,除了融入集体,别无选择。《女性月刊》嫣然

 

清平调词三首

李白

  云想衣裳花想容, 春风拂槛露华浓。
  若非群玉山头见, 会向瑶台月下逢。
  一枝红艳露凝香, 云雨巫山枉断肠。
  借问汉宫谁得似? 可怜飞燕倚新妆。
  名花倾国两相欢, 长得君王带笑看。
  解释春风无限恨, 沉香亭北倚阑干。

  这三首诗是李白在长安供奉翰林时所作。一日,玄宗和杨妃在宫中观牡丹花,因命李白写新乐章,李白奉诏而作。在三首诗中,把木芍药(牡丹)和杨妃交互在一起写,花即是人,人即是花,把人面花光浑融一片,同蒙唐玄宗的恩泽。从篇章结构上说,第一首从空间来写,把读者引入蟾宫阆苑;第二首从时间来写,把读者引入楚襄王的阳台,汉成帝的宫廷;第三首归到目前的现实,点明唐宫中的沉香亭北。诗笔不仅挥洒自如,而且相互钩带。“其一”中的春风,和“其三”中的春风,前后遥相呼应。

  第一首,一起七字:“云想衣裳花想容,”把杨妃的衣服,写成真如霓裳羽衣一般,簇拥着她那丰满的玉容。“想”字有正反两面的理解,可以说是见云而想到衣裳,见花而想到容貌,也可以说把衣裳想象为云,把容貌想象为花,这样交互参差,七字之中就给人以花团锦簇之感。接下去“春风拂槛露华浓”,进一步以“露华浓”来点染花容,美丽的牡丹花在晶莹的露水中显得更加艳冶,这就使上句更为酣满,同时也以风露暗喻君王的恩泽,使花容人面倍见精神。下面,诗人的想象忽又升腾到天堂西王母所居的群玉山、瑶台。“若非”、“会向”,诗人故作选择,意实肯定:这样超绝人寰的花容,恐怕只有在上天仙境才能见到!玉山、瑶台、月色,一色素淡的字眼,映衬花容人面,使人自然联想到白玉般的人儿,又象一朵温馨的白牡丹花。与此同时,诗人又不露痕迹,把杨妃比作天女下凡,真是精妙至极。

  第二首,起句“一枝红艳露凝香”,不但写色,而且写香;不但写天然的美,而且写含露的美,比上首的“露华浓”更进一层。“云雨巫山枉断肠”用楚襄王的故事,把上句的花,加以人化,指出楚王为神女而断肠,其实梦中的神女,那里及得到当前的花容人面!再算下来,汉成帝的皇后赵飞燕,可算得绝代美人了,可是赵飞燕还得倚仗新妆,那里及得眼前花容月貌般的杨妃,不须脂粉,便是天然绝色。这一首以压低神女和飞燕,来抬高杨妃,借古喻今,亦是尊题之法。相传赵飞燕体态轻盈,能站在宫人手托的水晶盘中歌舞,而杨妃则比较丰肥,固有“环肥燕瘦”之语(杨贵妃名玉环)。后人据此就编造事实,说杨妃极喜此三诗,时常吟哦,高力士因李白曾命之脱靴,认为大辱,就向杨妃进谗,说李白以飞燕之瘦,讥杨妃之肥,以飞燕之私通赤凤,讥杨妃之宫闱不检。李白诗中果有此意,首先就瞒不过博学能文的玄宗,而且杨妃也不是毫无文化修养的人。据原诗来看,很明显是抑古尊今,好事之徒,强加曲解,其实是不可通的。

  第三首从仙境古人返回到现实。起首二句“名花倾国两相欢,长得君王带笑看”,“倾国”美人,当然指杨妃,诗到此处才正面点出,并用“两相欢”把牡丹和“倾国”合为一提,“带笑看”三字再来一统,使牡丹、杨妃、玄宗三位一体,融合在一起了。由于第二句的“笑”,逗起了第三句的“解释春风无限恨”,春风两字即君王之代词,这一句,把牡丹美人动人的姿色写得情趣盎然,君王既带笑,当然无恨,恨都为之消释了。末句点明玄宗杨妃赏花地点──“沉香亭北”。花在阑外,人倚阑干,多么优雅风流。

  这三首诗,语语浓艳,字字流葩,而最突出的是将花与人浑融在一起写,如“云想衣裳花想容”,又似在写花光,又似在写人面。“一枝红艳露凝香”,也都是人、物交溶,言在此而意在彼。读这三首诗,如觉春风满纸,花光满眼,人面迷离,不待什么刻画,而自然使人觉得这是牡丹,这是美人玉色,而不是别的。无怪这三首诗当时就深为唐玄宗所赞赏。

 三类植物最适合室内放

  最近,美国航空航天局的科学家们发现,常青的观叶植物以及绿色开花植物中,很多都有消除建筑物内有毒化学物质的作用。此次研究还发现,植物不光是靠叶子吸取物质,植物的根以及土壤里的细菌在清除有害物方面都功不可没。

  能吸收有毒化学物质的植物

  1.芦荟、吊兰、虎尾兰、一叶兰、龟背竹是天然的清道夫,可以清除空气中的有害物质。有研究表明,虎尾兰和吊兰可吸收室内80%以上的有害气体,吸收甲醛的能力超强。芦荟也是吸收甲醛的好手。此外,如果装修业主想测试自己的家装环保情况,可以在家居的不同位置摆放文竹。它的生命力在植物中是最脆弱的,文竹健康生长说明家装没有污染。

  2.常青藤、铁树、菊花等能有效地清除二氧化硫、氯、乙醚、乙烯、一氧化碳、过氧化氮、硫、氟化氢、汞等有害物。

  3.紫苑属、黄耆、含烟草、黄耆属和鸡冠花等一类植物,能吸收大量的铀等放射性核素。

  4.天门冬可清除重金属微粒。

  能驱蚊虫的植物

  如果植物能驱蚊,一定比蚊香安全环保得多,蚊净香草就是这样一种植物。

  另外,一种名为除虫菊的植物含有除虫菊酯,也能有效驱除蚊虫。

  能杀病菌的植物

  1.玫瑰、桂花、紫罗兰、茉莉等芳香花卉产生的挥发性油类具有显著的杀菌作用。

  2.紫薇、茉莉、柠檬等植物,5分钟内就可以杀死白喉菌和痢疾菌等原生菌。

  3.仙人掌等原产于热带干旱地区的多肉植物,其肉质茎上的气孔白天关闭,夜间打开,在吸收二氧化碳的同时,制造氧气,使室内空气中的负离子浓度增加。

  3.在家居周围栽种爬山虎、葡萄、牵牛花、紫藤、蔷薇等攀爬植物,让它们顺墙或顺架攀附,形成一个绿色的凉棚,能够有效地减少阳光辐射,大大降低室内温度。

  4.丁香花散发的丁香酚,杀菌能力比碳酸还强5倍以上。因此这些花能芳香健脑,还能预防某些传染病,对牙痛也有止痛的作用。

  家居健康绿化手册

  客厅:选用艳丽插花

  客厅是家人团聚和会客的场所,适合选用艳丽插花和高贵大方的植物,如玫瑰、水仙、海棠、兰花、君子兰等。

  餐厅:可用植物作间隔

  可选用色彩艳丽的植物,如春兰、秋菊等。现在不少房间是客厅连着餐厅的,可用植物作间隔,如悬垂绿萝、洋常春藤、吊兰等。

  卧室:安排小型的盆花

  这里需要营造出一种恬静舒适的气氛,可在窗旁放置一盆茉莉、桂花或月季。如果是插花就可选用淡雅的山百合、黄花百合、水仙等。卧室面积较小,布置植物不宜过多,宜安排小型的盆花,如芦荟、文竹等。

  书房:以观叶植物为宜

  布置的时候应注意营造一个优雅宁静的氛围,可放置米兰、水仙、茉莉等清秀文雅的花卉。选择植物不宜过多,以观叶植物或颜色较浅的盆花为宜,如在书桌上摆一两盆文竹、万年青等,在书架上方靠墙处摆盆悬吊植物,使整个书房显得文雅清静。此外,书房还可摆些插花,插花的色彩不宜太浓,以简洁的东方式插花为宜,也可布置两盆盆景。

  厨房:可种盆葱蒜等

  这是一个经常要干活的空间,因此摆设植物宜小不宜大,最简便的办法是种一盆葱、蒜等食用植物作装饰。当然也可在靠近窗台的台面上放一瓶插花,摆脱油烟气息。厨房温湿度变化较大,应选择一些适应性强的小型盆花。

  卫生间:蕨类植物较合适

  应选择抵抗力强且耐阴暗的蕨类植物或不占地方的细长形绿色植物,放一盆藤蔓植物在卫生间的窗台上也非常漂亮。

  阳台:喜光耐旱多肉植物

  阳台多位于楼房的向阳面,具有阳光充足、通风的优点,为居民养花创造了良好的条件。阳台一般是水泥结构,蒸发量大,十分干燥。选用喜光耐旱的多肉植物,如仙人掌类、月季、茉莉花、石榴、葡萄、夜来香、六月雪、茑萝、美女樱等。喜阴的有龟背竹、紫露草、文竹、铁线蕨、万年青等。悬吊栽培的花卉有吊兰、虎头兰、莎草兰、矮牵牛、宝石花、紫鸭跖草等。阳台花卉管理、肥水供给要适当,浇水要根据干湿情况;夏季天气炎热,阳台环境干燥,上午浇水1次,傍晚浇1次透水,每天上、下午用小喷壶向枝叶上喷水及向地面洒水,提高空气湿度。

2006年11月09日

程序文档,曾经是程序员的一个头痛问题。写一个程序文档,比较花时间,但不是很难;麻烦的是当程序修改后,程序文档也要跟着同步更新,否则文档和程序就要脱节,文档也就变成没用的东西了。

好在有许多好用的文档生成器来解决这个问题。目前比较流行的C++文档生成器是doxygen。
本文就简单的介绍一下doxygen的文档注释方法,以供初学者参考:

1. 模块定义(单独显示一页)

/*
 * @defgroup 模块名 模块的说明文字
 * @{
 */
 
 … 定义的内容 …
 
/** @} */ // 模块结尾
 
2. 分组定义(在一页内分组显示)
/*
 * @name 分组说明文字
 * @{
 */
 
 … 定义的内容 …
 
/** @} */
 
3. 变量、宏定义、类型定义简要说明
/** 简要说明文字 */
#define FLOAT float
 
/** @brief 简要说明文字(在前面加 @brief 是标准格式) */
#define MIN_UINT 0
 
/*
 * 分行的简要说明 \n
 *  这是第二行的简要说明
 */
int b;
 
4. 函数说明
/*
 * 简要的函数说明文字 
 *  @param [in] param1 参数1说明
 *  @param [out] param2 参数2说明
 *  @return 返回值说明
 */
int func(int param1, int param2);
 
/*
 * 打开文件 \n
 *  文件打开成功后,必须使用 ::CloseFile 函数关闭。
 *  @param[in] file_name 文件名字符串
 *  @param[in] file_mode 文件打开模式字符串,可以由以下几个模块组合而成:
 *  - r 读取
 *  - w 可写
 *  - a 添加
 *  - t 文本模式(不能与 b 联用)
 *  - b 二进制模式(不能与 t 联用)
 *  @return 返回文件编号
 *  - -1 表示打开文件失败
 
 *  @note 文件打开成功后,必须使用 ::CloseFile 函数关闭
 *  @par 示例:
 *  @code
    // 用文本只读方式打开文件
    int f = OpenFile("d:\\test.txt", "rt");
 *  @endcode
 
 *  @see ::ReadFile ::WriteFile ::CloseFile
 *  @deprecated 由于特殊的原因,这个函数可能会在将来的版本中取消。
 */
int OpenFile(const char* file_name, const char* file_mode);
 
5. 枚举类型定义
/** 枚举常量 */
typedef enum TDayOfWeek
{
SUN = 0, /**<  星期天(注意,要以 “<” 小于号开头) */
MON = 1, /**<  星期一 */
TUE = 2, /**<  星期二 */
WED = 3, /**<  星期三 */
THU = 4, /**<  星期四 */
FRI = 5, /**<  星期五 */
SAT = 6  /**<  星期六 */
}
/** 定义类型 TEnumDayOfWeek */
TEnumDayOfWeek;  
  
6. 项目符号标记
  /* 
   *  A list of events:
   *    – mouse events
   *         -# mouse move event
   *         -# mouse click event\n
   *            More info about the click event.
   *         -# mouse double click event
   *    – keyboard events
   *         -# key down event
   *         -# key up event
   *
   *  More text here.
   */
 

结果为:

A list of events:

  • mouse events
    1. mouse move event
    2. mouse click event
      More info about the click event.
    3. mouse double click event
  • keyboard events
    1. key down event
    2. key up event

More text here.

代码示范:
/**//*
 * @defgroup EXAMPLES 自动注释文档范例
 * @author  沐枫
 * @version 1.0
 * @date    2004-2005
 * @{
 */
/**//*
 * @name 文件名常量
 * @{
 */
/**//** 日志文件名 */
#define LOG_FILENAME "d:\\log\\debug.log"
/**//** 数据文件名 */
#define DATA_FILENAME "d:\\data\\detail.dat"
/**//** 存档文件名 */
#define BAK_FILENAME "d:\\data\\backup.dat"
/**//** @}*/ // 文件名常量
/**//*
 * @name 系统状态常量
 *  @{
 */
 
/**//** 正常状态 */
#define SYS_NORMAL 0
/**//** 故障状态 */
#define SYS_FAULT 1
/**//** 警告状态 */
#define SYS_WARNNING 2
/**//** @}*/ // 系统状态常量
/**//** 枚举常量 */
typedef enum TDayOfWeek
{
        SUN = 0, /**//**< 星期天 */
        MON = 1, /**//**< 星期一 */
        TUE = 2, /**//**< 星期二 */
        WED = 3, /**//**< 星期三 */
        THU = 4, /**//**< 星期四 */
        FRI = 5, /**//**< 星期五 */
        SAT = 6  /**//**< 星期六 */
}
/**//** 定义类型 TEnumDayOfWeek */
TEnumDayOfWeek; 
/**//** 定义类型 PEnumDayOfWeek */
typedef TEnumDayOfWeek* PEnumDayOfWeek;
/**//** 定义枚举变量 enum1 */
TEnumDayOfWeek enum1;       
/**//** 定义枚举指针变量 enum2 */
PEnumDayOfWeek p_enum2;
/**//*
 * @defgroup FileUtils 文件操作函数
 * @{
 */
/**//*
 * 打开文件 \n
 *  文件打开成功后,必须使用 ::CloseFile 函数关闭。
 *  @param[in] file_name 文件名字符串
 *  @param[in] file_mode 文件打开模式字符串,可以由以下几个模块组合而成:
 *  – r 读取
 *  – w 可写
 *  – a 添加
 *  – t 文本模式(不能与 b 联用)
 *  – b 二进制模式(不能与 t 联用)
 *  @return 返回文件编号
 *  – -1 表示打开文件失败
 
 *  @note 文件打开成功后,必须使用 ::CloseFile 函数关闭
 *  @par 示例:
 *  @code
    // 用文本只读方式打开文件
    int f = OpenFile("d:\\test.txt", "rt");
 *  @endcode
 
 *  @see ::ReadFile ::WriteFile ::CloseFile
 *  @deprecated 由于特殊的原因,这个函数可能会在将来的版本中取消。
 */
int OpenFile(const char* file_name, const char* file_mode);
/**//*
 * 读取文件
 *  @param[in] file 文件编号,参见:::OpenFile
 *  @param[out] buffer 用于存放读取的文件内容
 *  @param[in] len 需要读取的文件长度
 *  @return 返回读取文件的长度
 *  – -1 表示读取文件失败
 
 *  @pre \e file 变量必须使用 ::OpenFile 返回值
 *  @pre \e buffer 不能为 NULL
 *  @see ::OpenFile ::WriteFile ::CloseFile
 */
int ReadFile(int file, char* buffer, int len);
/**//*
 * 写入文件
 *  @param[in] file 文件编号,参见:::OpenFile
 *  @param[in] buffer 用于存放将要写入的文件内容
 *  @param[in] len 需要写入的文件长度
 *  @return 返回写入的长度
 *  – -1 表示写入文件失败
 
 *  @pre \e file 变量必须使用 ::OpenFile 返回值
 *  @see ::OpenFile ::ReadFile ::CloseFile
 */
int WriteFile(int file, const char* buffer, int len);
/**//*
 * 关闭文件
 *  @param file 文件编号,参见:::OpenFile
 *  @retval 0  为成功
 *  @retval -1 表示失败
 
 *  @see ::OpenFile ::WriteFile ::ReadFile
 *  @deprecated 由于特殊的原因,这个函数可能会在将来的版本中取消。
 */
int CloseFile(int file);
/**//** @}*/ // 文件操作函数
/**//** @}*/ // 自动注释文档范例
生成的chm文档截图:

概念及作用
在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据。在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问,比如程序可能需要每个线程维护一个链表,而使用相同的函数操作,最简单的办法就是使用同名而不同变量地址的线程相关数据结构。这样的数据结构可以由Posix线程库维护,称为线程私有数据(Thread-specific Data,或TSD)。

创建和注销
Posix定义了两个API分别用来创建和注销TSD:

int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *))
 

该函数从TSD池中分配一项,将其值赋给key供以后访问使用。如果destr_function不为空,在线程退出(pthread_exit())时将以key所关联的数据为参数调用destr_function(),以释放分配的缓冲区。

不论哪个线程调用pthread_key_create(),所创建的key都是所有线程可访问的,但各个线程可根据自己的需要往key中填入不同的值,这就相当于提供了一个同名而不同值的全局变量。在LinuxThreads的实现中,TSD池用一个结构数组表示:

static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] = { { 0, NULL } };
 

创建一个TSD就相当于将结构数组中的某一项设置为"in_use",并将其索引返回给*key,然后设置destructor函数为destr_function。

注销一个TSD采用如下API:

int pthread_key_delete(pthread_key_t key)
 

这个函数并不检查当前是否有线程正使用该TSD,也不会调用清理函数(destr_function),而只是将TSD释放以供下一次调用pthread_key_create()使用。在LinuxThreads中,它还会将与之相关的线程数据项设为NULL(见"访问")。

访问
TSD的读写都通过专门的Posix Thread函数进行,其API定义如下:

int  pthread_setspecific(pthread_key_t  key,  const   void  *pointer)
void * pthread_getspecific(pthread_key_t key)

 

写入(pthread_setspecific())时,将pointer的值(不是所指的内容)与key相关联,而相应的读出函数则将与key相关联的数据读出来。数据类型都设为void *,因此可以指向任何类型的数据。

在LinuxThreads中,使用了一个位于线程描述结构(_pthread_descr_struct)中的二维void *指针数组来存放与key关联的数据,数组大小由以下几个宏来说明:

#define PTHREAD_KEY_2NDLEVEL_SIZE       32
#define PTHREAD_KEY_1STLEVEL_SIZE   \
((PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE – 1)
/ PTHREAD_KEY_2NDLEVEL_SIZE)
    其中在/usr/include/bits/local_lim.h中定义了PTHREAD_KEYS_MAX为1024,因此一维数组大小为32。而具体存放的位置由key值经过以下计算得到:
idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE
idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE
 

也就是说,数据存放与一个32×32的稀疏矩阵中。同样,访问的时候也由key值经过类似计算得到数据所在位置索引,再取出其中内容返回。

使用范例
以下这个例子没有什么实际意义,只是说明如何使用,以及能够使用这一机制达到存储线程私有数据的目的。

#include <stdio.h>
#include <pthread.h>

pthread_key_t   key;

void echomsg(int t)
{
        printf("destructor excuted in thread %d,param=%d\n",pthread_self(),t);
}

void * child1(void *arg)
{
        int tid=pthread_self();
        printf("thread %d enter\n",tid);
        pthread_setspecific(key,(void *)tid);
        sleep(2);
        printf("thread %d returns %d\n",tid,pthread_getspecific(key));
        sleep(5);
}

void * child2(void *arg)
{
        int tid=pthread_self();
        printf("thread %d enter\n",tid);
        pthread_setspecific(key,(void *)tid);
        sleep(1);
        printf("thread %d returns %d\n",tid,pthread_getspecific(key));
        sleep(5);
}

int main(void)
{
        int tid1,tid2;

        printf("hello\n");
        pthread_key_create(&key,echomsg);
        pthread_create(&tid1,NULL,child1,NULL);
        pthread_create(&tid2,NULL,child2,NULL);
        sleep(10);
        pthread_key_delete(key);
        printf("main thread exit\n");
        return 0;
}
 

给例程创建两个线程分别设置同一个线程私有数据为自己的线程ID,为了检验其私有性,程序错开了两个线程私有数据的写入和读出的时间,从程序运行结果可以看出,两个线程对TSD的修改互不干扰。同时,当线程退出时,清理函数会自动执行,参数为tid。

 

目前的互联网对于很多靠忽悠生存的创业者而言,可能是处于寒冬,但是对于真正实在做事的创业者,依然是春天,因为互联网上存在的机会还非常的多。所以会有越来越多的哥们踏入互联网行业来创业。

不过对于互联网创业者而言,我觉得他们最需要的并不是资金。这里用了“最需要”的三个字,表明资本并不是第一位的。为什么这样说呢?

回头看看这几年互联网的发展,融到大笔资金的创业者,最后失败的非常多,而没有融到大笔资金的众多创业者,做的成功的也非常的多。这说明资金不是决定项目成功最重要的因素,真正决定项目成败的还是创业者的能力,对于创业者而言,哪些能力高于资金之上呢?

1、赚钱的能力

我一直都认为,能赚小钱才能赚大钱,如果小钱都没有赚过,想一口吃成胖子的去突然成为亿万富翁,那只能成为天方夜谭式的梦想,是几乎不可能的。

想成为一个成功的创业者,给自己最低的底线不应该是失败,而应该是维持团队的生成和运营。所以作为一个互联网创业者,最需要的应该是赚钱的能力。哪怕只是赚小钱养活自己的团队,这个过程中,你的商业的理解就会逐步的深刻,等待你的内功修炼到一定层次的时候,做大就是很自然的事情了。

当你拥有了赚钱的能力以后,说明你已经非常实在可以赚到钱,这样你再创业的话,风险就会非常非常的小。

2、看钱的能力

对于互联网创业者而言,拥有了赚钱的能力后,一切可能将会变的有些平淡,但是互联网是一个梦想的舞台,任何创业者都有机会做的非常的大。所以对于互联网创业者而言,第二个需要具备的能力就是“看钱的能力!”

互联网上的机会还有非常多,机会背后都是钱,你能够看到这些钱么?

当你拥有了看钱的能力之后,只要你去按照正确的方法去做,成功就离你非常的近了。

如果你还没有赚钱的能力,你就认为自己已经拥有了看钱的能力的话,请你暂时不要相信自己的感觉。:)

3、用钱的能力

互联网创业可以低成本,但是发展到一定阶段,要飞速发展的时候,确实需要大量的资金,这时候,可能会有非常多的资本主动找上你,但是千万不要飘飘然。对于这时候的你,最重要依然不是资本,而是用钱的能力。

钱是工具,钱是一个像杠杆一样的工具,只有懂得用,才能获得大发展。但是不懂得用就盲目的用,后果就让你烦恼了。这两年我看到了好几家原来发展的很好的网站都是因为盲目的使用外来资本给自己带来了无尽的烦恼:

其中一家是一个电影网站,他们就靠传统的电影网站运作模式,非常的赚钱,最好的时候,每月的利润都在千万以上,但是后来视频概念火了,无数的资本开始涌入这个行业,于是不少资本看中了这个网站。但是这个网站在吸入资本之后,竟然一下找不到方向,可能迫于资本的压力,围绕着2.0的新概念瞎折腾。折腾一年之后,原来最赚钱的业务也渐渐失去了优势。

如果看钱能力火候不到,用钱能力还不足,盲目的纳入了外来的资本,结果会导致本来过的很舒服的你,一天比一天头疼。

幸福快乐的生活,比拥有更多的钱更重要,面对机会的时候,你一定根据自己的能力来选择。

做能力范围之内的事情,你会感觉到轻松愉快;

作稍稍超越你能力的事情,你会感觉到一种挑战的激情:

做能力范围之外的事情,短暂激情过后,更多的是苦不堪言。

互联网创业者最需要的不是资金,而是能力,当你具备以上三种能力之后,我相信资金对你就不是问题了,因为涌入互联网的资金会越来月多。前几年是海外资本,接下来几年将是中国民间资本。

在此祝广大的互联网创业者能力越来越强,生活越来越快乐!

 

2006年11月08日

//SvrOnlyOne.h
#ifndef _SVR_ONLY_ONE_H
#define _SVR_ONLY_ONE_H

const int SEM_PRMS = 0644;//信号量操作权限,0644即主用户(属主)可读写、组成员及其它成员可读不可写

class CSvrOnlyOne
{
public:
    bool Exists(key_t _nKey);//检查信号量是否已存在
    bool Mark(key_t _nKey);//设置信号量
    bool Unmark(key_t _nKey);//清除信号量
};

#endif //SVR_ONLY_ONE

//SvrOnlyOne.cpp
#include "ace/OS.h"
#include "ace/OS_NS_Thread.h"
#include "SvrOnlyOne.h"

bool CSvrOnlyOne::Exists(key_t _nKey)
{
    int nID = ACE_OS::semget(_nKey, 0, 0);
    if (nID == -1)
        return false;

    semun semctl_arg;
   
    semctl_arg.array = NULL;

    return ACE_OS::semctl(nID, 0, GETVAL, semctl_arg) > 0;
}

bool CSvrOnlyOne::Mark(key_t _nKey)
{
    int nID = ACE_OS::semget(_nKey, 0, 0);
    if (nID == -1)
        nID = ACE_OS::semget(_nKey, 1, IPC_CREAT | IPC_EXCL | SEM_PRMS);

    struct sembuf buf[2];

    buf[0].sem_num = 0;
    buf[0].sem_op = 0;
    buf[0].sem_flg = IPC_NOWAIT;
    buf[1].sem_num = 0;
    buf[1].sem_op = 1;
    buf[1].sem_flg = SEM_UNDO;//进程退出时自动回滚

    return ACE_OS::semop(nID, &buf[0], 2) == 0;
}

bool CSvrOnlyOne::Unmark(key_t _nKey)
{
    int nID = ACE_OS::semget(_nKey, 0, 0);
    if (nID == -1)
        return true;

    semun semctl_arg;

    semctl_arg.val = 0;

    return ACE_OS::semctl(nID, 0, IPC_RMID, semctl_arg) == 0;
}

 
  仔细想想地位卑贱的类型转换功能(cast),其在程序设计中的地位就象goto语句一样令人鄙视。但是它还不是无法令人忍受,因为当在某些紧要的关头,类型转换还是必需的,这时它是一个必需品。

  不过C风格的类型转换并不代表所有的类型转换功能。

  一来它们过于粗鲁,能允许你在任何类型之间进行转换。不过如果要进行更精确的类型转换,这会是一个优点。在这些类型转换中存在着巨大的不同,例如把一个指向const对象的指针(pointer-to-const-object)转换成指向非const对象的指针(pointer-to-non-const-object)(即一个仅仅去除const的类型转换),把一个指向基类的指针转换成指向子类的指针(即完全改变对象类型)。传统的C风格的类型转换不对上述两种转换进行区分。(这一点也不令人惊讶,因为C风格的类型转换是为C语言设计的,而不是为C++语言设计的)。

  二来C风格的类型转换在程序语句中难以识别。在语法上,类型转换由圆括号和标识符组成,而这些可以用在C++中的任何地方。这使得回答象这样一个最基本的有关类型转换的问题变得很困难:“在这个程序中是否使用了类型转换?”。这是因为人工阅读很可能忽略了类型转换的语句,而利用象grep的工具程序也不能从语句构成上区分出它们来。

  C++通过引进四个新的类型转换操作符克服了C风格类型转换的缺点,这四个操作符是, static_cast, const_cast, dynamic_cast, 和reinterpret_cast。在大多数情况下,对于这些操作符你只需要知道原来你习惯于这样写,

(type) expression

  而现在你总应该这样写:

static_cast<type>(expression)

  例如,假设你想把一个int转换成double,以便让包含int类型变量的表达式产生出浮点数值的结果。如果用C风格的类型转换,你能这样写:

int firstNumber, secondNumber;

double result = ((double)firstNumber)/secondNumber;

  如果用上述新的类型转换方法,你应该这样写:

double result = static_cast<double>(firstNumber)/secondNumber;

  这样的类型转换不论是对人工还是对程序都很容易识别。

   static_cast在功能上基本上与C风格的类型转换一样强大,含义也一样。它也有功能上限制。例如,你不能用static_cast象用C风格的类型转换一样把struct转换成int类型或者把double类型转换成指针类型,另外,static_cast不能从表达式中去除const属性,因为另一个新的类型转换操作符const_cast有这样的功能。
其它新的C++类型转换操作符被用在需要更多限制的地方。const_cast用于类型转换掉表达式的const或volatileness属性。通过使用const_cast,你向人们和编译器强调你通过类型转换想做的只是改变一些东西的constness或者volatileness属性。这个含义被编译器所约束。如果你试图使用const_cast来完成修改constness 或者volatileness属性之外的事情,你的类型转换将被拒绝。下面是一些例子:

class Widget { … };
class SpecialWidget: public Widget { … };
void update(SpecialWidget *psw);
SpecialWidget sw; // sw 是一个非const 对象。
const SpecialWidget& csw = sw; // csw 是sw的一个引用
// 它是一个const 对象
update(&csw); // 错误!不能传递一个const SpecialWidget* 变量
// 给一个处理SpecialWidget*类型变量的函数
update(const_cast<SpecialWidget*>(&csw));
// 正确,csw的const被显示地转换掉(
// csw和sw两个变量值在update
//函数中能被更新)
update((SpecialWidget*)&csw);
// 同上,但用了一个更难识别
//的C风格的类型转换
Widget *pw = new SpecialWidget;
update(pw); // 错误!pw的类型是Widget*,但是
// update函数处理的是SpecialWidget*类型
update(const_cast<SpecialWidget*>(pw));
// 错误!const_cast仅能被用在影响
// constness or volatileness的地方上。,
// 不能用在向继承子类进行类型转换。

  到目前为止,const_cast最普通的用途就是转换掉对象的const属性。

  第二种特殊的类型转换符是dynamic_cast,它被用于安全地沿着类的继承关系向下进行类型转换。这就是说,你能用dynamic_cast把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用,而且你能知道转换是否成功。失败的转换将返回空指针(当对指针进行类型转换时)或者抛出异常(当对引用进行类型转换时):

Widget *pw;

update(dynamic_cast<SpecialWidget*>(pw));
// 正确,传递给update函数一个指针
// 是指向变量类型为SpecialWidget的pw的指针
// 如果pw确实指向一个对象,
// 否则传递过去的将使空指针。
void updateViaRef(SpecialWidget& rsw);
updateViaRef(dynamic_cast<SpecialWidget&>(*pw));
//正确。传递给updateViaRef函数
// SpecialWidget pw 指针,如果pw
// 确实指向了某个对象
// 否则将抛出异常
dynamic_casts在帮助你浏览继承层次上是有限制的。它不能被用于缺乏虚函数的类型上(参见条款M24),也不能用它来转换掉constness:
int firstNumber, secondNumber;

double result = dynamic_cast<double>(firstNumber)/secondNumber;
// 错误!没有继承关系
const SpecialWidget sw;

update(dynamic_cast<SpecialWidget*>(&sw));
// 错误! dynamic_cast不能转换
// 掉const。

  如你想在没有继承关系的类型中进行转换,你可能想到static_cast。如果是为了去除const,你总得用const_cast。

  这四个类型转换符中的最后一个是reinterpret_cast。使用这个操作符的类型转换,其的转换结果几乎都是执行期定义(implementation-defined)。因此,使用reinterpret_casts的代码很难移植。

  reinterpret_casts的最普通的用途就是在函数指针类型之间进行转换。例如,假设你有一个函数指针数组:

typedef void (*FuncPtr)(); // FuncPtr is 一个指向函数
// 的指针,该函数没有参数
// 返回值类型为void
FuncPtr funcPtrArray[10]; // funcPtrArray 是一个能容纳
// 10个FuncPtrs指针的数组

  让我们假设你希望(因为某些莫名其妙的原因)把一个指向下面函数的指针存入funcPtrArray数组:

int doSomething();

  你不能不经过类型转换而直接去做,因为doSomething函数对于funcPtrArray数组来说有一个错误的类型。在FuncPtrArray数组里的函数返回值是void类型,而doSomething函数返回值是int类型。

funcPtrArray[0] = &doSomething; // 错误!类型不匹配
reinterpret_cast可以让你迫使编译器以你的方法去看待它们:
funcPtrArray[0] = // this compiles
reinterpret_cast<FuncPtr>(&doSomething);

  转换函数指针的代码是不可移植的(C++不保证所有的函数指针都被用一样的方法表示),在一些情况下这样的转换会产生不正确的结果(参见条款M31),所以你应该避免转换函数指针类型,除非你处于着背水一战和尖刀架喉的危急时刻。一把锋利的刀。一把非常锋利的刀。

  如果你使用的编译器缺乏对新的类型转换方式的支持,你可以用传统的类型转换方法代替static_cast, const_cast, 以及reinterpret_cast。也可以用下面的宏替换来模拟新的类型转换语法:

#define static_cast(TYPE,EXPR) ((TYPE)(EXPR))
#define const_cast(TYPE,EXPR) ((TYPE)(EXPR))
#define reinterpret_cast(TYPE,EXPR) ((TYPE)(EXPR))

  你可以象这样使用使用:

double result = static_cast(double, firstNumber)/secondNumber;
update(const_cast(SpecialWidget*, &sw));
funcPtrArray[0] = reinterpret_cast(FuncPtr, &doSomething);

  这些模拟不会象真实的操作符一样安全,但是当你的编译器可以支持新的的类型转换时,它们可以简化你把代码升级的过程。

  没有一个容易的方法来模拟dynamic_cast的操作,但是很多函数库提供了函数,安全地在派生类与基类之间进行类型转换。如果你没有这些函数而你有必须进行这样的类型转换,你也可以回到C风格的类型转换方法上,但是这样的话你将不能获知类型转换是否失败。当然,你也可以定义一个宏来模拟dynamic_cast的功能,就象模拟其它的类型转换一样:

#define dynamic_cast(TYPE,EXPR) (TYPE)(EXPR)

  请记住,这个模拟并不能完全实现dynamic_cast的功能,它没有办法知道转换是否失败。

  我知道,是的,我知道,新的类型转换操作符不是很美观而且用键盘键入也很麻烦。如果你发现它们看上去实在令人讨厌,C风格的类型转换还可以继续使用并且合法。然而,正是因为新的类型转换符缺乏美感才能使它弥补了在含义精确性和可辨认性上的缺点。并且,使用新类型转换符的程序更容易被解析(不论是对人工还是对于工具程序),它们允许编译器检测出原来不能发现的错误。这些都是放弃C风格类型转换方法的强有力的理由。还有第三个理由:也许让类型转换符不美观和键入麻烦是一件好事

 taglist:  用:TlistToggle 打开左边的tag窗口,再输一次自动关闭,按ctrl+w可以在窗口之间进行切换,也可以按照如下方法定义一个快捷键
  nnoremap <silent> <F8> :TlistToggle<CR><CR>
 tabbar:   按ESC+1,ESC+2,ESC+3, … ESC+0 在十个窗口切换,如果开了tag窗口,则可能要按一下回车,才能回到tabbar的当前编辑窗口
 SQLUtilities:这是一个用来格式化sql语言的,\sf 格式化当前行,如果sql语言占3行,则用3\sf

至此,基本工作已经完成了,你已经可以用vim来编辑一个ec文件,用ctrl+]来查找函数定义,用ctrl+t来返回,是不是很方便啊

下面列出我现在所用.vimrc,
let Tlist_Enable_Fold_Column = 0    "使taglist插件不显示左边的折叠行,
let Tlist_WinWidth = 20             "taglist窗口宽度
let Tlist_Show_One_File = 1         "taglist插件只显示当前文件的tag
let g:Tb_ForceSyntaxEnable = 1

colorscheme evening
set encoding=euc-cn                 "vim所使用的字符:euc-cn(simplified Chinese (Unix only)),如果有问题可以删除

set foldmethod=indent               "折叠使用indent风格
set foldlevel=10                    "多少层才自动折叠?[0m

set tabstop=4                       "tab占用4个字符宽度
set softtabstop=4
set shiftwidth=4
set expandtab                       "不使用tab,使用空格替代tab

set cinoptions=:N                   "使得自动缩进时swithc和case在同一列上,个人爱好,可以不设
set listchars=tab:>-,trail:-        "每个制表符会以 >— 显示1, 同时行尾空格以 – 显示, set list打开
" set whichwrap=h,l                
set nobackup                        " do not keep a backup file, use versions instead      

set cst
set csto=0
set cscopequickfix=s-,c-,d-,i-,t-,e-
cs add /u/infdev/cscope.out /u/infdev
set path+=~/include,../incl,../inc

nmap <silent> <F2>  :%s/\s\+$//<CR>         "删除行末多余空字符
nmap <silent> <F3>  [i                      "显示当前变量的定义
nmap <silent> <F4>  :TlistToggle<CR>        "打开tag窗口
nmap <silent> <F5>  gg=G                    "格式化整个文件,为误用,特意加shift键
nmap <silent> <F6>  =a{                     "格式化当前{}之内的文本
nmap <silent> <F7>  :cs find d <C-R>=expand("<cword>")<CR><CR>       "查找本函数调用的函数
nmap <silent> <F8>  :cs find c <C-R>=expand("<cword>")<CR><CR>       "查找调用指定函数的函数
nmap <silent> <F9>  :make<CR>               "make 当前目录的文件
nmap <silent> <F10> :cnext<CR>              "跳到下一个编译错误
nmap <silent> <F11> :cc<CR>                 "看完整的错误信息
nmap <silent> <F12> :clist<CR>              "看所有错误信息

下面附上常用的vim功能键