软件开发的六大阶段 (指针经典原创)
本阶段我们将组成企业项目调研组到企业进行现场调研,企业也部分需组织相应人员进行配合。整个调研工作将历时三星期到一个月左右时间。调研内容按以下方面进行。
1) 公司整体情况调研
目的:对企业的业务状况,组织结构,企业文化,发展战略进行仔细了解,从整体上对企业进行把握。
2) OA系统调研
目的:了解企业公文流转的方式,文档的管理,对使用OA系统的要求。在进行系统调研时,安派克公司将提供一个OA系统的原型供企业相关人员参考。
3) 人事工资系统调研
目的:了解企业现行的人事工资制度,将来可能的变化,对人事工资系统的要求。
4) 财务部门调研
目的:掌握财务部门对信息管理的要求。了解财务部门与公司其余部门的关系。特别是和销售,物资,生产部门之间的关系及其信息流动。
5) 行政部门调研
目的:了解行政部门的工作情况,在公司中的作用。了解行政部门和其它部门之间的关系,特别是和公司高级管理人员的关系。
6) 企业高级管理人员调研
目的:了解企业高级管理人员对各部门信息管理的要求,以及对公司各类信息收集、汇总整理的要求,想法。
7) 企业Internet网页调研 目的:了解企业需要如何在Internet上展示自己的企业形象。向用户提供何种服务,对代理商,经销商如何提供技术支持等方面的内容。
第二阶段:业务分析阶段
在完成第一阶段即调研阶段的工作后,我们将根据企业的目前状况,并且结合今后的发展情况,提出业务分析报告,供双方讨论确定。在此过程中如发现有争议,或不清楚的地方,还需要进行补充调研。
补充调研将采取电话,传真,E_MAIL等方式进行。待本报告的确认需经各具体业务部门确认。本分析阶段需要一到两个星期左右时间进行。
第三阶段:需求分析阶段
作为Intranet系统开发初期进行的需求分析阶段的工作,由我们的系统分析员主持。经过了解企业的要求,认真细致地调研、分析,最终建立企业Intranet系统的逻辑模型并写出系统的需求说明书。
需求分析在整个Intranet系统的开发过程中起着重要的作用,决定着系统开发的成败。在系统软件开发之前,首先应明确的是所要开发的软件应该具有哪些功能,应达到什么性能。明确了需求,就得到了系统设计开发的依据。
系统分析人员将到现场,全面了解用户的各项要求,澄清其中的模糊部分,对于哪些无法实现的要求,我们将与企业人员进行充分的协商,以得到一致的解决方案。
准确地表达被接受的用户要求也是需求分析的另一个重要方面,只有经过确切描述的要求才能成为设计的依据。我们最终将写出详尽的用户需求报告,提交给企业以确认。
1、现场调查研究: 调查研究是需求分析中掌握资料的基础工作。
2、确定需求: 确定需求就是要决定被开发的系统能够做什么,做到什么程度。这些需求包括:
A、功能需求 列出系统在功能上应该做到什么。这是最主要的需求。
B、性能需求 给出被开发系统工作时的技术性能指标。如响应时间、占用存储空间等。
C、可靠性需求 提出系统不发生故障的概率。发生故障的解决方案等。
D、安全和保密需求。
E、资源使用需求 指系统运行时所需数据量大小,所需内存及硬盘容量、网络传输速率等。
3、描述需求 已经确定下来的需求应该清晰、准确的描述。既编写系统需求说明书。
4、需求分析复核 作为需求分析阶段工作的复核,在需求分析的最后一步,应对功能的正确性、完整性和清晰性以及其他需求予以评价。
为保证系统开发的质量,复核应以企业与我们公司共同组成专门的审查小组进行审核。审核结束应有双方的结论意见及签字。后面的设计工作将完全以本需求报告为准。
第四阶段:系统设计阶段
1、系统结构的总体设计
决定系统的总体结构,包括整个系统分哪些部分,各部分之间有什么联系以及已确定的需求对这些组成部分如何分配等方面。
2、数据结构的设计
决定数据库系统的模式、子模式以及数据完整性、安全性设计。
3、完成用户使用手册的设计
设计用户手册的结构、内容及编写风格等。
4、制定初步的系统测试方案
对系统测试的策略、方法和步骤等提出明确的要求。
5、编写系统概要设计报告
6、概要设计评审
在以上几项工作完成以后,我们对系统概要设计报告进行审核。审核通过后,进行系统的详细设计。 详细的系统设计进程请见"项目进度表"
第五阶段:安装调试阶段
安装调试阶段实施的第一步是建立系统网络。网络的建立将由我们提供技术人员完成,他们将着重于检测该网络环境是否支持客户公司所选的系统,网络结构是否达到优化,是否可以使该系统稳定、高效地运行。
安装调试阶段实施的第二步是系统的安装。系统安装的复杂程度因系统本身的复杂性而异。我们将根据具体情况对安装过程做适当的调整,例如:一些小型财务软件的安装只需要十几分钟,而大型系统,如SAP、JDEdwards、Baan等,其安装需要事先周密计划,各单位统一安装、协调进行,在这期间,我们将要求客户公司提供全面的配合。
整个安装过程包括在系统实施前必须规化网络结构,根据业务量确定各个子网规模;其次是设置网络操作环境及通信协议;然后再安装各类数据库服务器、应用服务器及备份服务器;最后是系统客户端软件的安装…在此全过程中均会涉及到硬件/网络、软件/数据库等各方面的匹配。此时客户公司与我们公司之间的协调和沟通就显得尤为重要了,所以我们建议双方在开始安装之前对系统的各个环节进行统筹安排,从而保证成功地完成这项复杂的系统安装。
我们的这套EAI系统将把系统的"客户化"放在首位,客户公司可以根据自身的特点来对系统中预留的各项参数进行设置。鉴于只有将企业的特点与软件的功能紧密结合才能使软件功能得到最大限度的发挥,所以参数的设置将在客户公司需求的基础上,结合我们的技术建议,经双方共同讨论后最后决定。
第六阶段:技术培训阶段
为企业提供必要的系统维护培训(二至三名维护人员,为期一周),使其了解该系统的所有功能并熟练掌握整个系统的操作和日常维护,同时,我们公司负责为企业培训系统开发人员(二至三名,为期一周),使其掌握必要的开发技能。
当系统安装、调试完成,参数设置校验无误后,咨询人员将安排贵公司的培训,培训将根据用户在系统中的权限定义及责任范围分批分组进行。
应该强调的是,我们为贵公司员工的的培训不是仅针对系统的操作者,同时还注重培训用户自己的系统维护人员。由于该软件功能模块较多,同时为了满足贵公司让至少两名工作人员参与整个系统实施的全过程,包括用户培训,以便对整个系统的运作有全面的了解。在试运行/支持维护系统实施完毕后,需要有几个月的试运行,这是一个发现问题和解决问题的反复过程。我们也将在此过程中对系统的设置作进一步考核,同时对用户进行进一步的培训。
总之,我们所提供的培训将帮助贵公司建立自己的内部协调及系统维护工作机制,以便最终提高系统的运行效率。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=601443
面试十大难题的样板回答
在面试前先了解、练习对几个很难的问题进行回答会帮助你对其他问题的回答做准备。有的问题问得比较多,有的较少但却是回答其它问题的基础。
1、为什么不谈谈你自己?
分析:这是个开放性问题。从哪里谈起都行,但是滔滔不绝地讲上一两个小时可不是雇主所希望的。这样的问题是测验你是否能选择重点并且把它清楚、流畅地表达出来。显然,提问者想让你把你的背景和想要得到的位置联系起来。
回答对策:有几个基本的方法。一个是直接简要回答所问的问题,另一个是在回答前要求把问题问得更明确。在上述两种情况下,你都要很快地把你的答案转到你的技能、经验和你为得到目前这份工作接受的培训上来。
回答样板:“我来自一个小家庭,有一个弟弟,父母都还在工作。中学毕业后,我攻读市场营销学士。日间在一家商业机构担任行销执行员,学了不少管理方面的知识。 例如,我全权负责的一个批发销售公司的业务,销售总额一年为200万美元。在那里我学习到怎么管理人事,在压力下解决问题。我希望能更好的运用我的技能。我相信我的经验和学历将让我迎向未来更大的挑战。”
评语:只简单的介绍了个人历史,很快的将重点话题转到与工作有关的技能和经验上来。你也可请面谈者把他确实想了解的东西集中到一点,如你可问:“你是不是想知道我受过的教育,或者与工作有关的技术和经验?”等,大多雇主都会乐意告诉你他们感兴趣的是什么?
2、我为什么要雇用你?
分析:这是个直接、正面的问题,尽管这个问题不会问得这样明确,但是会在其它问题之后被提出来,这个问题没有隐含的意思。
回答对策:直接的问题需要直接了当回答,为什么他们要雇用你呢?最巧妙的回答对他们而不是对你有利。这个问题会使你向他们提供证据以证实你可以帮助他们改进工作效率,降低成本、增加销售、解决问题(如准时上班,改进对顾客的服务、组织一个或多个管理工作等)。
回答样板:“我是个经验丰富的经理,在员工队伍建设方面,从组织项目的实施到鼓励员工合作,我得心应手。多年来,我已经掌握了一套雇人和留人的技巧。此外,我还擅长帮助公司顺利实现技术改造和员工培训。我经常对主要客户进行示范讲解,我们的销售额在过去两年平均增加了87%。”
评语:在回答中,以实例提供有力的证据,直接而自信地推销自己。
3、你有哪些主要的优点?
分析:像前面问题一样,这个问题问得相当直接,但是有一点隐含。
回答对策:你的回答应当首先强调你适应的或已具有的技能。雇用你的决定在很大程度上取决于这些技能,你可以在后面详细介绍你与工作有关的技能。回答时, 一定要简单扼要。
回答样板:“我具有朝著目标努力工作的能力。一旦我下定决心做某事,我就要把它做好,例如,我的志愿是成为一个出色的公关经理,我喜欢接触不同的人,服务人群,为了实现这个目标。我目前正在修读有关课程。”
评语:如“我的学习能力、适应能力很强。”“人际关系很好”等都是可提出的优点,但尽可能要提供与工作相关的证据,这会使你与众不同。
4、你有哪些主要的缺点?
分析:这是个棘手的问题。若照实的回答,你会毁了工作,雇主试图使你处于不利的境地,观察你在类似的工作困境中将作出什么反应。
回答对策:回答这样的问题应诚实。完满地回答应该是用简洁正面的介绍抵消反面的问题。
回答样板1:“工人们指责我对工作太投入。我经常提前一点上班安排好我的工作,晚上晚一点下班,使要干的事得以完成。”
回答样板2:“我需要学会更耐心一点。我的性子比较急,我总要我的工作赶在第一时间完成。我不能容忍工作怠慢。”
评语:回答的虽是自身的缺点,但却表现了正面的效果,对工作的积极抵消了反面。
5、你想得到的薪水是多少?
分析:如果你对薪酬的要求太低,那显然贬低自己的能力;如果你对薪酬的要求太高,那又会显得你分量过重,公司受用不起。一些雇主通常都事先对求聘的职位定下开支预算,因而他们第一次提出的价钱往往是他们所能给予的最高价钱。他们问你只不过想证实一下这笔钱是否足以引起你对该工作的兴趣。
回答对策:在商谈薪酬之前,你已经调查了解了自己所从事工作的合理的市场价值。在与对方商谈时,不妨尽可能插入“合理的和市场价值”语汇。记得,商谈时降低原来的开价轻而易举,但一旦开出低价后想再提上去就难乎其难。
回答样板1:如果你尚未彻底表现自我价值,面试者就提此问题考你,你不妨参考以下答案:
“钱不是我唯一关心的事。我想先谈谈我对贵公司所能做的贡献--如果您允许的话。”
“我对工资没有硬性要求。我相信贵公司在处理我的问题上会友善合理。我注重的是找对工作机会,所以只要条件公平,我则不会计较太多。”
回答样板2:如果你已经阐明该职位的重要性,可是对方仍旧告诉你给你的报酬已是最好的。您不妨指出它的工作性质实际上值得你获得更高的报酬;阐明你将如何通过努力缩减公司的开支;说明在工作中你得自我承担哪些费用等,以证明你对公司的价值,和表明你要求更高报酬是以你的工作表现为前提的。
但是如果对方不愿妥协,在你未得到肯定的工作答复之前,不要使雇主排除对你的考虑。你可以问:‘你们决定雇用我了吗?”如果答案是肯定的,报酬却使你不愿接受,你可以这样拒绝:
“谢谢你给我提供工作机会。这个职位我很想的到,但是,工资比我想要的低,这是我无法接受这份工作的原因之一。也许你会重新考虑,或者以后能有使我对你们更有价值的工作时再考虑我。”
评语:即使拒绝对方,也要为协商留有余地。如果雇主需要你,他会乐于满足你的要求。一旦你对他们提出的标准说“不”,交易就做不成了
6、你以前的经验和我们现在的工作有哪些联系?
分析:这个提问要求你在与其它求职者进行比较时,你要克服你背景中显示出来的任何弱点。
回答对策:首先要介绍你的优势。假如其它求职者明显地比你受的教育多,工作经验多或知识多,那么你就要介绍你的优势。
回答样板1:“如你所知,我刚刚结束电脑编程方面的加强培训。另外,我在企业方面有三年多的工作经验,其中包括在老板不在时管理小型企业。我在那学会了处理财务及基本的会计工作。我还盘算和管理过价值30万美元的产品。这些经历帮我认识企业使用电脑编程的作用。虽然我刚接触编程工作,我对电脑语言是熟悉的。我受的教育是全面的,我有300多个小时的电脑操作时间,这是我课程的一部分。因为我是新手,我决心比别人更努力地工作,以便及时完成任务。”
评语:这种回答强调了可转换性的技能(会计工作知识)和适应性技能(按时完成任务,更努力工作)。这对缺乏工作经验的程序员来说是必要的。在这种情况下,在学校学的知识也非常重要,也要像“正式”工作那样予以强调。
回答样板2:“在以前的工作中,我使用过很多与做好这项工作所需要的相同的技术。尽管是不同的企业,但管理企业都需要有我具有的组织和监督能力。在过去的七年里,我使我的部门成为我们公司最赢利的部门之一。在我工作期间,每年销售额平均上升30%,利润也提高30%。由于这是个老公司,这样的业绩是很不一般的,七年中我得到两次晋升,并很快地荣升到管理层。我想在你们这样小的、发展型公司接受挑战,我感到我的经验为我走向这一步做好了准备。”
评语:回答者明白以前的工作领域与现在考虑的不同,但是,他强调了成绩和以前的成功。为完成这项工作,各种管理技术都会用到。回答中还谈到继续接受小公司工作挑战的动力。
7、你对以后有什么打算?
分析:这个问题是在考察你的工作动机。它是在探究是否可以信赖你把工作长久地干下去,而且干得努力。
回答对策:你最好的对策就是诚实。这是一贯强调的。我并非是要你把负面的信息也摆出来,你应该准备坦率地、正面地回答雇主关心的问题。而哪些是雇主关心的问题取决于你介绍个人背景的具体情况。
例如:
你对工作满意吗? (如果不满意你会离开公司吗?)
你想成家吗? (如果成家,你会停职去照料小孩吗?)
你是否有过短期工作后离开的历史?(如果有,你会不会也放弃这份工作呢?)
你是否刚搬到此地,是临时的或暂住人口? (如果是,你也不会在此地久居,对吗?)
你是否有比本工作要求更好的条件?(如果是,是什么使你不去高就呢?)
你有什么优势和承诺在工作中发展吗?(如果不是,谁需要一个没有优势和动力的人呢?)
有什么原因使你感到不满吗? (如果有,雇主自然会设法搞清楚。)
回答样板1:对于一个刚刚参加工作的人,他可以这样回答:
“我认识到要在这一领域造就自己,我很愿意从此开始。我想过我要做什么,而且肯定我的技能正是做好这项工作所需要的。例如,我善于与人打交道。在我过去的一项工作中,我每周向1000多名不同的人提供服务。在我18个月的工作中,我曾为72000多名顾客提供服务,从未得到一次正式的投诉。事实上,他们常因我的周到服务表扬我。我认识到我喜欢与公众接触,想到我能得到这份工作感到非常愉快。我想在工作中更好地学习,并与之共同进步。由于我对公司的贡献和价值不断提高,我希望能考虑使我得到更有责任的职务。”
评语:雇主想了解你会长期工作下去并努力工作。这样的回答使对此表示关注的雇主感到安慰。(注意,这样的回答可以在快餐店工作获得的经验为背景。)
回答样板2:对没有工作经验和只有各种短期工作经验的人,他们可以这样回答:
“我做过几种工作(一种或失业),我认识到应该珍视体面的、稳定的工作。我的各种经验是一种财富,我学到很多东西,我可以把它们用于这项工作中去。我正在寻找一份可以安定下来,努力工作并持久下去的工作。”
评语:这是一种可以接受的回答,只是回答太短,也没有提供证据。介绍自己的实例最好放在最后一句话之前。有些职务,如销售方面的工作,要求你有勃勃雄心,或者说是咄咄勇气。其它工作有对工作领域或专门机构的要求。你不会总能预料到雇主想要什么。如果你能正确地做,你就会具有任何工作要求的条件,而这一切只需要你用嘴讲出来,就是这么简单。
8、你以前的雇主(教师、介绍人、管理员等)对你的评价如何?
分析:这个问题与雇主的第二种期望有关。雇主想知道你的适应性技能--你是否容易相处,你是否是个好工人等等。你以前的雇主可能会谈到你存在的问题,当然,也可能不谈。你知道,许多雇主会在雇用你之前查阅你的证明信,如果你在面谈时谈的与你以前的雇主说的不一样,你就要倒霉了。
回答对策:一定要与你以前的雇主讨论你的求职计划,也要征求你介绍人的意见。要明确地告诉他们你想找的工作种类以及你准备做好新工作的理由。假如以前的雇主会说一些不利于你的话,你要和他开诚布公地谈谈,看他会说写什么。
如果你是被解雇或被迫辞职的,你可以向未来的雇主进行辩解。有很多成功的人与前雇主发生过冲突,如果能把这些冲突尽可能地讲出来,许多面谈者是会理解的。对和你关系不好的旧雇主,明智的办法是请他写一份文字证明材料,在这种情况下,他们不会给你极为不利的信。大的公司一般不接受电话提供证明材料,这可以使你大大地松一口气,只要给公司打个电话就清楚了。
如果可能的话,使用那些说你好话的证明信。要是你的前任老板不愿这么做,找个愿意帮忙你的人便行了。如果你被解雇了,最好的对策是实话实说。但是对你的前任老板不要太苛刻,这样会让人觉得你是个好抱怨而无责任感的人。再者,你也不是一点错也没有。要先承认有这么回事,接着要趁机谈谈你从中得到的教训。
回答样板:“我的三个前雇主都会说我工作努力,可靠、忠实,我离开那里是因为个人冲突。为此我深深地感到烦恼,只有放弃那里的工作。你可以给他们打电话,他们对我的评价是肯定。我认为还是向你们谈谈为好,我仍然尊敬他。我在那得到了几次晋升的机会,但是,随着我权力的增加,冲突也越发地多起来。我们主要是不同类型的人。我不知道问题会有那么严重,因为我一心只想工作。这是我的错,我认识到我应该更加注意人际关系的处理。”
评语:回答中介绍了一些正面的技能,并用具体事例加以说明,因而是有力的。
9、你为什么要找这样的职位?为什么是在这里?
分析:雇主想了解是否你是那种无论什么公司有活就行的人。果真如此,他或她就不会对你感兴趣。雇主想找那种想解决工作中问题的人。他们有理由认为这样的人工作起来更努力,更有效率,而那些想去特别的公司工作的人也是如此。
回答对策:事先了解哪些工作适合你的技能和兴趣非常重要。要回答这个问题,就要谈到你选择工作目标的动机,那项工作要求的而你又具备的技能,各种专门培训,或与职务有关的教育证书。
这个问题实际上有两方面的含意。一是为什么选择这个职位,二是为什么选择这个公司。如果你有选择这个公司的理由,或选择这个公司是你最大愿望,你就要准备回答为什么。如果可能的话,在面谈前,你要事先尽可能地对它进行了解。与别人联系得到详细的情报,或到图书馆查阅,看公司的年度报告,或任何能使你了解情况的方法都是必要的。
回答样板:“我花费了很多时间考虑各种职业的可能性,我认为这方面的工作最适合我,原因是这项工作要求的许多技能都是我擅长的。举例来说,分析问题和解决问题是我的强项,在以前的工作中我能比别人更早发现和解决问题。有一次,我提出一项计划使得租借设备的退货率减少了15%,这听起来不算高,但是取得了年增长25000美元的好效益。而成本仅为100美元。目前你们公司似乎是能让我施展解决问题能力的地方。这个公司工作运行良好,发展迅速,善于接受新思想。你们的销售去年上涨了30%,而且你们准备引进几项大型新产品。如果我在这里努力工作,证实我自身的价值,我感到我有机会与公司共同发展。
评语:这种回答巧妙地运用了“提供证据”技巧,这样的话符合一个出色的经理或优秀的秘书的身份。
10、为什么不讲一讲你个人的情况?
分析:一个好的面谈者很少这样直接地提出这个问题,通过随意的、友好的谈话也可以得到想了解的情况。在大多数情况下,面谈者会竭力地打探证明你不稳定或不可靠的信息。
回答对策:还有其它一些可能使某个雇主关注的问题,以上问题只是对某些性格的人的推测。这都是些不相关的问题,但是,如果雇主想以此来了解你可否可靠,你就得全力以赴地去应付了。要记住即使是随意地闲谈也要避免提及隐晦的问题。在回答个人情况时,要态度友好而且自信。
回答样板:
有小孩子的家:“我有两个小孩,都在上学。他们和我的一个好朋友在一起,照料孩子不成问题。”
一人主家:“我没有结婚,但是我有两个孩子。对我来说有一份稳定的收入很重要,照料孩子不成为问题。”
年轻、单身:“我没有结婚,即使结婚,我也不会改变做专职工作的打算,我可以把全部精力用在工作上。”
新搬来的:“我决定在Depression Culch 长期居住下来,我租了一套公寓,搬家公司的六辆车正在卸家俱。”
抚养人:“我有个愉快的童年,我父母住的地方离我只需一小时飞机的路程,我一年去看他们几次。”
闲暇时间:“在我不去上班时,我主要呆在家里。我爱参加社区组织的活动,我每周都要在教堂参加活动。”
评语:上述回答都可以扩展开,可以做为你回答问题时的参考。这里要告诉面谈者的是你个人的情况不影响你的工作能力,而且,确实还能对你有帮助。如果你的个人生活会扰乱你的工作,想必雇主也会很快对你失去耐心的。这不是他们的问题,也不应该成为他们的问题。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=572207
教你理解复杂的C/C++声明
作者:Vikram A Punathambekar
介绍
曾经碰到过让你迷惑不解、类似于int * (* (*fp1) (int) ) [10];这样的变量声明吗?本文将由易到难,一步一步教会你如何理解这种复杂的C/C++声明:我们将从每天都能碰到的较简单的声明入手,然后逐步加入const修饰符和typedef,还有函数指针,最后介绍一个能够让你准确地理解任何C/C++声明的“右左法则”。需要强调一下的是,复杂的C/C++声明并不是好的编程风格;我这里仅仅是教你如何去理解这些声明。
基础
让我们从一个非常简单的例子开始,如下:
int n;
这个应该被理解为“declare n as an int”(n是一个int型的变量)。
接下去来看一下指针变量,如下:
int *p;
这个应该被理解为“declare p as an int *”(p是一个int *型的变量),或者说p是一个指向一个int型变量的指针。我想在这里展开讨论一下:我觉得在声明一个指针(或引用)类型的变量时,最好将*(或&)写在紧靠变量之前,而不是紧跟基本类型之后。这样可以避免一些理解上的误区,比如:
int* p,q;
第一眼看去,好像是p和q都是int*类型的,但事实上,只有p是一个指针,而q是一个最简单的int型变量。
我们还是继续我们前面的话题,再来看一个指针的指针的例子:
char **argv;
理论上,对于指针的级数没有限制,你可以定义一个浮点类型变量的指针的指针的指针的指针...
再来看如下的声明:
int RollNum[30][4];
int (*p)[4]=RollNum;
int *q[5];
这里,p被声明为一个指向一个4元素(int类型)数组的指针,而q被声明为一个包含5个元素(int类型的指针)的数组。
另外,我们还可以在同一个声明中混合实用*和&,如下:
int **p1; // p1 is a pointer to a pointer to an int.
int *&p2; // p2 is a reference to a pointer to an int.
int &*p3; // ERROR: Pointer to a reference is illegal.
int &&p4; // ERROR: Reference to a reference is illegal.
注:p1是一个int类型的指针的指针;p2是一个int类型的指针的引用;p3是一个int类型引用的指针(不合法!);p4是一个int类型引用的引用(不合法!)。
const修饰符
当你想阻止一个变量被改变,可能会用到const关键字。在你给一个变量加上const修饰符的同时,通常需要对它进行初始化,因为以后的任何时候你将没有机会再去改变它。例如:
const int n=5;
int const m=10;
上述两个变量n和m其实是同一种类型的--都是const int(整形恒量)。因为C++标准规定,const关键字放在类型或变量名之前等价的。我个人更喜欢第一种声明方式,因为它更突出了const修饰符的作用。
当const与指针一起使用时,容易让人感到迷惑。例如,我们来看一下下面的p和q的声明:
const int *p;
int const *q;
他们当中哪一个代表const int类型的指针(const直接修饰int),哪一个代表int类型的const指针(const直接修饰指针)?实际上,p和q都被声明为const int类型的指针。而int类型的const指针应该这样声明:
int * const r= &n; // n has been declared as an int
这里,p和q都是指向const int类型的指针,也就是说,你在以后的程序里不能改变*p的值。而r是一个const指针,它在声明的时候被初始化指向变量n(即r=&n;)之后,r的值将不再允许被改变(但*r的值可以改变)。
组合上述两种const修饰的情况,我们来声明一个指向const int类型的const指针,如下:
const int * const p=&n // n has been declared as const int
下面给出的一些关于const的声明,将帮助你彻底理清const的用法。不过请注意,下面的一些声明是不能被编译通过的,因为他们需要在声明的同时进行初始化。为了简洁起见,我忽略了初始化部分;因为加入初始化代码的话,下面每个声明都将增加两行代码。
char ** p1; // pointer to pointer to char
const char **p2; // pointer to pointer to const char
char * const * p3; // pointer to const pointer to char
const char * const * p4; // pointer to const pointer to const char
char ** const p5; // const pointer to pointer to char
const char ** const p6; // const pointer to pointer to const char
char * const * const p7; // const pointer to const pointer to char
const char * const * const p8; // const pointer to const pointer to const char
注:p1是指向char类型的指针的指针;p2是指向const char类型的指针的指针;p3是指向char类型的const指针;p4是指向const char类型的const指针;p5是指向char类型的指针的const指针;p6是指向const char类型的指针的const指针;p7是指向char类型const指针的const指针;p8是指向const char类型的const指针的const指针。
typedef的妙用
typedef给你一种方式来克服“*只适合于变量而不适合于类型”的弊端。你可以如下使用typedef:
typedef char * PCHAR;
PCHAR p,q;
这里的p和q都被声明为指针。(如果不使用typedef,q将被声明为一个char变量,这跟我们的第一眼感觉不太一致!)下面有一些使用typedef的声明,并且给出了解释:
typedef char * a; // a is a pointer to a char
typedef a b(); // b is a function that returns
// a pointer to a char
typedef b *c; // c is a pointer to a function
// that returns a pointer to a char
typedef c d(); // d is a function returning
// a pointer to a function
// that returns a pointer to a char
typedef d *e; // e is a pointer to a function
// returning a pointer to a
// function that returns a
// pointer to a char
e var[10]; // var is an array of 10 pointers to
// functions returning pointers to
// functions returning pointers to chars.
typedef经常用在一个结构声明之前,如下。这样,当创建结构变量的时候,允许你不使用关键字struct(在C中,创建结构变量时要求使用struct关键字,如struct tagPOINT a;而在C++中,struct可以忽略,如tagPOINT b)。
typedef struct tagPOINT
{
int x;
int y;
}POINT;
POINT p; /* Valid C code */
函数指针
函数指针可能是最容易引起理解上的困惑的声明。函数指针在DOS时代写TSR程序时用得最多;在Win32和X-Windows时代,他们被用在需要回调函数的场合。当然,还有其它很多地方需要用到函数指针:虚函数表,STL中的一些模板,Win NT/2K/XP系统服务等。让我们来看一个函数指针的简单例子:
int (*p)(char);
这里p被声明为一个函数指针,这个函数带一个char类型的参数,并且有一个int类型的返回值。另外,带有两个float类型参数、返回值是char类型的指针的指针的函数指针可以声明如下:
char ** (*p)(float, float);
那么,带两个char类型的const指针参数、无返回值的函数指针又该如何声明呢?参考如下:
void * (*a[5])(char * const, char * const);
“右左法则”[重要!!!]
The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.
这是一个简单的法则,但能让你准确理解所有的声明。这个法则运用如下:从最内部的括号开始阅读声明,向右看,然后向左看。当你碰到一个括号时就调转阅读的方向。括号内的所有内容都分析完毕就跳出括号的范围。这样继续,直到整个声明都被分析完毕。
对上述“右左法则”做一个小小的修正:当你第一次开始阅读声明的时候,你必须从变量名开始,而不是从最内部的括号。
下面结合例子来演示一下“右左法则”的使用。
int * (* (*fp1) (int) ) [10];
阅读步骤:
1. 从变量名开始 -------------------------------------------- fp1
2. 往右看,什么也没有,碰到了),因此往左看,碰到一个* ------ 一个指针
3. 跳出括号,碰到了(int) ----------------------------------- 一个带一个int参数的函数
4. 向左看,发现一个* --------------------------------------- (函数)返回一个指针
5. 跳出括号,向右看,碰到[10] ------------------------------ 一个10元素的数组
6. 向左看,发现一个* --------------------------------------- 指针
7. 向左看,发现int ----------------------------------------- int类型
总结:fp1被声明成为一个函数的指针,该函数返回指向指针数组的指针.
再来看一个例子:
int *( *( *arr[5])())();
阅读步骤:
1. 从变量名开始 -------------------------------------------- arr
2. 往右看,发现是一个数组 ---------------------------------- 一个5元素的数组
3. 向左看,发现一个* --------------------------------------- 指针
4. 跳出括号,向右看,发现() -------------------------------- 不带参数的函数
5. 向左看,碰到* ------------------------------------------- (函数)返回一个指针
6. 跳出括号,向右发现() ------------------------------------ 不带参数的函数
7. 向左,发现* --------------------------------------------- (函数)返回一个指针
8. 继续向左,发现int --------------------------------------- int类型
总结:??
还有更多的例子:
float ( * ( *b()) [] )(); // b is a function that returns a
// pointer to an array of pointers
// to functions returning floats.
void * ( *c) ( char, int (*)()); // c is a pointer to a function that takes
// two parameters:
// a char and a pointer to a
// function that takes no
// parameters and returns
// an int
// and returns a pointer to void.
void ** (*d) (int &,
char **(*)(char *, char **)); // d is a pointer to a function that takes
// two parameters:
// a reference to an int and a pointer
// to a function that takes two parameters:
// a pointer to a char and a pointer
// to a pointer to a char
// and returns a pointer to a pointer
// to a char
// and returns a pointer to a pointer to void
float ( * ( * e[10])
(int &) ) [5]; // e is an array of 10 pointers to
// functions that take a single
// reference to an int as an argument
// and return pointers to
// an array of 5 floats.
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=569339
这是一篇我所见过的关于指针的最好的入门级文章,它可使初学者在很短的时间内掌握复杂的指针操作。虽然,现在的JAVA、C#等语言已经取消了指针,但作为一个C++程序员,指针的直接操作内存,在数据操作方面有着速度快,节约内存等优点,仍是很多C++程序员的最爱。指针就像是一把良剑,就看你怎么去应用它!
什么是指针?
其实指针就像是其它变量一样,所不同的是一般的变量包含的是实际的真实的数据,而指针是一个指示器,它告诉程序在内存的哪块区域可以找到数据。这是一个非常重要的概念,有很多程序和算法都是围绕指针而设计的,如链表。
开始学习
如何定义一个指针呢?就像你定义一个其它变量一样,只不过你要在指针名字前加上一个星号。我们来看一个例子:
下面这个程序定义了两个指针,它们都是指向整型数据。
int* pNumberOne;
int* pNumberTwo;
你注意到在两个变量名前的“p”前缀了吗?这是程序员通常在定义指针时的一个习惯,以提高便程序的阅读性,表示这是个指针。现在让我们来初始化这两个指针:
pNumberOne = &some_number;
pNumberTwo = &some_other_number;
&号读作“什么的地址”,它表示返回的是变量在内存中的地址而不是变量本身的值。在这个例子中,pNumberOne 等于some_number的地址,所以现在pNumberOne指向some_number。 如果现在我们在程序中要用到some_number,我们就可以使用pNumberOne。
我们来学习一个例子:
在这个例子中你将学到很多,如果你对指针的概念一点都不了解,我建议你多看几遍这个例子,指针是个很复杂的东西,但你会很快掌握它的。
这个例子用以增强你对上面所介绍内容的了解。它是用C编写的(注:原英文版是用C写的代码,译者重新用C++改写写了所有代码,并在DEV C++ 和VC++中编译通过!)
#include <iostream.h>
void main()
{
// 声明变量:
int nNumber;
int *pPointer;
// 现在给它们赋值:
nNumber = 15;
pPointer = &nNumber;
//打印出变量nNumber的值:
cout<<"nNumber is equal to :"<< nNumber<<endl;
// 现在通过指针改变nNumber的值:
*pPointer = 25;
//证明nNumber已经被上面的程序改变
//重新打印出nNumber的值:
cout<<"nNumber is equal to :"<<nNumber<<endl;
}
通读一下这个程序,编译并运行它,务必明白它是怎样工作的。如果你完成了,准备好,开始下一小节。
陷井!
试一下,你能找出下面这段程序的错误吗?
#include <iostream.h>
int *pPointer;
void SomeFunction();
{
int nNumber;
nNumber = 25;
//让指针指向nNumber:
pPointer = &nNumber;
}
void main()
{
SomeFunction(); //为pPointer赋值
//为什么这里失败了?为什么没有得到25
cout<<"Value of *pPointer: "<<*pPointer<<endl;
}
这段程序先调用了SomeFunction函数,创建了个叫nNumber的变量,接着让指针pPointer指向了它。可是问题出在哪儿呢?当函数结束后,nNumber被删掉了,因为这一个局部变量。局部变量在定义它的函数执行完后都会被系统自动删掉。也就是说当SomeFunction 函数返回主函数main()时,这个变量已经被删掉,但pPointer还指着变量曾经用过的但现在已不属于这个程序的区域。如果你还不明白,你可以再读读这个程序,注意它的局部变量和全局变量,这些概念都非常重要。
但这个问题怎么解决呢?答案是动态分配技术。注意这在C和C++中是不同的。由于大多数程序员都是用C++,所以我用到的是C++中常用的称谓。
动态分配
动态分配是指针的关键技术。它是用来在不必定义变量的情况下分配内存和让指针去指向它们。尽管这么说可能会让你迷惑,其实它真的很简单。下面的代码就是一个为一个整型数据分配内存的例子:
int *pNumber;
pNumber = new int;
第一行声明一个指针pNumber。第二行为一个整型数据分配一个内存空间,并让pNumber指向这个新内存空间。下面是一个新例,这一次是用double双精型:
double *pDouble;
pDouble = new double;
这种格式是一个规则,这样写你是不会错的。
但动态分配又和前面的例子有什么不同呢?就是在函数返回或执行完毕时,你分配的这块内存区域是不会被删除的所以我们现在可以用动态分配重写上面的程序:
#include <iostream.h>
int *pPointer;
void SomeFunction()
{
// 让指针指向一个新的整型
pPointer = new int;
*pPointer = 25;
}
void main()
{
SomeFunction(); // 为pPointer赋值
cout<<"Value of *pPointer: "<<*pPointer<<endl;
}
通读这个程序,编译并运行它,务必理解它是怎样工作的。当SomeFunction 调用时,它分配了一个内存,并让pPointer指向它。这一次,当函数返回时,新的内存区域被保留下来,所以pPointer始终指着有用的信息,这是因为了动态分配。但是你再仔细读读上面这个程序,虽然它得到了正确结果,可仍有一个严重的错误。
分配了内存,别忘了回收
太复杂了,怎么会还有严重的错误!其实要改正并不难。问题是:你动态地分配了一个内存空间,可它绝不会被自动删除。也就是说,这块内存空间会一直存在,直到你告诉电脑你已经使用完了。可结果是,你并没有告诉电脑你已不再需要这块内存空间了,所以它会继续占据着内存空间造成浪费,甚至你的程序运行完毕,其它程序运行时它还存在。当这样的问题积累到一定程度,最终将导致系统崩溃。所以这是很重要的,在你用完它以后,请释放它的空间,如:
delete pPointer;
这样就差不多了,你不得不小心。在这你终止了一个有效的指针(一个确实指向某个内存的指针)。
下面的程序,它不会浪费任何的内存:
#include <iostream.h>
int *pPointer;
void SomeFunction()
{
// 让指针指向一个新的整型
pPointer = new int;
*pPointer = 25;
}
void main()
{
SomeFunction(); //为pPointer赋值
cout<<"Value of *pPointer: "<<*pPointer<<endl;
delete pPointer;
}
只有一行与前一个程序不同,但就是这最后一行十分地重要。如果你不删除它,你就会制造一起“内存漏洞”,而让内存逐渐地泄漏。
(译者:假如在程序中调用了两次SomeFunction,你又该如何修改这个程序呢?请读者自己思考)
传递指针到函数
传递指针到函数是非常有用的,也很容易掌握。如果我们写一个程序,让一个数加上5,看一看这个程序完整吗?:
#include <iostream.h>
void AddFive(int Number)
{
Number = Number + 5;
}
void main()
{
int nMyNumber = 18;
cout<<"My original number is "<<nMyNumber<<endl;
AddFive(nMyNumber);
cout<<"My new number is "<<nMyNumber<<endl;
//得到了结果23吗?问题出在哪儿?
}
问题出在函数AddFive里用到的Number是变量nMyNumber的一个副本而传递给函数,而不是变量本身。因此, " Number = Number + 5" 这一行是把变量的副本加了5,而原始的变量在主函数main()里依然没变。试着运行这个程序,自己去体会一下。
要解决这个问题,我们就要传递一个指针到函数,所以我们要修改一下函数让它能接受指针:把'void AddFive(int Number)' 改成 'void AddFive(int* Number)' 。下面就是改过的程序,注意函数调用时要用&号,以表示传递的是指针:
#include <iostream.h>
void AddFive(int* Number)
{
*Number = *Number + 5;
}
void main()
{
int nMyNumber = 18;
cout<<"My original number is "<<nMyNumber<<endl;
AddFive(&nMyNumber);
cout<<"My new number is "<<nMyNumber<<endl;
}
试着自己去运行它,注意在函数AddFive的参数Number前加*号的重要性:它告诉编译器,我们是把指针所指的变量加5。而不并指针自己加5。
最后,如果想让函数返回指针的话,你可以这么写:
int * MyFunction();
在这句里,MyFunction返回一个指向整型的指针。
指向类的指针
指针在类中的操作要格外小心,你可以用如下的办法定义一个类:
class MyClass
{
public:
int m_Number;
char m_Character;
};
接着你就可以定义一个MyClass 类的变量了:
MyClass thing;
你应该已经知道怎样去定义一个指针了吧:
MyClass *thing;
接着你可以分配个内存空间给它:
thing = new MyClass;
注意,问题出现了。你打算怎样使用这个指针呢,通常你可能会写'thing.m_Number',但是thing是类吗,不,它是一个指向类的指针,它本身并不包含一个叫m_Number的变量。所以我们必须用另一种方法:就是把'.'(点号)换成 -> ,来看下面的例子:
class MyClass
{
public:
int m_Number;
char m_Character;
};
void main()
{
MyClass *pPointer;
pPointer = new MyClass;
pPointer->m_Number = 10;
pPointer->m_Character = 's';
delete pPointer;
}
指向数组的指针
你也可以让指针指向一个数组,按下面的方法操作:
int *pArray;
pArray = new int[6];
程序会创建一个指针pArray,让它指向一个有六个元素的数组。另外一种方法,不用动态分配:
int *pArray;
int MyArray[6];
pArray = &MyArray[0];
注意,&MyArray[0] 也可以简写成 MyArray ,都表示是数组的第一个元素地址。但如果写成pArray = &MyArray可能就会出问题,结果是 pArray 指向的是指向数组的指针(在一维数组中尽管与&MyArray[0]相等),而不是你想要的,在多维数组中很容易出错。
在数组中使用指针
一旦你定义了一个指向数组的指针,你该怎样使用它呢?让我们来看一个例子,一个指向整型数组的指针:
#include <iostream.h>
void main()
{
int Array[3];
Array[0] = 10;
Array[1] = 20;
Array[2] = 30;
int *pArray;
pArray = &Array[0];
cout<<"pArray points to the value %d\n"<<*pArray<<endl;
}
如果让指针指向数组元素中的下一个,可以用pArray++.也可以用你应该能想到的pArray + 1,都会让指针指向数组的下一个元素。要注意的是你在移动指针时,程序并不检查你是否已经移动地超出了你定义的数组,也就是说你很可能通过上面的简单指针加操作而访问到数组以外的数据,而结果就是,可能会使系统崩溃,所以请格外小心。
当然有了pArray + 1,也可以有pArray - 1,这种操作在循环中很常用,特别是while循环中。
另一个需要注意的是,如果你定义了一个指向整型数的指针:int* pNumberSet ,你可以把它当作是数组,如:pNumberSet[0] 和 *pNumberSet是相等的,pNumberSet[1]与*(pNumberSet + 1)也是相等的。
在这一节的最后提一个警告:如果你用 new 动态地分配了一个数组,
int *pArray;
pArray = new int[6];
别忘了回收,
delete[] pArray;
这一句是告诉编译器是删除整个数组而不一个单独的元素。千万记住了。
后话
还有一点要小心,别删除一个根本就没分配内存的指针,典型的是如果没用new分配,就别用delete:
void main()
{
int number;
int *pNumber = number;
delete pNumber; // 错误 - *pNumber 没有用new动态分配内存.
}
常见问题解答
Q:为什么我在编译程序时老是在 new 和 delete语句中出现'symbol undefined' 错误?
A:new 和 delete都是C++在C上的扩展,这个错误是说编译器认为你现在的程序是C而不C++,当然会出错了。看看你的文件名是不是.cpp结尾。
Q:new 和 malloc有什么不同?
A:new 是C++中的关健字,用来分配内存的一个标准函数。如果没有必要,请不要在C++中使用malloc。因为malloc是C中的语法,它不是为面向对象的C++而设计的。
Q:我可以同时使用free 和 delete吗?
A:你应该注意的是,它们各自所匹配的操作不同。free只用在用malloc分配的内存操作中,而delete只用在用new分配的内存操作中。
引用(写给某些有能力的读者)
这一节的内容不是我的这篇文章的中心,只是供某些有能力的读者参考。
有些读者经常问我关于引用和指针的问题,这里我简要地讨论一下。
在前面指针的学习中,我们知道(&)是读作“什么的地址”,但在下面的程序中,它是读作“什么的引用”
int& Number = myOtherNumber;
Number = 25;
引用有点像是一个指向myOtherNumber的指针,不同的是它是自动删除的。所以他比指针在某些场合更有用。与上面等价的代码是:
int* pNumber = &myOtherNumber;
*pNumber = 25;
指针与引用另一个不同是你不能修改你已经定义好的引用,也就是说你不能改变它在声明时所指的内容。举个例子:
int myFirstNumber = 25;
int mySecondNumber = 20;
int &myReference = myFirstNumber;
myReference = mySecondNumber;//这一步能使myReference 改变吗?
cout<<myFristNumber<<endl;//结果是20还是25?
当在类中操作时,引用的值必须在构造函数中设定,例:
CMyClass::CMyClass(int &variable) : m_MyReferenceInCMyClass(variable)
{
// constructor code here
}
总结
这篇文章开始可能会较难掌握,所以最好是多读几遍。有些读者暂时还不能理解,在这儿我再做一个简要的总结:
指针是一个指向内存区域的变量,定义时在变量名前加上星号(*)(如:int *number)。
你可以得到任何一个变量的地址,只在变量名前加上&(如:pNumber = &my_number)。
你可以用'new' 关键字动态分配内存。指针的类型必须与它所指的变量类型一样(如:int *number 就不能指向 MyClass)。
你可以传递一个指针到函数。必须用'delete'删除你动态分配的内存。
你可以用&array[0]而让指针指向一个数组。
你必须用delete[]而不是delete来删除动态分配的数组。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=568002