﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-江南小生-文章分类-C++</title><link>http://www.cppblog.com/fashion1840/category/1129.html</link><description>一个才子的Blog</description><language>zh-cn</language><lastBuildDate>Tue, 01 Jul 2008 07:43:22 GMT</lastBuildDate><pubDate>Tue, 01 Jul 2008 07:43:22 GMT</pubDate><ttl>60</ttl><item><title>C++资源之不完全导引</title><link>http://www.cppblog.com/fashion1840/articles/4223.html</link><dc:creator>江南小生</dc:creator><author>江南小生</author><pubDate>Thu, 16 Mar 2006 02:07:00 GMT</pubDate><guid>http://www.cppblog.com/fashion1840/articles/4223.html</guid><wfw:comment>http://www.cppblog.com/fashion1840/comments/4223.html</wfw:comment><comments>http://www.cppblog.com/fashion1840/articles/4223.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/fashion1840/comments/commentRss/4223.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/fashion1840/services/trackbacks/4223.html</trackback:ping><description><![CDATA[<DIV class=postbody twffan="done">
<P>C++资源之不完全导引</P>
<P>　　1，前言 <BR>&nbsp; <BR>　　无数次听到“我要开始学习C++!”的呐喊，无数次听到“C++太复杂了，我真的学不会”的无奈。Stan Lippman先生曾在《C++ Primer》一书中指出“C++是最为难学的高级程序设计语言之一”，人们常将“之一”去掉以表达自己对C++的敬畏。诚然，C++程序设计语言对于学习者的确有很多难以逾越的鸿沟，体系结构的庞大，应接不暇并不断扩充的特性……除此之外，参考资料之多与冗杂使它的学习者望而却步，欲求深入者苦不堪言。希望这一份不完全导引能够成为您C++学习之路上的引路灯。 <BR>&nbsp; <BR>　　撰写本文的初衷并不打算带领大家体验古老的C++历史，如果你想了解C++的历史与其前期发展中诸多技术的演变，你应当去参考Bjarne的《The Design and Evolution of C++》。当然也不打算给大家一个无所不包的宝典（并非不想：其一是因水平有限，其二无奈C++之博大精深），所给出的仅仅是一些我们认为对于想学习C++的广大读者来说最重要并且触手可及的开发与学习资源。 <BR>&nbsp; <BR>　　本文介绍并分析了一些编译器，开发环境，库，少量的书籍以及参考网站，并且尽可能尝试着给出一个利用这些资源的导引，望对如同我们一样的初学者能够有所裨益。 <BR>&nbsp; <BR>-------------------------------------------------------------------------------- <BR>&nbsp; <BR>　　2，编译器 <BR>&nbsp; <BR>　　在C++之外的任何语言中，编译器都从来没有受到过如此之重视。因为C++是一门相当复杂的语言，所以编译器也难于构建。直到最近我们才开始能够使用上完全符合C++标准的编译器（哦，你可能会责怪那些编译器厂商不能尽早的提供符合标准的编译器，这只能怪他们各自维系着自身的一套别人不愿接受的标准）。什么？你说这无关紧要？哦，不，你所需要的是和标准化C++高度兼容的编译环境。长远来看，只有这样的编译器对C++开发人员来说才是最有意义的工具，尤其是对于程序设计语言的学习者。一至性让代码具备可移植性，并让一门语言及其库的应用更为广泛。嗯，是的，我们这里只打算介绍一些公认的优秀编译器。 <BR>&nbsp; <BR>　　2.1 Borland C++ <BR>&nbsp; <BR>　　这个是Borland C++ Builder和Borland C++ Builder X这两种开发环境的后台编译器。（哦，我之所以将之分为两种开发环境你应当能明白为什么，正如Delphi 7到Delphi8的转变，是革命性的两代。）Borland C++由老牌开发工具厂商Borland 倾力打造。该公司的编译器素以速度快，空间效率高著称，Borland C++ 系列编译 器秉承了这个传统，属于非常优质的编译器。标准化方面早在5.5版本的编译器中对标准化C++的兼容就达到92.73%。目前最新版本是Borland C++ Builder X中的6.0版本，官方称100%符合ANSI/ISO的C++标准以及C99标准。嗯…这正是我前面所指的“完全符合C++标准的编译器”。 <BR>&nbsp; <BR>　　2.2 Visual C++ <BR>&nbsp; <BR>　　这个正是我们熟知的Visual Studio 和 Visual Studio.net 2002, 2003以及2005 Whidbey中带的C++编译器。由Microsoft公司研制。在Visual Studio 6.0中， <BR>因为编译器有太多地方不能与后来出现的C++标准相吻合而饱受批评（想想你在使用STL的时候编译时报出的那些令人厌恶的error和warning吧）。VC++6.0对标准化C+ +的兼容只有83.43%。但是随着C++编译器设计大师Stanley Lippman以及诸多C++社群达人的加盟，在Visual Studio.NET 2003中，Visual C++编译器已经成为一个非 常成熟可靠的C++编译器了。Dr.Dobb's Journal的评测显示Visual C++7.1对标准C++的兼容性高达98.22%，一度成为CBX之前兼容性最好的编译器。结合强大的Visua l Studio.NET开发环境，是一个非常不错的选择。至于Whidbey时代的Visual C++, 似乎微软所最关注的是C++/CLI……我们不想评论微软下一代的C++编译器对标准化 兼容如何，但他确实越来越适合.NET (其实你和我的感觉可能是一样的，微软不应当把标准C++这块肥肉丢给Borland,然而微软可能并不这样认为)。 <BR>&nbsp; <BR>　　2.3 GNU C++ <BR>&nbsp; <BR>　　著名的开源C++编译器。是类Unix操作系统下编写C++程序的首选。特点是有非常好的移植性，你可以在非常广泛的平台上使用它，同时也是编写跨平台，嵌入式 程序很好的选择。另外在符合标准这个方面一直都非常好，GCC3.3大概能够达到96.15%。但是由于其跨平台的特性，在代码尺寸速度等优化上略微差一点。 <BR>&nbsp; <BR>　　基于GNU C++的编译器有很多，比如： <BR>&nbsp; <BR>　　(1) Mingw <BR>&nbsp; <BR>　　<A href="http://www.mingw.org/"><FONT color=#1d58d1>http://www.mingw.org/</FONT></A> <BR>&nbsp; <BR>　　GCC的一个Windows的移植版本（Dev-C++的后台） <BR>&nbsp; <BR>　　(2) Cygwin <BR>&nbsp; <BR>　　<A href="http://sources.redhat.com/cygwin/"><FONT color=#1d58d1>http://sources.redhat.com/cygwin/</FONT></A> <BR>&nbsp; <BR>　　GCC的另外一个Windows移植版本是Cygwin的一部分，Cygwin是Windows下的一个Unix仿真环境。严格的说是模拟GNU的环境，这也就是"Gnu's Not Unix"要表达的意思，噢，扯远了，这并不是我们在这里关心的实质内容。 <BR>&nbsp; <BR>　　(3) Djgpp <BR>&nbsp; <BR>　　<A href="http://www.delorie.com/djgpp/"><FONT color=#1d58d1>http://www.delorie.com/djgpp/</FONT></A> <BR>&nbsp; <BR>　　这是GCC的DOS移植版本。 <BR>&nbsp; <BR>　　(4) RSXNT <BR>&nbsp; <BR>　　<A href="http://www.mathematik.uni-bielefeld.de/~rainer/"><FONT color=#1d58d1>http://www.mathematik.uni-bielefeld.de/~rainer/</FONT></A> <BR>&nbsp; <BR>　　这是GCC的DOS和Windows移植版本。 <BR>&nbsp; <BR>　　(5) Intel C++ <BR>&nbsp; <BR>　　著名CPU制造厂商Intel出品的编译器，Special Design for Intel x86！对于Intel x86结构的CPU经过特别的优化。在有些应用情况下，特别是数值计算等高性能应用，仅仅采用Intel的编译器编译就能大幅度的提高性能。 <BR>&nbsp; <BR>　　(6) Digital Mars C++ <BR>&nbsp; <BR>　　网络上提供免费下载，Zortech/Symantec C++的继承者，其前身在当年惨烈的C++四国战中也是主角之一。 <BR>&nbsp; <BR>-------------------------------------------------------------------------------- <BR>&nbsp; <BR>　　3，开发环境 <BR>&nbsp; <BR>　　开发环境对于程序员的作用不言而喻。选择自己朝夕相处的环境也不是容易的事情，特别是在IDE如此丰富的情况下。下面就是我们推荐的一些常见的C++开发环境，并没有包括一些小型的，罕见的IDE。其中任何一款都是功能丰富，可以用作日常开发使用的。对于不同层面的开发者，请参见内文关于适用对象的描述。 <BR>&nbsp; <BR>　　3.1 Visual Studio 6.0 <BR>&nbsp; <BR>　　这个虽然是Microsoft公司的老版本的开发环境，但是鉴于其后继版本Visual Studio.NET的庞大身躯，以及初学者并不那么高的功能要求，所以推荐这个开发环境给C++的初学者，供其学习C++的最基本的部分，比如C的那部分子集，当然你别指望他能够支持最新的C99标准。在日常的开发中，仍然有很多公司使用这个经典稳定的环境，比如笔者就看曾亲见有些公司将其编译器替换为GCC做手机开发之用。 <BR>&nbsp; <BR>　　3.2 Visual Studio.NET 2003 <BR>&nbsp; <BR>　　作为Microsoft公司官方正式发布的最新版本开发环境，其中有太多激动人心的功能。结合其最新的C++编译器。对于机器配置比较好的开发人员来说，使用这个开发环境将能满足其大部分的要求。这里不打算单独说Visual Studio Whidbey,虽然Visual Studio .NET 2005 - Whidbey社区预览版已经推出，但暂不是很稳定，读者可以亲身去体验。 <BR>&nbsp; <BR>　　3.3 Borland C++ Builder 6 <BR>&nbsp; <BR>　　这个并不是Borland的C++开发环境的最新版本。选择它的原因是它不是用Java写的IDE，速度比较快。它有一个很完善的GUI窗体设计器，和Delphi共用一个VCL。由于这些特点，比较适合初学者上手。但是由于其GUI的中心位置，可能不利于对于C++语言的学习。而且其为了支持VCL这个Object Pascal写的库也对C++进行了一些私有的扩充。使得人们有一个不得不接受的事实：“Borland C++ Builder 6的高手几乎都是Delphi高手”。 <BR>&nbsp; <BR>　　3.4 Borland C++ Builder X <BR>&nbsp; <BR>　　正如前文所述，虽然版本号上和前面那个IDE非常相象，但是其实它们是完全不同的两个集成开发环境。C++Builder更多的是一个和Delphi同步的C++版本的开发环境，C++BuilderX则是完全从C++的角度思考得出的一个功能丰富的IDE。其最大的特点是跨平台，跨编译器，多种Framework的集成，并且有一个WxWindows为基础的GUI设计器。尤其是采用了纯C++来重写了整个Framework,摒弃了以前令人无奈的版本。对于C++的开发来说，从编译器，到库，到功能集成都是非常理想的。可以预见，Borland C++ Builder X 2.0很值得C++爱好者期待。唯一令人难堪之处是作为一个 <BR>C++的开发工具，其IDE是用Java写的，在配置不够理想的机器上请慎重考虑再安装 <BR>。 <BR>&nbsp; <BR>　　3.5 Emacs + GCC <BR>&nbsp; <BR>　　前面讲的大部分是Windows环境下的集成开发环境。Linux上的开发者更倾向于使用Emacs来编辑C++的文件，用Makefile来命令GCC做编译。虽然看上去比较松散，但是这些东西综合起来还是一个开0发环境。如果你能够娴熟的使用这样的环境写程序，你的水平应该足够指导我们来写这篇陋文了。 <BR>&nbsp; <BR>　　3.6 Dev C++ <BR>&nbsp; <BR>　　GCC是一个很好的编译器。在Windows上的C++编译器一直和标准有着一段距离的时候，GCC就是一个让Windows下开发者流口水的编译器。Dev-C++就是能够让GCC跑在Windows下的工具，作为集成开发环境，还提供了同专业IDE相媲美的语法高亮，代码提示，调试等功能。由于使用Delphi开发，占用内存少，速度很快，比较适合轻量级的学习和使用。 <BR>&nbsp; <BR>　　3.7 Eclipse + CDT <BR>&nbsp; <BR>　　Eclipse可是近来大名鼎鼎的开发工具。最新一期的Jolt大奖就颁给了这个杰出的神物。说其神奇是因为，它本身是用Java写的，但是拥有比一般Java写的程序快得多的速度。而且因为其基于插件组装一切的原则，使得能够有CDT这样的插件把Eclipse变成一个C/C++的开发环境。如果你一直用Eclipse写Java的程序，不妨用它体验一下C++开发的乐趣。 <BR>&nbsp; <BR>------------------------------------------------------------------------ <BR>-------- <BR>&nbsp; <BR>　　4，工具 <BR>&nbsp; <BR>　　C++的辅助工具繁多，我们分门别类的为大家作介绍： <BR>&nbsp; <BR>　　4.1 文档类 <BR>&nbsp; <BR>　　(1) Doxygen <BR>&nbsp; <BR>　　参考站点：<A href="http://www.doxygen.org/"><FONT color=#1d58d1>http://www.doxygen.org</FONT></A> <BR>&nbsp; <BR>　　Doxygen是一种适合C风格语言（如C++、C、IDL、Java甚至包括C#和PHP）的、开放源码的、基于命令行的文档产生器。 <BR>&nbsp; <BR>　　(2) C++2HTML <BR>&nbsp; <BR>　　参考站点：<A href="http://www.bedaux.net/cpp2html/"><FONT color=#1d58d1>http://www.bedaux.net/cpp2html/</FONT></A> <BR>&nbsp; <BR>　　把C++代码变成语法高亮的HTML <BR>&nbsp; <BR>　　(3) CodeColorizer <BR>&nbsp; <BR>　　参考站点：<A href="http://www.chami.com/colorizer/"><FONT color=#1d58d1>http://www.chami.com/colorizer/</FONT></A> <BR>&nbsp; <BR>　　它能把好几种语言的源代码着色为HTML <BR>&nbsp; <BR>　　(4) Doc-O-Matic <BR>&nbsp; <BR>　　参考站点：<A href="http://www.doc-o-matic.com/"><FONT color=#1d58d1>http://www.doc-o-matic.com/</FONT></A> <BR>&nbsp; <BR>　　Doc-O_Matic为你的C/C++，C++.net，Delphi/Pascal, VB.NET，C#和Java程序或者组件产生准确的文档。Doc-O-Matic使用源代码中的符号和注释以及外部的文档文件创建与流行的文档样式一致的文档。 <BR>&nbsp; <BR>　　(5) DocVizor <BR>&nbsp; <BR>　　参考站点：<A href="http://www.ucancode.net/Products/DocBuilder/Features.htm"><FONT color=#1d58d1>http://www.ucancode.net/Products/DocBuilder/Features.htm</FONT></A> <BR>&nbsp; <BR>　　DocVizor满足了面向对象软件开发者的基本要求——它让我们能够看到C++工程中的类层次结构。DocVizor快速地产生完整可供打印的类层次结构图，包括从第三方库中来的那些类，除此之外DocVizor还能从类信息中产生HTML文件。 <BR>&nbsp; <BR>　　(6) SourcePublisher C++ <BR>&nbsp; <BR>　　参考站点：<A href="http://www.scitools.com/sourcepublisher_c.html"><FONT color=#1d58d1>http://www.scitools.com/sourcepublisher_c.html</FONT></A> <BR>&nbsp; <BR>　　给源代码产生提供快速直观的HTML报表，包括代码，类层次结构，调用和被调用树，包含和被包含树。支持多种操作系统。 <BR>&nbsp; <BR>　　(7) Understand <BR>&nbsp; <BR>　　参考站点：<A href="http://www.scitools.com/ucpp.html"><FONT color=#1d58d1>http://www.scitools.com/ucpp.html</FONT></A> <BR>&nbsp; <BR>　　分析任何规模的C或者C++工程，帮助我们更好的理解以及编写文档。 <BR>&nbsp; <BR>　　4.2 代码类 <BR>&nbsp; <BR>　　(1) CC-Rider <BR>&nbsp; <BR>　　参考站点：<A href="http://www.cc-rider.com/"><FONT color=#1d58d1>http://www.cc-rider.com</FONT></A> <BR>&nbsp; <BR>　　CC-Rider是用于C/C++程序强大的代码可视化工具，通过交互式浏览、编辑及自动文件来促进程序的维持和发展。 <BR>&nbsp; <BR>　　(2) CodeInspect <BR>&nbsp; <BR>　　参考站点：<A href="http://www.yokasoft.com/"><FONT color=#1d58d1>http://www.yokasoft.com/</FONT></A> <BR>&nbsp; <BR>　　一种新的C/C++代码分析工具。它检查我们的源代码找出非标准的，可能的，以及普通的错误代码。 <BR>&nbsp; <BR>　　(3) CodeWizard <BR>&nbsp; <BR>　　参考站点：<A href="http://www.parasoft.com/"><FONT color=#1d58d1>http://www.parasoft.com</FONT></A> <BR>&nbsp; <BR>　　先进的C/C++源代码分析工具，使用超过500个编码规范自动化地标明危险的，但是编译器不能检查到的代码结构。 <BR>&nbsp; <BR>　　(4) C++ Validation Test Suites <BR>&nbsp; <BR>　　参考站点：<A href="http://www.plumhall.com/suites.html"><FONT color=#1d58d1>http://www.plumhall.com/suites.html</FONT></A> <BR>&nbsp; <BR>　　一组用于测试编译器和库对于标准吻合程度的代码库。 <BR>&nbsp; <BR>　　(5) CppRefactory <BR>&nbsp; <BR>　　参考站点：<A href="http://cpptool.sourceforge.net/"><FONT color=#1d58d1>http://cpptool.sourceforge.net/</FONT></A> <BR>&nbsp; <BR>　　CPPRefactory是一个使得开发者能够重构他们的C++代码的程序。目的是使得C++代码的重构能够尽可能的有效率和简单。 <BR>&nbsp; <BR>　　(6) Lzz <BR>&nbsp; <BR>　　参考站点：<A href="http://www.lazycplusplus.com/"><FONT color=#1d58d1>http://www.lazycplusplus.com/</FONT></A> <BR>&nbsp; <BR>　　Lzz是一个自动化许多C++编程中的体力活的工具。它能够节省我们许多事件并且使得编码更加有乐趣。给出一系列的声明，Lzz会给我们创建头文件和源文件。 <BR>&nbsp; <BR>　　(7) QA C++ Generation 2000 <BR>&nbsp; <BR>　　参考站点：<A href="http://www.programmingresearch.com/solutions/qacpp.htm"><FONT color=#1d58d1>http://www.programmingresearch.com/solutions/qacpp.htm</FONT></A> <BR>&nbsp; <BR>　　它关注面向对象的C++源代码，对有关于设计，效率，可靠性，可维护性的部分提出警告信息。 <BR>&nbsp; <BR>　　(8) s-mail project - Java to C++DOL <BR>&nbsp; <BR>　　参考站点：<A href="http://sadlocha.strefa.pl/s-mail/ja2dol.html"><FONT color=#1d58d1>http://sadlocha.strefa.pl/s-mail/ja2dol.html</FONT></A> <BR>&nbsp; <BR>　　把Java源代码翻译为相应的C++源代码的命令行工具。 <BR>&nbsp; <BR>　　(9) SNIP from Cleanscape Software International <BR>&nbsp; <BR>　　参考站点：<A href="http://www.cleanscape.net/stdprod/snip/index.html"><FONT color=#1d58d1>http://www.cleanscape.net/stdprod/snip/index.html</FONT></A> <BR>&nbsp; <BR>　　一个填平编码和设计之间沟壑的易于使用的C++开发工具，节省大量编辑和调试的事件，它还使得开发者能够指定设计模式作为对象模型，自动从对象模型中产生C++的类。 <BR>&nbsp; <BR>　　(10) SourceStyler C++ <BR>&nbsp; <BR>　　参考站点：<A href="http://www.ochresoftware.com/"><FONT color=#1d58d1>http://www.ochresoftware.com/</FONT></A> <BR>&nbsp; <BR>　　对C/C++源代码提供完整的格式化和排版控制的工具。提供多于75个的格式化选项以及完全支持ANSI C++。 <BR>&nbsp; <BR>　　4.3 编译类 <BR>&nbsp; <BR>　　(1) Compilercache <BR>&nbsp; <BR>　　参考站点：<A href="http://www.erikyyy.de/compilercache/"><FONT color=#1d58d1>http://www.erikyyy.de/compilercache/</FONT></A> <BR>&nbsp; <BR>　　Compilercache是一个对你的C和C++编译器的封装脚本。每次我们进行编译，封装脚本，把编译的结果放入缓存，一旦编译相同的东西，结果将从缓存中取出而不是再次编译。 <BR>&nbsp; <BR>　　(2) Ccache <BR>&nbsp; <BR>　　参考站点：<A href="http://ccache.samba.org/"><FONT color=#1d58d1>http://ccache.samba.org/</FONT></A> <BR>&nbsp; <BR>　　Ccache是一个编译器缓存。它使用起来就像C/C++编译器的缓存预处理器，编译速度通常能提高普通编译过程的5~10倍。 <BR>&nbsp; <BR>　　(3) Cmm (C++ with MultiMethods) <BR>&nbsp; <BR>　　参考站点：<A href="http://www.op59.net/cmm/cmm-0.28/users.html"><FONT color=#1d58d1>http://www.op59.net/cmm/cmm-0.28/users.html</FONT></A> <BR>&nbsp; <BR>　　这是一种C++语言的扩展。读入Cmm源代码输出C++的源代码，功能是对C++语言添加了对multimethod的支持。 <BR>&nbsp; <BR>　　(4) The Frost Project <BR>&nbsp; <BR>　　参考站点：<A href="http://frost.flewid.de/"><FONT color=#1d58d1>http://frost.flewid.de/</FONT></A> <BR>&nbsp; <BR>　　Forst使得你能够在C++程序中像原生的C++特性一样使用multimethod以及虚函数参数。它是一个编译器的外壳。 <BR>&nbsp; <BR>　　4.4 测试和调试类 <BR>&nbsp; <BR>　　(1) CPPUnit <BR>&nbsp; <BR>　　CppUnit 是个基于 LGPL 的开源项目，最初版本移植自 JUnit，是一个非常优秀的开源测试框架。CppUnit 和 JUnit 一样主要思想来源于极限编程。主要功能就是对单元测试进行管理，并可进行自动化测试。 <BR>&nbsp; <BR>　　(2) C++Test <BR>&nbsp; <BR>　　参考站点：<A href="http://www.parasoft.com/"><FONT color=#1d58d1>http://www.parasoft.com/</FONT></A> <BR>&nbsp; <BR>　　C++ Test是一个单元测试工具，它自动化了C和C++类，函数或者组件的测试。 <BR>&nbsp; <BR>&nbsp; <BR>　　(3) Cantata++ <BR>&nbsp; <BR>　　参考站点：<A href="http://www.iplbath.com/products/tools/pt400.shtml"><FONT color=#1d58d1>http://www.iplbath.com/products/tools/pt400.shtml</FONT></A> <BR>&nbsp; <BR>　　设计的目的是为了满足在合理的经济开销下使用这个工具可以让开发工程师开展单元测试和集成测试的需求. <BR>&nbsp; <BR>　　(4) Purify <BR>&nbsp; <BR>　　参考站点：<A href="http://www-900.ibm.com/cn/software/rational/products/purif"><FONT color=#1d58d1>http://www-900.ibm.com/cn/software/rational/products/purif</FONT></A> <BR>yplus/index.shtml <BR>&nbsp; <BR>　　IBM Rational PurifyPlus是一套完整的运行时分析工具，旨在提高应用程序的可靠性和性能。PurifyPlus将内存错误和泄漏检测、应用程序性能描述、代码覆盖分析等功能组合在一个单一、完整的工具包中。 <BR>&nbsp; <BR>　　(5) BoundsChecker <BR>&nbsp; <BR>　　BoundsChecker是一个C++运行时错误检测和调试工具。它通过在Visual Studio内自动化调试过程加速开发并且缩短上市的周期。BoundsChecker提供清楚，详细的程序错误分析，许多是对C++独有的并且在static，stack和heap内存中检测和诊断错误，以及发现内存和资源的泄漏。　　<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (6) Insure++ <BR>&nbsp; <BR>　　参考站点：<A href="http://www.parasoft.com/"><FONT color=#1d58d1>http://www.parasoft.com/</FONT></A> <BR>&nbsp; <BR>　　一个自动化的运行时程序测试工具，检查难以察觉的错误,如内存覆盖，内存泄漏，内存分配错误，变量初始化错误，变量定义冲突，指针错误，库错误，逻辑错误和算法错误等。 <BR>&nbsp; <BR>　　(7) GlowCode <BR>&nbsp; <BR>　　参考站点：<A href="http://www.glowcode.com/"><FONT color=#1d58d1>http://www.glowcode.com/</FONT></A> <BR>&nbsp; <BR>　　GlowCode包括内存泄漏检查，code profiler，函数调用跟踪等功能。给C++开发者提供完整的错误诊断，和运行时性能分析工具包。 <BR>&nbsp; <BR>　　(8) Stack Spy <BR>&nbsp; <BR>　　参考站点：<A href="http://www.imperioustech.com/"><FONT color=#1d58d1>http://www.imperioustech.com/</FONT></A> <BR>&nbsp; <BR>　　它能捕捉stack corruption, stack over run, stack overflow等有关栈的错误。 <BR>&nbsp; <BR>------------------------------------------------------------------------------ <BR>-- <BR>&nbsp; <BR>　　5，库 <BR>&nbsp; <BR>　　在C++中，库的地位是非常高的。C++之父 Bjarne Stroustrup先生多次表示了设计库来扩充功能要好过设计更多的语法的言论。现实中，C++的库门类繁多，解决的问题也是极其广泛，库从轻量级到重量级的都有。不少都是让人眼界大开，亦或是望而生叹的思维杰作。由于库的数量非常庞大，而且限于笔者水平，其中很多并不了解。所以文中所提的一些库都是比较著名的大型库。 <BR>&nbsp; <BR>　　5.1 标准库 <BR>&nbsp; <BR>　　标准库中提供了C++程序的基本设施。虽然C++标准库随着C++标准折腾了许多年，直到标准的出台才正式定型，但是在标准库的实现上却很令人欣慰得看到多种实现，并且已被实践证明为有工业级别强度的佳作。 <BR>&nbsp; <BR>　　(1) Dinkumware C++ Library <BR>&nbsp; <BR>　　参考站点：<A href="http://www.dinkumware.com/"><FONT color=#1d58d1>http://www.dinkumware.com/</FONT></A> <BR>&nbsp; <BR>　　P.J. Plauger编写的高品质的标准库。P.J. Plauger博士是Dr. Dobb's程序设计杰出奖的获得者。其编写的库长期被Microsoft采用，并且最近Borland也取得了其OEM的license，在其C/C++的产品中采用Dinkumware的库。 <BR>&nbsp; <BR>　　(2) RogueWave Standard C++ Library <BR>&nbsp; <BR>　　参考站点：<A href="http://www.roguewave.com/"><FONT color=#1d58d1>http://www.roguewave.com/</FONT></A> <BR>&nbsp; <BR>　　这个库在Borland C++ Builder的早期版本中曾经被采用，后来被其他的库给替换了。笔者不推荐使用。 <BR>&nbsp; <BR>　　(3) SGI STL <BR>&nbsp; <BR>　　参考站点：<A href="http://www.roguewave.com/"><FONT color=#1d58d1>http://www.roguewave.com/</FONT></A> <BR>&nbsp; <BR>　　SGI公司的C++标准模版库。 <BR>&nbsp; <BR>　　(4) STLport <BR>&nbsp; <BR>　　参考站点：<A href="http://www.stlport.org/"><FONT color=#1d58d1>http://www.stlport.org/</FONT></A> <BR>&nbsp; <BR>　　SGI STL库的跨平台可移植版本。 <BR>&nbsp; <BR>　　5.2 “准”标准库 - Boost <BR>&nbsp; <BR>　　参考站点：<A href="http://www.boost.org/"><FONT color=#1d58d1>http://www.boost.org</FONT></A> <BR>&nbsp; <BR>　　国内镜像：<A href="http://www.c-view.org/tech/lib/boost/index.htm"><FONT color=#1d58d1>http://www.c-view.org/tech/lib/boost/index.htm</FONT></A> <BR>&nbsp; <BR>　　Boost库是一个经过千锤百炼、可移植、提供源代码的C++库，作为标准库的后备，是C++标准化进程的发动机之一。 Boost库由C++标准委员会库工作组成员发起，在C++社区中影响甚大，其成员已近2000人。 Boost库为我们带来了最新、最酷、最实用的技术，是不折不扣的“准”标准库。 <BR>&nbsp; <BR>　　Boost中比较有名气的有这么几个库： <BR>&nbsp; <BR>　　Regex <BR>&nbsp; <BR>　　正则表达式库 <BR>&nbsp; <BR>　　Spirit <BR>&nbsp; <BR>　　LL parser framework，用C++代码直接表达EBNF <BR>&nbsp; <BR>　　Graph <BR>&nbsp; <BR>　　图组件和算法 <BR>&nbsp; <BR>　　Lambda <BR>&nbsp; <BR>　　在调用的地方定义短小匿名的函数对象，很实用的functional功能 <BR>&nbsp; <BR>　　concept check <BR>&nbsp; <BR>　　检查泛型编程中的concept <BR>&nbsp; <BR>&nbsp; <BR>　　Mpl <BR>&nbsp; <BR>　　用模板实现的元编程框架 <BR>&nbsp; <BR>&nbsp; <BR>　　Thread <BR>&nbsp; <BR>　　可移植的C++多线程库 <BR>&nbsp; <BR>&nbsp; <BR>　　Python <BR>&nbsp; <BR>　　把C++类和函数映射到Python之中 <BR>&nbsp; <BR>　　Pool <BR>&nbsp; <BR>　　内存池管理 <BR>&nbsp; <BR>&nbsp; <BR>　　smart_ptr <BR>&nbsp; <BR>　　5个智能指针，学习智能指针必读，一份不错的参考是来自CUJ的文章： <BR>&nbsp; <BR>　　Smart Pointers in Boost，哦，这篇文章可以查到，CUJ是提供在线浏览的。中文版见笔者在《Dr. Dobb's Journal软件研发杂志》第7辑上的译文。 <BR>&nbsp; <BR>　　Boost总体来说是实用价值很高，质量很高的库。并且由于其对跨平台的强调，对标准C++的强调，是编写平台无关，现代C++的开发者必备的工具。但是Boost中也有很多是实验性质的东西，在实际的开发中实用需要谨慎。并且很多Boost中的库功能堪称对语言功能的扩展，其构造用尽精巧的手法，不要贸然的花费时间研读。Boost另外一面，比如Graph这样的库则是具有工业强度，结构良好，非常值得研读的精品代码，并且也可以放心的在产品代码中多多利用。 <BR>&nbsp; <BR>　　5.3 GUI <BR>&nbsp; <BR>　　在众多C++的库中，GUI部分的库算是比较繁荣，也比较引人注目的。在实际开发中，GUI库的选择也是非常重要的一件事情，下面我们综述一下可选择的GUI库，各自的特点以及相关工具的支持。 <BR>&nbsp; <BR>　　(1) MFC <BR>&nbsp; <BR>　　大名鼎鼎的微软基础类库（Microsoft Foundation Class）。大凡学过VC++的人都应该知道这个库。虽然从技术角度讲，MFC是不大漂亮的，但是它构建于Windows API 之上，能够使程序员的工作更容易,编程效率高，减少了大量在建立 Windows 程序时必须编写的代码，同时它还提供了所有一般 C++ 编程的优点，例如继承和封装。MFC 编写的程序在各个版本的Windows操作系统上是可移植的，例如，在Windows 3.1下编写的代码可以很容易地移植到 Windows NT 或 Windows 95 上。但是在最近发展以及官方支持上日渐势微。 <BR>&nbsp; <BR>　　(2) QT <BR>&nbsp; <BR>　　参考网站：<A href="http://www.trolltech.com/"><FONT color=#1d58d1>http://www.trolltech.com/</FONT></A> <BR>&nbsp; <BR>　　Qt是Trolltech公司的一个多平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的很容易扩展，并且允许真正地组件编程。自从1996年早些时候，Qt进入商业领域，它已经成为全世界范围内数千种成功的应用程序的基础。Qt也是流行的Linux桌面环境KDE 的基础，同时它还支持Windows、Macintosh、Unix/X11等多种平台。 <BR>&nbsp; <BR>　　(3) WxWindows <BR>&nbsp; <BR>　　参考网站：<A href="http://www.wxwindows.org/"><FONT color=#1d58d1>http://www.wxwindows.org/</FONT></A> <BR>&nbsp; <BR>　　跨平台的GUI库。因为其类层次极像MFC，所以有文章介绍从MFC到WxWindows的代码移植以实现跨平台的功能。通过多年的开发也是一个日趋完善的GUI库，支持同样不弱于前面两个库。并且是完全开放源代码的。新近的C++ Builder X的GUI设计器就是基于这个库的。 <BR>&nbsp; <BR>　　(4) Fox <BR>&nbsp; <BR>　　参考网站：<A href="http://www.fox-toolkit.org/"><FONT color=#1d58d1>http://www.fox-toolkit.org/</FONT></A> <BR>&nbsp; <BR>　　开放源代码的GUI库。作者从自己亲身的开发经验中得出了一个理想的GUI库应该是什么样子的感受出发，从而开始了对这个库的开发。有兴趣的可以尝试一下。 <BR>&nbsp; <BR>&nbsp; <BR>　　(5) WTL <BR>&nbsp; <BR>　　基于ATL的一个库。因为使用了大量ATL的轻量级手法，模板等技术，在代码尺寸，以及速度优化方面做得非常到位。主要面向的使用群体是开发COM轻量级供网络下载的可视化控件的开发者。 <BR>&nbsp; <BR>　　(6) GTK <BR>&nbsp; <BR>　　参考网站：<A href="http://gtkmm.sourceforge.net/"><FONT color=#1d58d1>http://gtkmm.sourceforge.net/</FONT></A> <BR>&nbsp; <BR>　　GTK是一个大名鼎鼎的C的开源GUI库。在Linux世界中有Gnome这样的杀手应用。而GTK就是这个库的C++封装版本。 <BR>&nbsp; <BR>　　5.4 网络通信 <BR>&nbsp; <BR>　　(1) ACE <BR>&nbsp; <BR>　　参考网站：<A href="http://www.cs.wustl.edu/~schmidt/ACE.html"><FONT color=#1d58d1>http://www.cs.wustl.edu/~schmidt/ACE.html</FONT></A> <BR>&nbsp; <BR>　　C++库的代表，超重量级的网络通信开发框架。ACE自适配通信环境（Adaptive Communication Environment）是可以自由使用、开放源代码的面向对象框架，在其中实现了许多用于并发通信软件的核心模式。ACE提供了一组丰富的可复用C++包装外观（Wrapper Facade）和框架组件，可跨越多种平台完成通用的通信软件任务，其中包括：事件多路分离和事件处理器分派、信号处理、服务初始化、进程间通信、共享内存管理、消息路由、分布式服务动态（重）配置、并发执行和同步，等等。 <BR>&nbsp; <BR>　　(2) StreamModule <BR>&nbsp; <BR>　　参考网站：<A href="http://www.omnifarious.org/StrMod/"><FONT color=#1d58d1>http://www.omnifarious.org/StrMod/</FONT></A> <BR>&nbsp; <BR>　　设计用于简化编写分布式程序的库。尝试着使得编写处理异步行为的程序更容易，而不是用同步的外壳包起异步的本质。 <BR>&nbsp; <BR>　　(3) SimpleSocket <BR>&nbsp; <BR>　　参考网站：<A href="http://home.hetnet.nl/~lcbokkers/simsock.htm"><FONT color=#1d58d1>http://home.hetnet.nl/~lcbokkers/simsock.htm</FONT></A> <BR>&nbsp; <BR>　　这个类库让编写基于socket的客户/服务器程序更加容易。 <BR>&nbsp; <BR>　　(4) A Stream Socket API for C++ <BR>&nbsp; <BR>　　参考网站：<A href="http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html"><FONT color=#1d58d1>http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html</FONT></A> <BR>&nbsp; <BR>　　又一个对Socket的封装库。 <BR>&nbsp; <BR>　　5.5 XML <BR>&nbsp; <BR>　　(1) Xerces <BR>&nbsp; <BR>　　参考网站：<A href="http://xml.apache.org/xerces-c/"><FONT color=#1d58d1>http://xml.apache.org/xerces-c/</FONT></A> <BR>&nbsp; <BR>　　Xerces-C++ 是一个非常健壮的XML解析器，它提供了验证，以及SAX和DOM API。XML验证在文档类型定义(Document Type Definition，DTD)方面有很好的支持，并且在2001年12月增加了支持W3C XML Schema 的基本完整的开放标准。 <BR>&nbsp; <BR>　　(2) XMLBooster <BR>&nbsp; <BR>　　参考网站：<A href="http://www.xmlbooster.com/"><FONT color=#1d58d1>http://www.xmlbooster.com/</FONT></A> <BR>&nbsp; <BR>　　这个库通过产生特制的parser的办法极大的提高了XML解析的速度，并且能够产生相应的GUI程序来修改这个parser。在DOM和SAX两大主流XML解析办法之外提供了另外一个可行的解决方案。 <BR>&nbsp; <BR>　　(3) Pull Parser <BR>&nbsp; <BR>　　参考网站：<A href="http://www.extreme.indiana.edu/xgws/xsoap/xpp/"><FONT color=#1d58d1>http://www.extreme.indiana.edu/xgws/xsoap/xpp/</FONT></A> <BR>&nbsp; <BR>　　这个库采用pull方法的parser。在每个SAX的parser底层都有一个pull的parser，这个xpp把这层暴露出来直接给大家使用。在要充分考虑速度的时候值得尝试。 <BR>&nbsp; <BR>&nbsp; <BR>　　(4) Xalan <BR>&nbsp; <BR>　　参考网站：<A href="http://xml.apache.org/xalan-c/"><FONT color=#1d58d1>http://xml.apache.org/xalan-c/</FONT></A> <BR>&nbsp; <BR>　　Xalan是一个用于把XML文档转换为HTML，纯文本或者其他XML类型文档的XSLT处理器。 <BR>&nbsp; <BR>　　(5) CMarkup <BR>&nbsp; <BR>　　参考网站：<A href="http://www.firstobject.com/xml.htm"><FONT color=#1d58d1>http://www.firstobject.com/xml.htm</FONT></A> <BR>&nbsp; <BR>　　这是一种使用EDOM的XML解析器。在很多思路上面非常灵活实用。值得大家在DOM和SAX之外寻求一点灵感。 <BR>&nbsp; <BR>　　(6) libxml++ <BR>&nbsp; <BR>　　<A href="http://libxmlplusplus.sourceforge.net/"><FONT color=#1d58d1>http://libxmlplusplus.sourceforge.net/</FONT></A> <BR>&nbsp; <BR>　　libxml++是对著名的libxml XML解析器的C++封装版本 <BR>&nbsp; <BR>　　5.6 科学计算 <BR>&nbsp; <BR>　　(1) Blitz++ <BR>&nbsp; <BR>　　参考网站：<A href="http://www.oonumerics.org/blitz/"><FONT color=#1d58d1>http://www.oonumerics.org/blitz/</FONT></A> <BR>&nbsp; <BR>　　Blitz++ 是一个高效率的数值计算函数库，它的设计目的是希望建立一套既具像C++ 一样方便，同时又比Fortran速度更快的数值计算环境。通常，用C++所写出的数值程序，比 Fortran慢20%左右，因此Blitz++正是要改掉这个缺点。方法是利用C++的template技术，程序执行甚至可以比Fortran更快。Blitz++目前仍在发展中，对于常见的SVD，FFTs，QMRES等常见的线性代数方法并不提供，不过使用者可以很容易地利用Blitz++所提供的函数来构建。 <BR>&nbsp; <BR>　　(2) POOMA <BR>&nbsp; <BR>　　参考网站：<A href="http://www.codesourcery.com/pooma/pooma"><FONT color=#1d58d1>http://www.codesourcery.com/pooma/pooma</FONT></A> <BR>&nbsp; <BR>　　POOMA是一个免费的高性能的C++库，用于处理并行式科学计算。POOMA的面向对象设计方便了快速的程序开发，对并行机器进行了优化以达到最高的效率，方便在工业和研究环境中使用。 <BR>&nbsp; <BR>　　(3) MTL <BR>&nbsp; <BR>　　参考网站：<A href="http://www.osl.iu.edu/research/mtl/"><FONT color=#1d58d1>http://www.osl.iu.edu/research/mtl/</FONT></A> <BR>&nbsp; <BR>　　Matrix Template Library(MTL)是一个高性能的泛型组件库，提供了各种格式矩阵的大量线性代数方面的功能。在某些应用使用高性能编译器的情况下，比如Intel的编译器，从产生的汇编代码可以看出其与手写几乎没有两样的效能。<BR>&nbsp; <BR>　　(4) CGAL <BR>&nbsp; <BR>　　参考网站：<A href="http://www.cgal.org/"><FONT color=#1d58d1>www.cgal.org</FONT></A> <BR>&nbsp; <BR>　　Computational Geometry Algorithms Library的目的是把在计算几何方面的大部分重要的解决方案和方法以C++库的形式提供给工业和学术界的用户。 <BR>&nbsp; <BR>　　5.7 游戏开发 <BR>&nbsp; <BR>　　(1) Audio/Video 3D C++ Programming Library <BR>&nbsp; <BR>　　参考网站：<A href="http://www.galacticasoftware.com/products/av/"><FONT color=#1d58d1>http://www.galacticasoftware.com/products/av/</FONT></A> <BR>&nbsp; <BR>　　AV3D是一个跨平台，高性能的C++库。主要的特性是提供3D图形，声效支持（SB,以及S3M），控制接口（键盘，鼠标和遥感），XMS。 <BR>&nbsp; <BR>　　(2) KlayGE <BR>&nbsp; <BR>　　参考网站：<A href="http://home.g365.net/enginedev/"><FONT color=#1d58d1>http://home.g365.net/enginedev/</FONT></A> <BR>&nbsp; <BR>　　国内游戏开发高手自己用C++开发的游戏引擎。KlayGE是一个开放源代码、跨平台的游戏引擎，并使用Python作脚本语言。KlayGE在LGPL协议下发行。感谢龚敏敏先生为中国游戏开发事业所做出的贡献。 <BR>&nbsp; <BR>　　(3) OGRE <BR>&nbsp; <BR>　　参考网站：<A href="http://www.ogre3d.org/"><FONT color=#1d58d1>http://www.ogre3d.org</FONT></A> <BR>&nbsp; <BR>　　OGRE（面向对象的图形渲染引擎）是用C++开发的，使用灵活的面向对象3D引擎。它的目的是让开发者能更方便和直接地开发基于3D硬件设备的应用程序或游戏。引擎中的类库对更底层的系统库（如：Direct3D和OpenGL）的全部使用细节进行了抽象，并提供了基于现实世界对象的接口和其它类。 <BR>&nbsp; <BR>　　5.8 线程 <BR>&nbsp; <BR>　　(1) C++ Threads <BR>&nbsp; <BR>　　参考网站：<A href="http://threads.sourceforge.net/"><FONT color=#1d58d1>http://threads.sourceforge.net/</FONT></A> <BR>&nbsp; <BR>　　这个库的目标是给程序员提供易于使用的类，这些类被继承以提供在Linux环境中很难看到的大量的线程方面的功能。 <BR>&nbsp; <BR>　　(2) ZThreads <BR>&nbsp; <BR>　　参考网站：<A href="http://zthread.sourceforge.net/"><FONT color=#1d58d1>http://zthread.sourceforge.net/</FONT></A> <BR>&nbsp; <BR>　　一个先进的面向对象，跨平台的C++线程和同步库。 <BR>&nbsp; <BR>　　5.9 序列化 <BR>&nbsp; <BR>　　(1) s11n <BR>&nbsp; <BR>　　参考网站：<A href="http://s11n.net/"><FONT color=#1d58d1>http://s11n.net/</FONT></A> <BR>&nbsp; <BR>　　一个基于STL的C++库，用于序列化POD，STL容器以及用户定义的类型。 <BR>&nbsp; <BR>　　(2) Simple XML Persistence Library <BR>&nbsp; <BR>　　参考网站：<A href="http://sxp.sourceforge.net/"><FONT color=#1d58d1>http://sxp.sourceforge.net/</FONT></A> <BR>&nbsp; <BR>　　这是一个把对象序列化为XML的轻量级的C++库。 <BR>&nbsp; <BR>　　5.10 字符串 <BR>&nbsp; <BR>　　(1) C++ Str Library <BR>&nbsp; <BR>　　参考网站：<A href="http://www.utilitycode.com/str/"><FONT color=#1d58d1>http://www.utilitycode.com/str/</FONT></A> <BR>&nbsp; <BR>　　操作字符串和字符的库，支持Windows和支持gcc的多种平台。提供高度优化的代码，并且支持多线程环境和Unicode，同时还有正则表达式的支持。 <BR>&nbsp; <BR>　　(2) Common Text Transformation Library <BR>&nbsp; <BR>　　参考网站：<A href="http://cttl.sourceforge.net/"><FONT color=#1d58d1>http://cttl.sourceforge.net/</FONT></A> <BR>&nbsp; <BR>　　这是一个解析和修改STL字符串的库。CTTL substring类可以用来比较，插入，替换以及用EBNF的语法进行解析。 <BR>&nbsp; <BR>　　(3) GRETA <BR>&nbsp; <BR>　　参考网站：<A href="http://research.microsoft.com/projects/greta/"><FONT color=#1d58d1>http://research.microsoft.com/projects/greta/</FONT></A> <BR>&nbsp; <BR>　　这是由微软研究院的研究人员开发的处理正则表达式的库。在小型匹配的情况下有非常优秀的表现。 <BR>&nbsp; <BR>　　5.11 综合 <BR>&nbsp; <BR>　　(1) P::Classes <BR>&nbsp; <BR>　　参考网站：<A href="http://pclasses.com/"><FONT color=#1d58d1>http://pclasses.com/</FONT></A> <BR>&nbsp; <BR>　　一个高度可移植的C++应用程序框架。当前关注类型和线程安全的signal/slot机制，i/o系统包括基于插件的网络协议透明的i/o架构，基于插件的应用程序消息日志框架，访问sql数据库的类等等。 <BR>&nbsp; <BR>　　(2) ACDK - Artefaktur Component Development Kit <BR>&nbsp; <BR>　　参考网站：<A href="http://acdk.sourceforge.net/"><FONT color=#1d58d1>http://acdk.sourceforge.net/</FONT></A> <BR>&nbsp; <BR>　　这是一个平台无关的C++组件框架，类似于Java或者.NET中的框架（反射机制，线程，Unicode，废料收集，I/O，网络，实用工具，XML，等等），以及对Java, Perl, Python, TCL, Lisp, COM 和 CORBA的集成。 <BR>&nbsp; <BR>　　(3) dlib C++ library <BR>&nbsp; <BR>　　参考网站：<A href="http://www.cis.ohio-state.edu/~kingd/dlib/"><FONT color=#1d58d1>http://www.cis.ohio-state.edu/~kingd/dlib/</FONT></A> <BR>&nbsp; <BR>　　各种各样的类的一个综合。大整数，Socket，线程，GUI，容器类,以及浏览目录的API等等。 <BR>&nbsp; <BR>　　(4) Chilkat C++ Libraries <BR>&nbsp; <BR>　　参考网站：<A href="http://www.chilkatsoft.com/cpp_libraries.asp"><FONT color=#1d58d1>http://www.chilkatsoft.com/cpp_libraries.asp</FONT></A> <BR>&nbsp; <BR>　　这是提供zip，e-mail，编码，S/MIME，XML等方面的库。 <BR>&nbsp; <BR>　　(5) C++ Portable Types Library (PTypes) <BR>&nbsp; <BR>　　参考网站：<A href="http://www.melikyan.com/ptypes/"><FONT color=#1d58d1>http://www.melikyan.com/ptypes/</FONT></A> <BR>&nbsp; <BR>　　这是STL的比较简单的替代品，以及可移植的多线程和网络库。 <BR>&nbsp; <BR>　　(6) LFC <BR>&nbsp; <BR>　　参考网站：<A href="http://lfc.sourceforge.net/"><FONT color=#1d58d1>http://lfc.sourceforge.net/</FONT></A> <BR>&nbsp; <BR>　　哦，这又是一个尝试提供一切的C++库 <BR>&nbsp; <BR>　　5.12 其他库 <BR>&nbsp; <BR>　　(1) Loki <BR>&nbsp; <BR>　　参考网站：<A href="http://www.moderncppdesign.com/"><FONT color=#1d58d1>http://www.moderncppdesign.com/</FONT></A> <BR>&nbsp; <BR>　　哦，你可能抱怨我早该和Boost一起介绍它，一个实验性质的库。作者在loki中把C++模板的功能发挥到了极致。并且尝试把类似设计模式这样思想层面的东西通过库来提供。同时还提供了智能指针这样比较实用的功能。 <BR>&nbsp; <BR>　　(2) ATL <BR>&nbsp; <BR>　　ATL(Active Template Library) <BR>&nbsp; <BR>　　是一组小巧、高效、灵活的类，这些类为创建可互操作的COM组件提供了基本的设施。 <BR>&nbsp; <BR>　　(3) FC++: The Functional C++ Library <BR>&nbsp; <BR>　　这个库提供了一些函数式语言中才有的要素。属于用库来扩充语言的一个代表作。如果想要在OOP之外寻找另一分的乐趣，可以去看看函数式程序设计的世界。大师Peter Norvig在 “Teach Yourself Programming in Ten Years”一文中就将函数式语言列为至少应当学习的6类编程语言之一。 <BR>&nbsp; <BR>　　(4) FACT! <BR>&nbsp; <BR>　　参考网站：<A href="http://www.kfa-juelich.de/zam/FACT/start/index.html"><FONT color=#1d58d1>http://www.kfa-juelich.de/zam/FACT/start/index.html</FONT></A> <BR>&nbsp; <BR>　　另外一个实现函数式语言特性的库 <BR>&nbsp; <BR>　　(5) Crypto++ <BR>&nbsp; <BR>　　提供处理密码，消息验证，单向hash，公匙加密系统等功能的免费库。 <BR>&nbsp; <BR>　　还有很多非常激动人心或者是极其实用的C++库，限于我们的水平以及文章的篇幅不能包括进来。在对于这些已经包含近来的库的介绍中，由于并不是每一个我们都使用过，所以难免有偏颇之处，请读者见谅。 <BR>&nbsp; <BR>------------------------------------------------------------------------ <BR>-------- <BR>&nbsp; <BR>　　6，书籍 <BR>&nbsp; <BR>　　以前熊节先生曾撰文评论相对于Java程序设计语言，C++的好书多如牛毛。荣耀先生在《程序员》杂志上撰文《C++程序设计之四书五经》也将本领域内几乎所有的经典书籍作了全面的介绍,任何关于书的评论此时看来便是很多余的了。个人浅见，除非你打算以C++作为唯一兴趣或者生存之本，一般读者确实没有足够的时间和必要将20余本书籍全部阅读。更有参考价值的是荣耀先生的另一篇文章：《至少应该阅读的九本C++著作》，可以从下面的地址浏览到此文： <BR>&nbsp; <BR>　　<A href="http://www.royaloo.com/articles/articles_2003/9CppBooks.htm"><FONT color=#1d58d1>http://www.royaloo.com/articles/articles_2003/9CppBooks.htm</FONT></A> <BR>&nbsp; <BR>　　下面几本书对于走在C++初学之路上的读者是我们最愿意推荐给大家的： <BR>&nbsp; <BR>　　(1) 《C++ Primer》 <BR>&nbsp; <BR>　　哦，也许你会抱怨我们为什么不先介绍TCPL,但对于走在学习之路上的入门者，本书内容更为全面，更为详细易懂，我们称它为“C++的超级宝典”并不过分。配有一本不错的习题解答《C++ Primer Answer Book》可以辅助你的学习之路。 <BR>&nbsp; <BR>　　(2) 《Essential C++》 <BR>&nbsp; <BR>　　如果说《C++ Primer》是C++领域的超级宝典，那么此书作为掌握C++的大局观当之无愧。正如《.NET大局观》一书能够让读者全揽.NET，本书讲述了C++中最核心的全部主题。书虽不厚，内容精炼，不失为《C++ Primer》读者茶余饭后的主题回顾之作。 <BR>&nbsp; <BR>　　(3) 《The C++ Programming Language》 <BR>&nbsp; <BR>　　Bjarne为你带来的C++教程，真正能够告诉你怎么用才叫真正的C++的唯一一本书。虽然如同“某某程序设计语言”这样的书籍会给大家一个内容全揽，入门到精通的感觉，但本书确实不太适合初学者阅读。如果你自认为是一名很有经验的C++程序员，那至少也要反复咀嚼Bjarne先生所强调的若干内容。 <BR>&nbsp; <BR>　　(4) 《Effective C++》，《More Effective C++》 <BR>&nbsp; <BR>　　是的，正如一些C++爱好者经常以读过与没有读过上述两本作品来区分你是否是C++高手。我们也极力推崇这两本著作。在各种介绍C++专家经验的书籍里面，这本是最贴近语言本质，看后最能够有脱胎换骨感觉的书，读此书你需每日三省汝。 </P>
<P>　　技术书籍仁者见仁，过多的评论反无太多意义，由读者喜好选择最适合自己的 书方为上策。 <BR>&nbsp; <BR>------------------------------------------------------------------------ <BR>-------- <BR>&nbsp; <BR>　　7，资源网站 <BR>&nbsp; <BR>　　正如我们可以通过计算机历史上的重要人物了解计算机史的发展，C++相关人物的网站也可以使我们得到最有价值的参考与借鉴，下面的人物我们认为没有介绍的必要，只因下面的人物在C++领域的地位众所周知，我们只将相关的资源进行罗列以供读者学习，他们有的工作于贝尔实验室，有的工作于知名编译器厂商，有的在不断推进语言的标准化，有的为读者撰写了多部千古奇作…… <BR>　　(1) Bjarne Stroustrup <BR>　　<A href="http://www.research.att.com/~bs/"><FONT color=#1d58d1>http://www.research.att.com/~bs/</FONT></A> <BR>&nbsp; <BR>　　(2) Stanley B. Lippman <BR>　　<A href="http://blogs.msdn.com/slippman/"><FONT color=#1d58d1>http://blogs.msdn.com/slippman/</FONT></A> <BR>　　中文版 <A href="http://www.zengyihome.net/slippman/index.htm"><FONT color=#1d58d1>http://www.zengyihome.net/slippman/index.htm</FONT></A> <BR>&nbsp; <BR>　　(3) Scott Meyers <BR>　　<A href="http://www.aristeia.com/"><FONT color=#1d58d1>http://www.aristeia.com/</FONT></A> <BR>&nbsp; <BR>　　(4) David Musser <BR>　　<A href="http://www.cs.rpi.edu/~musser/"><FONT color=#1d58d1>http://www.cs.rpi.edu/~musser/</FONT></A> <BR>&nbsp; <BR>　　(5) Bruce Eckel <BR>　　<A href="http://www.bruceeckel.com/"><FONT color=#1d58d1>http://www.bruceeckel.com</FONT></A> <BR>&nbsp; <BR>　　(6) Nicolai M. Josuttis <BR>　　<A href="http://www.josuttis.com/"><FONT color=#1d58d1>http://www.josuttis.com/</FONT></A> <BR>&nbsp; <BR>　　(7) Herb Sutter <BR>　　<A href="http://www.gotw.ca/"><FONT color=#1d58d1>http://www.gotw.ca/</FONT></A> <BR>&nbsp; <BR>　　(8) Andrei Alexandrescu <BR>　　<A href="http://www.coderncppdesign.com/"><FONT color=#1d58d1>http://www.coderncppdesign.com/</FONT></A> <BR>&nbsp; <BR>　　(9) 侯捷先生 <BR>　　<A href="http://www.jjhou.com/"><FONT color=#1d58d1>http://www.jjhou.com</FONT></A> <BR>&nbsp; <BR>　　(10) 孟岩先生 <BR>　　先生繁忙于工作，痴迷于技术，暂无个人主页，关于先生的作品可以通过CSDN的专栏和侯先生的主页访问到。 <BR>&nbsp; <BR>　　(11) 荣耀先生 <BR>　　<A href="http://www.royaloo.com/"><FONT color=#1d58d1>http://www.royaloo.com/</FONT></A> <BR>&nbsp; <BR>　　(12) 潘爱民先生 <BR>　　<A href="http://www.icst.pku.edu.cn/panaimin/pam_homepage.htm"><FONT color=#1d58d1>http://www.icst.pku.edu.cn/panaimin/pam_homepage.htm</FONT></A> <BR>&nbsp; <BR>　　除了上述大师的主页外，以下的综合类C++学习参考站点是我们非常愿意向大家推荐的： <BR>&nbsp; <BR>　　(1) CodeProject <BR>　　<A href="http://www.codeproject.com/"><FONT color=#1d58d1>http://www.codeproject.com</FONT></A> <BR>&nbsp; <BR>　　(2) CodeGuru <BR>　　<A href="http://www.codeguru.com/"><FONT color=#1d58d1>http://www.codeguru.com</FONT></A> <BR>&nbsp; <BR>　　(3) Dr. Dobb's Journal <BR>　　<A href="http://www.ddj.com/"><FONT color=#1d58d1>http://www.ddj.com</FONT></A> <BR>&nbsp; <BR>　　(4) C/C++ Users Journal <BR>　　<A href="http://www.cuj.com/"><FONT color=#1d58d1>http://www.cuj.com</FONT></A> <BR>&nbsp; <BR>　　(5) C维视点 <BR>　　<A href="http://www.c-view.org/"><FONT color=#1d58d1>http://www.c-view.org</FONT></A> <BR>&nbsp; <BR>　　(6) allaboutprogram <BR>　　<A href="http://www.allaboutprogram.com/"><FONT color=#1d58d1>http://www.allaboutprogram.com</FONT></A> <BR>　　其他资料 <BR>&nbsp; <BR>　　(1) ISO IEC JTC1/SC22/WG21 - C++：标准C++的权威参考 <BR>　　<A href="http://anubis.dkuug.dk/jtc1/sc22/wg21/"><FONT color=#1d58d1>http://anubis.dkuug.dk/jtc1/sc22/wg21/</FONT></A> <BR>&nbsp; <BR>　　(2) C++ FAQ LITE — Frequently Asked Questions: 最为全面的C++FAQ <BR>　　<A href="http://www.sunistudio.com/cppfaq/index.html"><FONT color=#1d58d1>http://www.sunistudio.com/cppfaq/index.html</FONT></A> </P>
<P>　　C/C++ 新闻组： <BR>　　你不妨尝试从这里提问和回答问题，很多不错的Q&amp;A资源...... <BR>&nbsp; <BR>　　(1) .alt.comp.lang.learn.c-c++ <BR>　　这个简单些，如果你和我一样是个菜鸟 <BR>&nbsp; <BR>　　(2) .comp.lang.c++.moderated <BR>嗯，这个显然水平高一些 <BR>&nbsp; <BR>　　(3) .comp.std.c++ <BR>　　如果你需要讨论标准C++相关话题的话 <BR>&nbsp; <BR>------------------------------------------------------------------------ <BR>-------- <BR>&nbsp; <BR>　　8，不得不写的结束语 <BR>&nbsp; <BR>　　结束的时候也是总结现状，展望未来的时候。虽然C++从脱胎于C开始，一路艰难坎坷的走过来，但是无论如何C++已经取得了工业基础的地位。文章列举的大量相关资源就是最好的证明，而业界的大量用C++写成的产品代码以及大量的C++职业工程师则是最直接的证明。同时，我们可以看到各个高校的计算机专业都开设有C++这门课程，网络上对于C++的学习讨论也从来都没有停过。但是，在Java和.NET两大企业开发平台的围攻下，给人的感觉是C++越来越“不行”了。<BR>&nbsp; <BR>　　C++在面向企业的软件开发中，在开发便捷性等方面的确要比Java和C#差很多，其中一个问题是C++语言本身比较复杂，学习曲线比较陡峭，另外一个问题是C++标准化的时间太长，丧失了很多的壮大机会，耗费了很多精力在厂商的之间的斗争上，而C++的标准库离一个完善的程序开发框架还缺少太多太多的内容，各个第三方的类库和框架又在一致性和完整性上没法和随平台提供的框架相提并论。难道C++真的 要退出历史舞台了？ <BR>&nbsp; <BR>　　从C++目前的活跃程度，以及应用现状来说是完全能够肯定C++仍然是软件工业的基础，也不会退出历史舞台的。另外从Boost，Loki这些库中我们也能够看到C++的发展非常活跃，对于新技术新思维非常激进，C++仍然广泛受到关注。从ACE在高性能通信领域的应用，以及MTL这样的库在数值计算领域的出色表现，我们可以看到C++在高性能应用场合下的不可替代的作用，而嵌入式系统这样的内存受限开发平台，比如Symbian OS上，C++已经发挥着并且将发挥更大的作用。可以预见的是以后的软件无论上层的应用怎么变，它的底层核心都会是由C/C++这样的系统级软件编写的，比如Java虚拟机，.NET Framwork。因为只有这样的系统级软件才能完全彻底的发挥机器的功能。 <BR>&nbsp; <BR>　　需要看到的是两个趋势，一个趋势是C++变得更加复杂，更加学院派，通过模板等有潜力的语法因素构造越来越精巧的库成为了现代C++的热点，虽然在利用库实现新的编程范式，乃至设计模式等方面很有开创意义，也确实产生了一些能够便捷开发的工具，但是更多的是把C++变得更加强大，更加复杂，也更加难懂，似乎也更加学院派，不得不说它正在向边缘化道路发展。另一个趋势是C++在主流的企业应用开 <BR>发中已经逐渐退出了，ERP这样的企业软件开发中基本上不会考虑C++，除非需要考虑性能或者和遗留代码的集成这些因素。C++退守到系统级别语言，成为软件工业的基础是大势所趋。然而反思一下，真的是退守么？自从STL出现，无数的人风起云涌的开始支持C++,他们狂呼“我看到深夜消失了，目标软件工程的出现。我看到了可维护的代码。”是的，STL在可维护性下做得如此出色。但是又怎样呢？STL为C++铺平了现代软件工程的道路，而在上层应用程序软件开发领域这块场地早不单独属于C++,很多程序设计语言都做得很出色，疯狂的支持者会毫不犹豫地说我们应当支持C++,因为它是世界上最棒的语言。而坦率地说，你的腰杆真的那么硬么？也许只是在逃避一些事实。C++是优秀的，这不可否认，STL的出现让C++一度走上了最辉煌的时刻，然而现在看来……我的一位恩师曾言：真正能够将STL应用得淋漓尽致的人很保守地说国内也不超过200人，或许不加入STL能够使C++向着它应当发展的方向发展的更好，而现在看来，C++也应当回首到真正属于他的那一片圣地上…… <BR>&nbsp; <BR>------------------------------------------------------------------------------ <BR>-- <BR>&nbsp; <BR>参考资料 <BR>&nbsp; <BR>本文成文时参考了以下资源： <BR>&nbsp; <BR>1、《程序员》2004年2月，3月，“C++ 程序设计之四书五经” 荣耀 <BR>&nbsp; <BR>2、水'木清华BBS C++版精华区 <BR>&nbsp; <BR>3、<A href="http://jjhou.csdn.net/"><FONT color=#1d58d1>http://jjhou.csdn.net</FONT></A> <BR>&nbsp; <BR>4、<A href="http://www.royaloo.com/"><FONT color=#1d58d1>http://www.royaloo.com</FONT></A><BR>&nbsp; <BR>5、<A href="http://www.zengyihome.net/"><FONT color=#1d58d1>http://www.zengyihome.net</FONT></A> <BR>&nbsp; <BR>6、C/C++ 开发人员：充实您的 XML 工具箱 <A href="http://www-900.ibm.com/developerWorks/cn/xml/x-ctlbx/index.shtml"><FONT color=#1d58d1>http://www-900.ibm.com/developerWorks/cn/xml/x-ctlbx/index.shtml</FONT></A> <BR></P></DIV><img src ="http://www.cppblog.com/fashion1840/aggbug/4223.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/fashion1840/" target="_blank">江南小生</a> 2006-03-16 10:07 <a href="http://www.cppblog.com/fashion1840/articles/4223.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>堆和栈的区别</title><link>http://www.cppblog.com/fashion1840/articles/4221.html</link><dc:creator>江南小生</dc:creator><author>江南小生</author><pubDate>Thu, 16 Mar 2006 01:59:00 GMT</pubDate><guid>http://www.cppblog.com/fashion1840/articles/4221.html</guid><wfw:comment>http://www.cppblog.com/fashion1840/comments/4221.html</wfw:comment><comments>http://www.cppblog.com/fashion1840/articles/4221.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/fashion1840/comments/commentRss/4221.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/fashion1840/services/trackbacks/4221.html</trackback:ping><description><![CDATA[<H2><FONT color=#1d58d1>堆和栈的区别</FONT></H2>
<DIV class=postbody twffan="done">一、预备知识—程序的内存分配<BR>一个由c/C++编译的程序占用的内存分为以下几个部分<BR>1、栈区（stack）—&nbsp;由编译器自动分配释放&nbsp;，存放函数的参数值，局部变量的值等。其操作方式类似于数据结构中的栈。<BR>2、堆区（heap）&nbsp;—&nbsp;一般由程序员分配释放，&nbsp;若程序员不释放，程序结束时可能由OS回收&nbsp;。注意它与数据结构中的堆是两回事，分配方式倒是类似于链表，呵呵。<BR>3、全局区（静态区）（static）—，全局变量和静态变量的存储是放在一块的，初始化的全局变量和静态变量在一块区域，&nbsp;未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。&nbsp;-&nbsp;程序结束后有系统释放&nbsp;<BR>4、文字常量区—常量字符串就是放在这里的。&nbsp;程序结束后由系统释放<BR>5、程序代码区—存放函数体的二进制代码。<BR>二、例子程序&nbsp;<BR>这是一个前辈写的，非常详细&nbsp;<BR>//main.cpp&nbsp;<BR>int&nbsp;a&nbsp;=&nbsp;0;&nbsp;全局初始化区&nbsp;<BR>char&nbsp;*p1;&nbsp;全局未初始化区&nbsp;<BR>main()&nbsp;<BR>{&nbsp;<BR>int&nbsp;b;&nbsp;栈&nbsp;<BR>char&nbsp;s[]&nbsp;=&nbsp;"abc";&nbsp;栈&nbsp;<BR>char&nbsp;*p2;&nbsp;栈&nbsp;<BR>char&nbsp;*p3&nbsp;=&nbsp;"123456";&nbsp;123456\0在常量区，p3在栈上。&nbsp;<BR>static&nbsp;int&nbsp;c&nbsp;=0；&nbsp;全局（静态）初始化区&nbsp;<BR>p1&nbsp;=&nbsp;(char&nbsp;*)malloc(10);&nbsp;<BR>p2&nbsp;=&nbsp;(char&nbsp;*)malloc(20);&nbsp;<BR>分配得来得10和20字节的区域就在堆区。&nbsp;<BR>strcpy(p1,&nbsp;"123456");&nbsp;123456\0放在常量区，编译器可能会将它与p3所指向的"123456"优化成一个地方。&nbsp;<BR>}&nbsp; 
<P></P>
<P><BR>二、堆和栈的理论知识&nbsp;<BR>2.1申请方式&nbsp;<BR>stack:&nbsp;<BR>由系统自动分配。&nbsp;例如，声明在函数中一个局部变量&nbsp;int&nbsp;b;&nbsp;系统自动在栈中为b开辟空间&nbsp;<BR>heap:&nbsp;<BR>需要程序员自己申请，并指明大小，在c中malloc函数&nbsp;<BR>如p1&nbsp;=&nbsp;(char&nbsp;*)malloc(10);&nbsp;<BR>在C++中用new运算符&nbsp;<BR>如p2&nbsp;=&nbsp;(char&nbsp;*)malloc(10);&nbsp;<BR>但是注意p1、p2本身是在栈中的。&nbsp;</P>
<P><BR>2.2&nbsp;<BR>申请后系统的响应&nbsp;<BR>栈：只要栈的剩余空间大于所申请空间，系统将为程序提供内存，否则将报异常提示栈溢出。&nbsp;<BR>堆：首先应该知道操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，&nbsp;<BR>会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大小，这样，代码中的delete语句才能正确的释放本内存空间。另外，由于找到的堆结点的大小不一定正好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表中。&nbsp;</P>
<P>2.3申请大小的限制&nbsp;<BR>栈：在Windows下,栈是向低地址扩展的数据结构，是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的，在WINDOWS下，栈的大小是2M（也有的说是1M，总之是一个编译时就确定的常数），如果申请的空间超过栈的剩余空间时，将提示overflow。因此，能从栈获得的空间较小。&nbsp;<BR>堆：堆是向高地址扩展的数据结构，是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见，堆获得的空间比较灵活，也比较大。&nbsp;</P>
<P><BR>2.4申请效率的比较：&nbsp;<BR>栈由系统自动分配，速度较快。但程序员是无法控制的。&nbsp;<BR>堆是由new分配的内存，一般速度比较慢，而且容易产生内存碎片,不过用起来最方便.&nbsp;<BR>另外，在WINDOWS下，最好的方式是用VirtualAlloc分配内存，他不是在堆，也不是在栈是直接在进程的地址空间中保留一快内存，虽然用起来最不方便。但是速度快，也最灵活。&nbsp;</P>
<P>2.5堆和栈中的存储内容&nbsp;<BR>栈：&nbsp;在函数调用时，第一个进栈的是主函数中后的下一条指令（函数调用语句的下一条可执行语句）的地址，然后是函数的各个参数，在大多数的C编译器中，参数是由右往左入栈的，然后是函数中的局部变量。注意静态变量是不入栈的。&nbsp;<BR>当本次函数调用结束后，局部变量先出栈，然后是参数，最后栈顶指针指向最开始存的地址，也就是主函数中的下一条指令，程序由该点继续运行。&nbsp;<BR>堆：一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。&nbsp;</P>
<P>2.6存取效率的比较&nbsp;</P>
<P>char&nbsp;s1[]&nbsp;=&nbsp;"aaaaaaaaaaaaaaa";&nbsp;<BR>char&nbsp;*s2&nbsp;=&nbsp;"bbbbbbbbbbbbbbbbb";&nbsp;<BR>aaaaaaaaaaa是在运行时刻赋值的；&nbsp;<BR>而bbbbbbbbbbb是在编译时就确定的；&nbsp;<BR>但是，在以后的存取中，在栈上的数组比指针所指向的字符串(例如堆)快。&nbsp;<BR>比如：&nbsp;<BR>#include&nbsp;<BR>void&nbsp;main()&nbsp;<BR>{&nbsp;<BR>char&nbsp;a&nbsp;=&nbsp;1;&nbsp;<BR>char&nbsp;c[]&nbsp;=&nbsp;"1234567890";&nbsp;<BR>char&nbsp;*p&nbsp;="1234567890";&nbsp;<BR>a&nbsp;=&nbsp;c[1];&nbsp;<BR>a&nbsp;=&nbsp;p[1];&nbsp;<BR>return;&nbsp;<BR>}&nbsp;<BR>对应的汇编代码&nbsp;<BR>10:&nbsp;a&nbsp;=&nbsp;c[1];&nbsp;<BR>00401067&nbsp;8A&nbsp;4D&nbsp;F1&nbsp;mov&nbsp;cl,byte&nbsp;ptr&nbsp;[ebp-0Fh]&nbsp;<BR>0040106A&nbsp;88&nbsp;4D&nbsp;FC&nbsp;mov&nbsp;byte&nbsp;ptr&nbsp;[ebp-4],cl&nbsp;<BR>11:&nbsp;a&nbsp;=&nbsp;p[1];&nbsp;<BR>0040106D&nbsp;8B&nbsp;55&nbsp;EC&nbsp;mov&nbsp;edx,dword&nbsp;ptr&nbsp;[ebp-14h]&nbsp;<BR>00401070&nbsp;8A&nbsp;42&nbsp;01&nbsp;mov&nbsp;al,byte&nbsp;ptr&nbsp;[edx+1]&nbsp;<BR>00401073&nbsp;88&nbsp;45&nbsp;FC&nbsp;mov&nbsp;byte&nbsp;ptr&nbsp;[ebp-4],al&nbsp;<BR>第一种在读取时直接就把字符串中的元素读到寄存器cl中，而第二种则要先把指针值读到edx中，在根据edx读取字符，显然慢了。&nbsp;</P>
<P><BR>2.7小结：&nbsp;<BR>堆和栈的区别可以用如下的比喻来看出：&nbsp;<BR>使用栈就象我们去饭馆里吃饭，只管点菜（发出申请）、付钱、和吃（使用），吃饱了就走，不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作，他的好处是快捷，但是自由度小。&nbsp;<BR>使用堆就象是自己动手做喜欢吃的菜肴，比较麻烦，但是比较符合自己的口味，而且自由度大。&nbsp;<BR><BR><BR><BR></P>
<P>windows进程中的内存结构</P>
<P><BR>在阅读本文之前，如果你连堆栈是什么多不知道的话，请先阅读文章后面的基础知识。&nbsp;</P>
<P>接触过编程的人都知道，高级语言都能通过变量名来访问内存中的数据。那么这些变量在内存中是如何存放的呢？程序又是如何使用这些变量的呢？下面就会对此进行深入的讨论。下文中的C语言代码如没有特别声明，默认都使用VC编译的release版。&nbsp;</P>
<P>首先，来了解一下&nbsp;C&nbsp;语言的变量是如何在内存分部的。C&nbsp;语言有全局变量(Global)、本地变量(Local)，静态变量(Static)、寄存器变量(Regeister)。每种变量都有不同的分配方式。先来看下面这段代码：&nbsp;</P>
<P>#include&nbsp;&lt;stdio.h&gt;&nbsp;</P>
<P>int&nbsp;g1=0,&nbsp;g2=0,&nbsp;g3=0;&nbsp;</P>
<P>int&nbsp;main()&nbsp;<BR>{&nbsp;<BR>static&nbsp;int&nbsp;s1=0,&nbsp;s2=0,&nbsp;s3=0;&nbsp;<BR>int&nbsp;v1=0,&nbsp;v2=0,&nbsp;v3=0;&nbsp;</P>
<P>//打印出各个变量的内存地址&nbsp;</P>
<P>printf("0x%08x\n",&amp;v1);&nbsp;//打印各本地变量的内存地址&nbsp;<BR>printf("0x%08x\n",&amp;v2);&nbsp;<BR>printf("0x%08x\n\n",&amp;v3);&nbsp;<BR>printf("0x%08x\n",&amp;g1);&nbsp;//打印各全局变量的内存地址&nbsp;<BR>printf("0x%08x\n",&amp;g2);&nbsp;<BR>printf("0x%08x\n\n",&amp;g3);&nbsp;<BR>printf("0x%08x\n",&amp;s1);&nbsp;//打印各静态变量的内存地址&nbsp;<BR>printf("0x%08x\n",&amp;s2);&nbsp;<BR>printf("0x%08x\n\n",&amp;s3);&nbsp;<BR>return&nbsp;0;&nbsp;<BR>}&nbsp;</P>
<P>编译后的执行结果是：&nbsp;</P>
<P>0x0012ff78&nbsp;<BR>0x0012ff7c&nbsp;<BR>0x0012ff80&nbsp;</P>
<P>0x004068d0&nbsp;<BR>0x004068d4&nbsp;<BR>0x004068d8&nbsp;</P>
<P>0x004068dc&nbsp;<BR>0x004068e0&nbsp;<BR>0x004068e4&nbsp;</P>
<P>输出的结果就是变量的内存地址。其中v1,v2,v3是本地变量，g1,g2,g3是全局变量，s1,s2,s3是静态变量。你可以看到这些变量在内存是连续分布的，但是本地变量和全局变量分配的内存地址差了十万八千里，而全局变量和静态变量分配的内存是连续的。这是因为本地变量和全局/静态变量是分配在不同类型的内存区域中的结果。对于一个进程的内存空间而言，可以在逻辑上分成3个部份：代码区，静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈(stack)”和“堆(heap)”是两种不同的动态数据区，栈是一种线性结构，堆是一种链式结构。进程的每个线程都有私有的“栈”，所以每个线程虽然代码一样，但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区，本地变量分配在动态数据区，即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。&nbsp;</P>
<P><BR>├———————┤低端内存区域&nbsp;<BR>│&nbsp;……&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;动态数据区&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;……&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;代码区&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;静态数据区&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;……&nbsp;│&nbsp;<BR>├———————┤高端内存区域&nbsp;</P>
<P><BR>堆栈是一个先进后出的数据结构，栈顶地址总是小于等于栈的基地址。我们可以先了解一下函数调用的过程，以便对堆栈在程序中的作用有更深入的了解。不同的语言有不同的函数调用规定，这些因素有参数的压入规则和堆栈的平衡。windows&nbsp;API的调用规则和ANSI&nbsp;C的函数调用规则是不一样的，前者由被调函数调整堆栈，后者由调用者调整堆栈。两者通过“__stdcall”和“__cdecl”前缀区分。先看下面这段代码：&nbsp;</P>
<P>#include&nbsp;&lt;stdio.h&gt;&nbsp;</P>
<P>void&nbsp;__stdcall&nbsp;func(int&nbsp;param1,int&nbsp;param2,int&nbsp;param3)&nbsp;<BR>{&nbsp;<BR>int&nbsp;var1=param1;&nbsp;<BR>int&nbsp;var2=param2;&nbsp;<BR>int&nbsp;var3=param3;&nbsp;<BR>printf("0x%08x\n",¶m1);&nbsp;//打印出各个变量的内存地址&nbsp;<BR>printf("0x%08x\n",¶m2);&nbsp;<BR>printf("0x%08x\n\n",¶m3);&nbsp;<BR>printf("0x%08x\n",&amp;var1);&nbsp;<BR>printf("0x%08x\n",&amp;var2);&nbsp;<BR>printf("0x%08x\n\n",&amp;var3);&nbsp;<BR>return;&nbsp;<BR>}&nbsp;</P>
<P>int&nbsp;main()&nbsp;<BR>{&nbsp;<BR>func(1,2,3);&nbsp;<BR>return&nbsp;0;&nbsp;<BR>}&nbsp;</P>
<P>编译后的执行结果是：&nbsp;</P>
<P>0x0012ff78&nbsp;<BR>0x0012ff7c&nbsp;<BR>0x0012ff80&nbsp;</P>
<P>0x0012ff68&nbsp;<BR>0x0012ff6c&nbsp;<BR>0x0012ff70&nbsp;</P>
<P><BR>├———————┤&lt;—函数执行时的栈顶（ESP）、低端内存区域&nbsp;<BR>│&nbsp;……&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;var&nbsp;1&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;var&nbsp;2&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;var&nbsp;3&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;RET&nbsp;│&nbsp;<BR>├———————┤&lt;—“__cdecl”函数返回后的栈顶（ESP）&nbsp;<BR>│&nbsp;parameter&nbsp;1&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;parameter&nbsp;2&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;parameter&nbsp;3&nbsp;│&nbsp;<BR>├———————┤&lt;—“__stdcall”函数返回后的栈顶（ESP）&nbsp;<BR>│&nbsp;……&nbsp;│&nbsp;<BR>├———————┤&lt;—栈底（基地址&nbsp;EBP）、高端内存区域&nbsp;</P>
<P><BR>上图就是函数调用过程中堆栈的样子了。首先，三个参数以从又到左的次序压入堆栈，先压“param3”，再压“param2”，最后压入“param1”；然后压入函数的返回地址(RET)，接着跳转到函数地址接着执行（这里要补充一点，介绍UNIX下的缓冲溢出原理的文章中都提到在压入RET后，继续压入当前EBP，然后用当前ESP代替EBP。然而，有一篇介绍windows下函数调用的文章中说，在windows下的函数调用也有这一步骤，但根据我的实际调试，并未发现这一步，这还可以从param3和var1之间只有4字节的间隙这点看出来）；第三步，将栈顶(ESP)减去一个数，为本地变量分配内存空间，上例中是减去12字节(ESP=ESP-3*4，每个int变量占用4个字节)；接着就初始化本地变量的内存空间。由于“__stdcall”调用由被调函数调整堆栈，所以在函数返回前要恢复堆栈，先回收本地变量占用的内存(ESP=ESP+3*4)，然后取出返回地址，填入EIP寄存器，回收先前压入参数占用的内存(ESP=ESP+3*4)，继续执行调用者的代码。参见下列汇编代码：&nbsp;</P>
<P>;--------------func&nbsp;函数的汇编代码-------------------&nbsp;</P>
<P>:00401000&nbsp;83EC0C&nbsp;sub&nbsp;esp,&nbsp;0000000C&nbsp;//创建本地变量的内存空间&nbsp;<BR>:00401003&nbsp;8B442410&nbsp;mov&nbsp;eax,&nbsp;dword&nbsp;ptr&nbsp;[esp+10]&nbsp;<BR>:00401007&nbsp;8B4C2414&nbsp;mov&nbsp;ecx,&nbsp;dword&nbsp;ptr&nbsp;[esp+14]&nbsp;<BR>:0040100B&nbsp;8B542418&nbsp;mov&nbsp;edx,&nbsp;dword&nbsp;ptr&nbsp;[esp+18]&nbsp;<BR>:0040100F&nbsp;89442400&nbsp;mov&nbsp;dword&nbsp;ptr&nbsp;[esp],&nbsp;eax&nbsp;<BR>:00401013&nbsp;8D442410&nbsp;lea&nbsp;eax,&nbsp;dword&nbsp;ptr&nbsp;[esp+10]&nbsp;<BR>:00401017&nbsp;894C2404&nbsp;mov&nbsp;dword&nbsp;ptr&nbsp;[esp+04],&nbsp;ecx&nbsp;</P>
<P>……………………（省略若干代码）&nbsp;</P>
<P>:00401075&nbsp;83C43C&nbsp;add&nbsp;esp,&nbsp;0000003C&nbsp;;恢复堆栈，回收本地变量的内存空间&nbsp;<BR>:00401078&nbsp;C3&nbsp;ret&nbsp;000C&nbsp;;函数返回，恢复参数占用的内存空间&nbsp;<BR>;如果是“__cdecl”的话，这里是“ret”，堆栈将由调用者恢复&nbsp;</P>
<P>;-------------------函数结束-------------------------&nbsp;</P>
<P><BR>;--------------主程序调用func函数的代码--------------&nbsp;</P>
<P>:00401080&nbsp;6A03&nbsp;push&nbsp;00000003&nbsp;//压入参数param3&nbsp;<BR>:00401082&nbsp;6A02&nbsp;push&nbsp;00000002&nbsp;//压入参数param2&nbsp;<BR>:00401084&nbsp;6A01&nbsp;push&nbsp;00000001&nbsp;//压入参数param1&nbsp;<BR>:00401086&nbsp;E875FFFFFF&nbsp;call&nbsp;00401000&nbsp;//调用func函数&nbsp;<BR>;如果是“__cdecl”的话，将在这里恢复堆栈，“add&nbsp;esp,&nbsp;0000000C”&nbsp;</P>
<P>聪明的读者看到这里，差不多就明白缓冲溢出的原理了。先来看下面的代码：&nbsp;</P>
<P>#include&nbsp;&lt;stdio.h&gt;&nbsp;<BR>#include&nbsp;&lt;string.h&gt;&nbsp;</P>
<P>void&nbsp;__stdcall&nbsp;func()&nbsp;<BR>{&nbsp;<BR>char&nbsp;lpBuff[8]="\0";&nbsp;<BR>strcat(lpBuff,"AAAAAAAAAAA");&nbsp;<BR>return;&nbsp;<BR>}&nbsp;</P>
<P>int&nbsp;main()&nbsp;<BR>{&nbsp;<BR>func();&nbsp;<BR>return&nbsp;0;&nbsp;<BR>}&nbsp;</P>
<P>编译后执行一下回怎么样？哈，“"0x00414141"指令引用的"0x00000000"内存。该内存不能为"read"。”，“非法操作”喽！"41"就是"A"的16进制的ASCII码了，那明显就是strcat这句出的问题了。"lpBuff"的大小只有8字节，算进结尾的\0，那strcat最多只能写入7个"A"，但程序实际写入了11个"A"外加1个\0。再来看看上面那幅图，多出来的4个字节正好覆盖了RET的所在的内存空间，导致函数返回到一个错误的内存地址，执行了错误的指令。如果能精心构造这个字符串，使它分成三部分，前一部份仅仅是填充的无意义数据以达到溢出的目的，接着是一个覆盖RET的数据，紧接着是一段shellcode，那只要着个RET地址能指向这段shellcode的第一个指令，那函数返回时就能执行shellcode了。但是软件的不同版本和不同的运行环境都可能影响这段shellcode在内存中的位置，那么要构造这个RET是十分困难的。一般都在RET和shellcode之间填充大量的NOP指令，使得exploit有更强的通用性。&nbsp;</P>
<P><BR>├———————┤&lt;—低端内存区域&nbsp;<BR>│&nbsp;……&nbsp;│&nbsp;<BR>├———————┤&lt;—由exploit填入数据的开始&nbsp;<BR>│&nbsp;│&nbsp;<BR>│&nbsp;buffer&nbsp;│&lt;—填入无用的数据&nbsp;<BR>│&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;RET&nbsp;│&lt;—指向shellcode，或NOP指令的范围&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;NOP&nbsp;│&nbsp;<BR>│&nbsp;……&nbsp;│&lt;—填入的NOP指令，是RET可指向的范围&nbsp;<BR>│&nbsp;NOP&nbsp;│&nbsp;<BR>├———————┤&nbsp;<BR>│&nbsp;│&nbsp;<BR>│&nbsp;shellcode&nbsp;│&nbsp;<BR>│&nbsp;│&nbsp;<BR>├———————┤&lt;—由exploit填入数据的结束&nbsp;<BR>│&nbsp;……&nbsp;│&nbsp;<BR>├———————┤&lt;—高端内存区域&nbsp;</P>
<P><BR>windows下的动态数据除了可存放在栈中，还可以存放在堆中。了解C++的朋友都知道，C++可以使用new关键字来动态分配内存。来看下面的C++代码：&nbsp;</P>
<P>#include&nbsp;&lt;stdio.h&gt;&nbsp;<BR>#include&nbsp;&lt;iostream.h&gt;&nbsp;<BR>#include&nbsp;&lt;windows.h&gt;&nbsp;</P>
<P>void&nbsp;func()&nbsp;<BR>{&nbsp;<BR>char&nbsp;*buffer=new&nbsp;char[128];&nbsp;<BR>char&nbsp;bufflocal[128];&nbsp;<BR>static&nbsp;char&nbsp;buffstatic[128];&nbsp;<BR>printf("0x%08x\n",buffer);&nbsp;//打印堆中变量的内存地址&nbsp;<BR>printf("0x%08x\n",bufflocal);&nbsp;//打印本地变量的内存地址&nbsp;<BR>printf("0x%08x\n",buffstatic);&nbsp;//打印静态变量的内存地址&nbsp;<BR>}&nbsp;</P>
<P>void&nbsp;main()&nbsp;<BR>{&nbsp;<BR>func();&nbsp;<BR>return;&nbsp;<BR>}&nbsp;</P>
<P>程序执行结果为：&nbsp;</P>
<P>0x004107d0&nbsp;<BR>0x0012ff04&nbsp;<BR>0x004068c0&nbsp;</P>
<P>可以发现用new关键字分配的内存即不在栈中，也不在静态数据区。VC编译器是通过windows下的“堆(heap)”来实现new关键字的内存动态分配。在讲“堆”之前，先来了解一下和“堆”有关的几个API函数：&nbsp;</P>
<P>HeapAlloc&nbsp;在堆中申请内存空间&nbsp;<BR>HeapCreate&nbsp;创建一个新的堆对象&nbsp;<BR>HeapDestroy&nbsp;销毁一个堆对象&nbsp;<BR>HeapFree&nbsp;释放申请的内存&nbsp;<BR>HeapWalk&nbsp;枚举堆对象的所有内存块&nbsp;<BR>GetProcessHeap&nbsp;取得进程的默认堆对象&nbsp;<BR>GetProcessHeaps&nbsp;取得进程所有的堆对象&nbsp;<BR>LocalAlloc&nbsp;<BR>GlobalAlloc&nbsp;</P>
<P>当进程初始化时，系统会自动为进程创建一个默认堆，这个堆默认所占内存的大小为1M。堆对象由系统进行管理，它在内存中以链式结构存在。通过下面的代码可以通过堆动态申请内存空间：&nbsp;</P>
<P>HANDLE&nbsp;hHeap=GetProcessHeap();&nbsp;<BR>char&nbsp;*buff=HeapAlloc(hHeap,0,8);&nbsp;</P>
<P>其中hHeap是堆对象的句柄，buff是指向申请的内存空间的地址。那这个hHeap究竟是什么呢？它的值有什么意义吗？看看下面这段代码吧：&nbsp;</P>
<P>#pragma&nbsp;comment(linker,"/entry:main")&nbsp;//定义程序的入口&nbsp;<BR>#include&nbsp;&lt;windows.h&gt;&nbsp;</P>
<P>_CRTIMP&nbsp;int&nbsp;(__cdecl&nbsp;*printf)(const&nbsp;char&nbsp;*,&nbsp;...);&nbsp;//定义STL函数printf&nbsp;<BR>/*---------------------------------------------------------------------------&nbsp;<BR>写到这里，我们顺便来复习一下前面所讲的知识：&nbsp;<BR>(*注)printf函数是C语言的标准函数库中函数，VC的标准函数库由msvcrt.dll模块实现。&nbsp;<BR>由函数定义可见，printf的参数个数是可变的，函数内部无法预先知道调用者压入的参数个数，函数只能通过分析第一个参数字符串的格式来获得压入参数的信息，由于这里参数的个数是动态的，所以必须由调用者来平衡堆栈，这里便使用了__cdecl调用规则。BTW，Windows系统的API函数基本上是__stdcall调用形式，只有一个API例外，那就是wsprintf，它使用__cdecl调用规则，同printf函数一样，这是由于它的参数个数是可变的缘故。&nbsp;<BR>---------------------------------------------------------------------------*/&nbsp;<BR>void&nbsp;main()&nbsp;<BR>{&nbsp;<BR>HANDLE&nbsp;hHeap=GetProcessHeap();&nbsp;<BR>char&nbsp;*buff=HeapAlloc(hHeap,0,0x10);&nbsp;<BR>char&nbsp;*buff2=HeapAlloc(hHeap,0,0x10);&nbsp;<BR>HMODULE&nbsp;hMsvcrt=LoadLibrary("msvcrt.dll");&nbsp;<BR>printf=(void&nbsp;*)GetProcAddress(hMsvcrt,"printf");&nbsp;<BR>printf("0x%08x\n",hHeap);&nbsp;<BR>printf("0x%08x\n",buff);&nbsp;<BR>printf("0x%08x\n\n",buff2);&nbsp;<BR>}&nbsp;</P>
<P>执行结果为：&nbsp;</P>
<P>0x00130000&nbsp;<BR>0x00133100&nbsp;<BR>0x00133118&nbsp;</P>
<P>hHeap的值怎么和那个buff的值那么接近呢？其实hHeap这个句柄就是指向HEAP首部的地址。在进程的用户区存着一个叫PEB(进程环境块)的结构，这个结构中存放着一些有关进程的重要信息，其中在PEB首地址偏移0x18处存放的ProcessHeap就是进程默认堆的地址，而偏移0x90处存放了指向进程所有堆的地址列表的指针。windows有很多API都使用进程的默认堆来存放动态数据，如windows&nbsp;2000下的所有ANSI版本的函数都是在默认堆中申请内存来转换ANSI字符串到Unicode字符串的。对一个堆的访问是顺序进行的，同一时刻只能有一个线程访问堆中的数据，当多个线程同时有访问要求时，只能排队等待，这样便造成程序执行效率下降。&nbsp;</P>
<P>最后来说说内存中的数据对齐。所位数据对齐，是指数据所在的内存地址必须是该数据长度的整数倍，DWORD数据的内存起始地址能被4除尽，WORD数据的内存起始地址能被2除尽，x86&nbsp;CPU能直接访问对齐的数据，当他试图访问一个未对齐的数据时，会在内部进行一系列的调整，这些调整对于程序来说是透明的，但是会降低运行速度，所以编译器在编译程序时会尽量保证数据对齐。同样一段代码，我们来看看用VC、Dev-C++和lcc三个不同编译器编译出来的程序的执行结果：&nbsp;</P>
<P>#include&nbsp;&lt;stdio.h&gt;&nbsp;</P>
<P>int&nbsp;main()&nbsp;<BR>{&nbsp;<BR>int&nbsp;a;&nbsp;<BR>char&nbsp;b;&nbsp;<BR>int&nbsp;c;&nbsp;<BR>printf("0x%08x\n",&amp;a);&nbsp;<BR>printf("0x%08x\n",&amp;b);&nbsp;<BR>printf("0x%08x\n",&amp;c);&nbsp;<BR>return&nbsp;0;&nbsp;<BR>}&nbsp;</P>
<P>这是用VC编译后的执行结果：&nbsp;<BR>0x0012ff7c&nbsp;<BR>0x0012ff7b&nbsp;<BR>0x0012ff80&nbsp;<BR>变量在内存中的顺序：b(1字节)-a(4字节)-c(4字节)。&nbsp;</P>
<P>这是用Dev-C++编译后的执行结果：&nbsp;<BR>0x0022ff7c&nbsp;<BR>0x0022ff7b&nbsp;<BR>0x0022ff74&nbsp;<BR>变量在内存中的顺序：c(4字节)-中间相隔3字节-b(占1字节)-a(4字节)。&nbsp;</P>
<P>这是用lcc编译后的执行结果：&nbsp;<BR>0x0012ff6c&nbsp;<BR>0x0012ff6b&nbsp;<BR>0x0012ff64&nbsp;<BR>变量在内存中的顺序：同上。&nbsp;</P>
<P>三个编译器都做到了数据对齐，但是后两个编译器显然没VC“聪明”，让一个char占了4字节，浪费内存哦。&nbsp;</P>
<P><BR>基础知识：&nbsp;<BR>堆栈是一种简单的数据结构，是一种只允许在其一端进行插入或删除的线性表。允许插入或删除操作的一端称为栈顶，另一端称为栈底，对堆栈的插入和删除操作被称为入栈和出栈。有一组CPU指令可以实现对进程的内存实现堆栈访问。其中，POP指令实现出栈操作，PUSH指令实现入栈操作。CPU的ESP寄存器存放当前线程的栈顶指针，EBP寄存器中保存当前线程的栈底指针。CPU的EIP寄存器存放下一个CPU指令存放的内存地址，当CPU执行完当前的指令后，从EIP寄存器中读取下一条指令的内存地址，然后继续执行。&nbsp;</P>
<P><BR>参考：《Windows下的HEAP溢出及其利用》by:&nbsp;isno&nbsp;<BR>《windows核心编程》by:&nbsp;Jeffrey&nbsp;Richter&nbsp;<BR><BR><BR><BR></P>
<P>摘要：&nbsp;讨论常见的堆性能问题以及如何防范它们。（共&nbsp;9&nbsp;页）</P>
<P>前言<BR>您是否是动态分配的&nbsp;C/C++&nbsp;对象忠实且幸运的用户？您是否在模块间的往返通信中频繁地使用了“自动化”？您的程序是否因堆分配而运行起来很慢？不仅仅您遇到这样的问题。几乎所有项目迟早都会遇到堆问题。大家都想说，“我的代码真正好，只是堆太慢”。那只是部分正确。更深入理解堆及其用法、以及会发生什么问题，是很有用的。</P>
<P>什么是堆？<BR>（如果您已经知道什么是堆，可以跳到“什么是常见的堆性能问题？”部分）</P>
<P>在程序中，使用堆来动态分配和释放对象。在下列情况下，调用堆操作：&nbsp;</P>
<P>事先不知道程序所需对象的数量和大小。</P>
<P><BR>对象太大而不适合堆栈分配程序。<BR>堆使用了在运行时分配给代码和堆栈的内存之外的部分内存。下图给出了堆分配程序的不同层。<BR><A href="http://club.5ivb.net/UploadFile/2005311144027byUID16686.gif" target=_blank><IMG alt="" src="http://club.5ivb.net/UploadFile/2005311144027byUID16686.gif" onload="javascript:if(this.width>screen.width-333)this.width=screen.width-333" border=0 twffan="done" dypop="按此在新窗口浏览图片"></A></P>
<P>GlobalAlloc/GlobalFree：Microsoft&nbsp;Win32&nbsp;堆调用，这些调用直接与每个进程的默认堆进行对话。</P>
<P>LocalAlloc/LocalFree：Win32&nbsp;堆调用（为了与&nbsp;Microsoft&nbsp;Windows&nbsp;NT&nbsp;兼容），这些调用直接与每个进程的默认堆进行对话。</P>
<P>COM&nbsp;的&nbsp;IMalloc&nbsp;分配程序（或&nbsp;CoTaskMemAlloc&nbsp;/&nbsp;CoTaskMemFree）：函数使用每个进程的默认堆。自动化程序使用“组件对象模型&nbsp;(COM)”的分配程序，而申请的程序使用每个进程堆。</P>
<P>C/C++&nbsp;运行时&nbsp;(CRT)&nbsp;分配程序：提供了&nbsp;malloc()&nbsp;和&nbsp;free()&nbsp;以及&nbsp;new&nbsp;和&nbsp;delete&nbsp;操作符。如&nbsp;Microsoft&nbsp;Visual&nbsp;Basic&nbsp;和&nbsp;Java&nbsp;等语言也提供了新的操作符并使用垃圾收集来代替堆。CRT&nbsp;创建自己的私有堆，驻留在&nbsp;Win32&nbsp;堆的顶部。</P>
<P>Windows&nbsp;NT&nbsp;中，Win32&nbsp;堆是&nbsp;Windows&nbsp;NT&nbsp;运行时分配程序周围的薄层。所有&nbsp;API&nbsp;转发它们的请求给&nbsp;NTDLL。</P>
<P>Windows&nbsp;NT&nbsp;运行时分配程序提供&nbsp;Windows&nbsp;NT&nbsp;内的核心堆分配程序。它由具有&nbsp;128&nbsp;个大小从&nbsp;8&nbsp;到&nbsp;1,024&nbsp;字节的空闲列表的前端分配程序组成。后端分配程序使用虚拟内存来保留和提交页。</P>
<P>在图表的底部是“虚拟内存分配程序”，操作系统使用它来保留和提交页。所有分配程序使用虚拟内存进行数据的存取。</P>
<P>分配和释放块不就那么简单吗？为何花费这么长时间？</P>
<P>堆实现的注意事项<BR>传统上，操作系统和运行时库是与堆的实现共存的。在一个进程的开始，操作系统创建一个默认堆，叫做“进程堆”。如果没有其他堆可使用，则块的分配使用“进程堆”。语言运行时也能在进程内创建单独的堆。（例如，C&nbsp;运行时创建它自己的堆。）除这些专用的堆外，应用程序或许多已载入的动态链接库&nbsp;(DLL)&nbsp;之一可以创建和使用单独的堆。Win32&nbsp;提供一整套&nbsp;API&nbsp;来创建和使用私有堆。有关堆函数（英文）的详尽指导，请参见&nbsp;MSDN。</P>
<P>当应用程序或&nbsp;DLL&nbsp;创建私有堆时，这些堆存在于进程空间，并且在进程内是可访问的。从给定堆分配的数据将在同一个堆上释放。（不能从一个堆分配而在另一个堆释放。）</P>
<P>在所有虚拟内存系统中，堆驻留在操作系统的“虚拟内存管理器”的顶部。语言运行时堆也驻留在虚拟内存顶部。某些情况下，这些堆是操作系统堆中的层，而语言运行时堆则通过大块的分配来执行自己的内存管理。不使用操作系统堆，而使用虚拟内存函数更利于堆的分配和块的使用。</P>
<P>典型的堆实现由前、后端分配程序组成。前端分配程序维持固定大小块的空闲列表。对于一次分配调用，堆尝试从前端列表找到一个自由块。如果失败，堆被迫从后端（保留和提交虚拟内存）分配一个大块来满足请求。通用的实现有每块分配的开销，这将耗费执行周期，也减少了可使用的存储空间。</P>
<P>Knowledge&nbsp;Base&nbsp;文章&nbsp;Q10758，“用&nbsp;calloc()&nbsp;和&nbsp;malloc()&nbsp;管理内存”&nbsp;（搜索文章编号）,&nbsp;包含了有关这些主题的更多背景知识。另外，有关堆实现和设计的详细讨论也可在下列著作中找到：“Dynamic&nbsp;Storage&nbsp;Allocation:&nbsp;A&nbsp;Survey&nbsp;and&nbsp;Critical&nbsp;Review”，作者&nbsp;Paul&nbsp;R.&nbsp;Wilson、Mark&nbsp;S.&nbsp;Johnstone、Michael&nbsp;Neely&nbsp;和&nbsp;David&nbsp;Boles；“International&nbsp;Workshop&nbsp;on&nbsp;Memory&nbsp;Management”,&nbsp;作者&nbsp;Kinross,&nbsp;Scotland,&nbsp;UK,&nbsp;1995&nbsp;年&nbsp;9&nbsp;月(<IMG src="http://club.5ivb.net/pic/url.gif" align=absMiddle border=0 twffan="done"><A href="http://www.cs.utexas.edu/users/oops/papers.html" target=_blank><FONT color=#000000>http://www.cs.utexas.edu/users/oops/papers.html</FONT></A>)（英文）。</P>
<P>Windows&nbsp;NT&nbsp;的实现（Windows&nbsp;NT&nbsp;版本&nbsp;4.0&nbsp;和更新版本）&nbsp;使用了&nbsp;127&nbsp;个大小从&nbsp;8&nbsp;到&nbsp;1,024&nbsp;字节的&nbsp;8&nbsp;字节对齐块空闲列表和一个“大块”列表。“大块”列表（空闲列表[0]）&nbsp;保存大于&nbsp;1,024&nbsp;字节的块。空闲列表容纳了用双向链表链接在一起的对象。默认情况下，“进程堆”执行收集操作。（收集是将相邻空闲块合并成一个大块的操作。）收集耗费了额外的周期，但减少了堆块的内部碎片。</P>
<P>单一全局锁保护堆，防止多线程式的使用。（请参见“Server&nbsp;Performance&nbsp;and&nbsp;Scalability&nbsp;Killers”中的第一个注意事项,&nbsp;George&nbsp;Reilly&nbsp;所著，在&nbsp;“MSDN&nbsp;Online&nbsp;Web&nbsp;Workshop”上（站点：<IMG src="http://club.5ivb.net/pic/url.gif" align=absMiddle border=0 twffan="done"><A href="http://msdn.microsoft.com/workshop/server/iis/tencom.asp" target=_blank><FONT color=#000000>http://msdn.microsoft.com/workshop/server/iis/tencom.asp</FONT></A>（英文）。）单一全局锁本质上是用来保护堆数据结构，防止跨多线程的随机存取。若堆操作太频繁，单一全局锁会对性能有不利的影响。</P>
<P>什么是常见的堆性能问题？<BR>以下是您使用堆时会遇到的最常见问题：&nbsp;</P>
<P>分配操作造成的速度减慢。光分配就耗费很长时间。最可能导致运行速度减慢原因是空闲列表没有块，所以运行时分配程序代码会耗费周期寻找较大的空闲块，或从后端分配程序分配新块。</P>
<P><BR>释放操作造成的速度减慢。释放操作耗费较多周期，主要是启用了收集操作。收集期间，每个释放操作“查找”它的相邻块，取出它们并构造成较大块，然后再把此较大块插入空闲列表。在查找期间，内存可能会随机碰到，从而导致高速缓存不能命中，性能降低。</P>
<P><BR>堆竞争造成的速度减慢。当两个或多个线程同时访问数据，而且一个线程继续进行之前必须等待另一个线程完成时就发生竞争。竞争总是导致麻烦；这也是目前多处理器系统遇到的最大问题。当大量使用内存块的应用程序或&nbsp;DLL&nbsp;以多线程方式运行（或运行于多处理器系统上）时将导致速度减慢。单一锁定的使用—常用的解决方案—意味着使用堆的所有操作是序列化的。当等待锁定时序列化会引起线程切换上下文。可以想象交叉路口闪烁的红灯处走走停停导致的速度减慢。&nbsp;<BR>竞争通常会导致线程和进程的上下文切换。上下文切换的开销是很大的，但开销更大的是数据从处理器高速缓存中丢失，以及后来线程复活时的数据重建。</P>
<P>堆破坏造成的速度减慢。造成堆破坏的原因是应用程序对堆块的不正确使用。通常情形包括释放已释放的堆块或使用已释放的堆块，以及块的越界重写等明显问题。（破坏不在本文讨论范围之内。有关内存重写和泄漏等其他细节，请参见&nbsp;Microsoft&nbsp;Visual&nbsp;C++(R)&nbsp;调试文档&nbsp;。）</P>
<P><BR>频繁的分配和重分配造成的速度减慢。这是使用脚本语言时非常普遍的现象。如字符串被反复分配，随重分配增长和释放。不要这样做，如果可能，尽量分配大字符串和使用缓冲区。另一种方法就是尽量少用连接操作。<BR>竞争是在分配和释放操作中导致速度减慢的问题。理想情况下，希望使用没有竞争和快速分配/释放的堆。可惜，现在还没有这样的通用堆，也许将来会有。</P>
<P>在所有的服务器系统中（如&nbsp;IIS、MSProxy、DatabaseStacks、网络服务器、&nbsp;Exchange&nbsp;和其他）,&nbsp;堆锁定实在是个大瓶颈。处理器数越多，竞争就越会恶化。</P>
<P>尽量减少堆的使用<BR>现在您明白使用堆时存在的问题了，难道您不想拥有能解决这些问题的超级魔棒吗？我可希望有。但没有魔法能使堆运行加快—因此不要期望在产品出货之前的最后一星期能够大为改观。如果提前规划堆策略，情况将会大大好转。调整使用堆的方法，减少对堆的操作是提高性能的良方。</P>
<P>如何减少使用堆操作？通过利用数据结构内的位置可减少堆操作的次数。请考虑下列实例：</P>
<P>struct&nbsp;ObjectA&nbsp;{<BR>&nbsp;&nbsp;&nbsp;//&nbsp;objectA&nbsp;的数据&nbsp;<BR>}</P>
<P>struct&nbsp;ObjectB&nbsp;{<BR>&nbsp;&nbsp;&nbsp;//&nbsp;objectB&nbsp;的数据&nbsp;<BR>}</P>
<P>//&nbsp;同时使用&nbsp;objectA&nbsp;和&nbsp;objectB</P>
<P>//<BR>//&nbsp;使用指针&nbsp;<BR>//<BR>struct&nbsp;ObjectB&nbsp;{<BR>&nbsp;&nbsp;&nbsp;struct&nbsp;ObjectA&nbsp;*&nbsp;pObjA;<BR>&nbsp;&nbsp;&nbsp;//&nbsp;objectB&nbsp;的数据&nbsp;<BR>}</P>
<P>//<BR>//&nbsp;使用嵌入<BR>//<BR>struct&nbsp;ObjectB&nbsp;{<BR>&nbsp;&nbsp;&nbsp;struct&nbsp;ObjectA&nbsp;pObjA;<BR>&nbsp;&nbsp;&nbsp;//&nbsp;objectB&nbsp;的数据&nbsp;<BR>}</P>
<P>//<BR>//&nbsp;集合&nbsp;–&nbsp;在另一对象内使用&nbsp;objectA&nbsp;和&nbsp;objectB<BR>//</P>
<P>struct&nbsp;ObjectX&nbsp;{<BR>&nbsp;&nbsp;&nbsp;struct&nbsp;ObjectA&nbsp;&nbsp;objA;<BR>&nbsp;&nbsp;&nbsp;struct&nbsp;ObjectB&nbsp;&nbsp;objB;<BR>}</P>
<P>避免使用指针关联两个数据结构。如果使用指针关联两个数据结构，前面实例中的对象&nbsp;A&nbsp;和&nbsp;B&nbsp;将被分别分配和释放。这会增加额外开销—我们要避免这种做法。</P>
<P><BR>把带指针的子对象嵌入父对象。当对象中有指针时，则意味着对象中有动态元素（百分之八十）和没有引用的新位置。嵌入增加了位置从而减少了进一步分配/释放的需求。这将提高应用程序的性能。</P>
<P><BR>合并小对象形成大对象（聚合）。聚合减少分配和释放的块的数量。如果有几个开发者，各自开发设计的不同部分，则最终会有许多小对象需要合并。集成的挑战就是要找到正确的聚合边界。</P>
<P><BR>内联缓冲区能够满足百分之八十的需要（aka&nbsp;80-20&nbsp;规则）。个别情况下，需要内存缓冲区来保存字符串/二进制数据，但事先不知道总字节数。估计并内联一个大小能满足百分之八十需要的缓冲区。对剩余的百分之二十，可以分配一个新的缓冲区和指向这个缓冲区的指针。这样，就减少分配和释放调用并增加数据的位置空间，从根本上提高代码的性能。</P>
<P><BR>在块中分配对象（块化）。块化是以组的方式一次分配多个对象的方法。如果对列表的项连续跟踪，例如对一个&nbsp;{名称，值}&nbsp;对的列表，有两种选择：选择一是为每一个“名称-值”对分配一个节点；选择二是分配一个能容纳（如五个）“名称-值”对的结构。例如，一般情况下，如果存储四对，就可减少节点的数量，如果需要额外的空间数量，则使用附加的链表指针。&nbsp;<BR>块化是友好的处理器高速缓存，特别是对于&nbsp;L1-高速缓存，因为它提供了增加的位置&nbsp;—不用说对于块分配，很多数据块会在同一个虚拟页中。</P>
<P>正确使用&nbsp;_amblksiz。C&nbsp;运行时&nbsp;(CRT)&nbsp;有它的自定义前端分配程序，该分配程序从后端（Win32&nbsp;堆）分配大小为&nbsp;_amblksiz&nbsp;的块。将&nbsp;_amblksiz&nbsp;设置为较高的值能潜在地减少对后端的调用次数。这只对广泛使用&nbsp;CRT&nbsp;的程序适用。<BR>使用上述技术将获得的好处会因对象类型、大小及工作量而有所不同。但总能在性能和可升缩性方面有所收获。另一方面，代码会有点特殊，但如果经过深思熟虑，代码还是很容易管理的。</P>
<P>其他提高性能的技术<BR>下面是一些提高速度的技术：&nbsp;</P>
<P>使用&nbsp;Windows&nbsp;NT5&nbsp;堆&nbsp;<BR>由于几个同事的努力和辛勤工作，1998&nbsp;年初&nbsp;Microsoft&nbsp;Windows(R)&nbsp;2000&nbsp;中有了几个重大改进：</P>
<P>改进了堆代码内的锁定。堆代码对每堆一个锁。全局锁保护堆数据结构，防止多线程式的使用。但不幸的是，在高通信量的情况下，堆仍受困于全局锁，导致高竞争和低性能。Windows&nbsp;2000&nbsp;中，锁内代码的临界区将竞争的可能性减到最小,从而提高了可伸缩性。</P>
<P><BR>使用&nbsp;“Lookaside”列表。堆数据结构对块的所有空闲项使用了大小在&nbsp;8&nbsp;到&nbsp;1,024&nbsp;字节（以&nbsp;8-字节递增）的快速高速缓存。快速高速缓存最初保护在全局锁内。现在，使用&nbsp;lookaside&nbsp;列表来访问这些快速高速缓存空闲列表。这些列表不要求锁定，而是使用&nbsp;64&nbsp;位的互锁操作，因此提高了性能。</P>
<P><BR>内部数据结构算法也得到改进。<BR>这些改进避免了对分配高速缓存的需求，但不排除其他的优化。使用&nbsp;Windows&nbsp;NT5&nbsp;堆评估您的代码；它对小于&nbsp;1,024&nbsp;字节&nbsp;(1&nbsp;KB)&nbsp;的块（来自前端分配程序的块）是最佳的。GlobalAlloc()&nbsp;和&nbsp;LocalAlloc()&nbsp;建立在同一堆上，是存取每个进程堆的通用机制。如果希望获得高的局部性能，则使用&nbsp;Heap(R)&nbsp;API&nbsp;来存取每个进程堆，或为分配操作创建自己的堆。如果需要对大块操作，也可以直接使用&nbsp;VirtualAlloc()&nbsp;/&nbsp;VirtualFree()&nbsp;操作。</P>
<P>上述改进已在&nbsp;Windows&nbsp;2000&nbsp;beta&nbsp;2&nbsp;和&nbsp;Windows&nbsp;NT&nbsp;4.0&nbsp;SP4&nbsp;中使用。改进后，堆锁的竞争率显著降低。这使所有&nbsp;Win32&nbsp;堆的直接用户受益。CRT&nbsp;堆建立于&nbsp;Win32&nbsp;堆的顶部，但它使用自己的小块堆，因而不能从&nbsp;Windows&nbsp;NT&nbsp;改进中受益。（Visual&nbsp;C++&nbsp;版本&nbsp;6.0&nbsp;也有改进的堆分配程序。）</P>
<P>使用分配高速缓存&nbsp;<BR>分配高速缓存允许高速缓存分配的块，以便将来重用。这能够减少对进程堆（或全局堆）的分配/释放调用的次数，也允许最大限度的重用曾经分配的块。另外，分配高速缓存允许收集统计信息,以便较好地理解对象在较高层次上的使用。</P>
<P>典型地，自定义堆分配程序在进程堆的顶部实现。自定义堆分配程序与系统堆的行为很相似。主要的差别是它在进程堆的顶部为分配的对象提供高速缓存。高速缓存设计成一套固定大小（如&nbsp;32&nbsp;字节、64&nbsp;字节、128&nbsp;字节等）。这一个很好的策略，但这种自定义堆分配程序丢失与分配和释放的对象相关的“语义信息”。&nbsp;</P>
<P>与自定义堆分配程序相反，“分配高速缓存”作为每类分配高速缓存来实现。除能够提供自定义堆分配程序的所有好处之外，它们还能够保留大量语义信息。每个分配高速缓存处理程序与一个目标二进制对象关联。它能够使用一套参数进行初始化，这些参数表示并发级别、对象大小和保持在空闲列表中的元素的数量等。分配高速缓存处理程序对象维持自己的私有空闲实体池（不超过指定的阀值）并使用私有保护锁。合在一起，分配高速缓存和私有锁减少了与主系统堆的通信量，因而提供了增加的并发、最大限度的重用和较高的可伸缩性。</P>
<P>需要使用清理程序来定期检查所有分配高速缓存处理程序的活动情况并回收未用的资源。如果发现没有活动，将释放分配对象的池，从而提高性能。</P>
<P>可以审核每个分配/释放活动。第一级信息包括对象、分配和释放调用的总数。通过查看它们的统计信息可以得出各个对象之间的语义关系。利用以上介绍的许多技术之一，这种关系可以用来减少内存分配。</P>
<P>分配高速缓存也起到了调试助手的作用，帮助您跟踪没有完全清除的对象数量。通过查看动态堆栈返回踪迹和除没有清除的对象之外的签名，甚至能够找到确切的失败的调用者。</P>
<P>MP&nbsp;堆&nbsp;<BR>MP&nbsp;堆是对多处理器友好的分布式分配的程序包，在&nbsp;Win32&nbsp;SDK（Windows&nbsp;NT&nbsp;4.0&nbsp;和更新版本）中可以得到。最初由&nbsp;JVert&nbsp;实现，此处堆抽象建立在&nbsp;Win32&nbsp;堆程序包的顶部。MP&nbsp;堆创建多个&nbsp;Win32&nbsp;堆，并试图将分配调用分布到不同堆，以减少在所有单一锁上的竞争。</P>
<P>本程序包是好的步骤&nbsp;—一种改进的&nbsp;MP-友好的自定义堆分配程序。但是，它不提供语义信息和缺乏统计功能。通常将&nbsp;MP&nbsp;堆作为&nbsp;SDK&nbsp;库来使用。如果使用这个&nbsp;SDK&nbsp;创建可重用组件，您将大大受益。但是，如果在每个&nbsp;DLL&nbsp;中建立这个&nbsp;SDK&nbsp;库，将增加工作设置。</P>
<P>重新思考算法和数据结构&nbsp;<BR>要在多处理器机器上伸缩，则算法、实现、数据结构和硬件必须动态伸缩。请看最经常分配和释放的数据结构。试问，“我能用不同的数据结构完成此工作吗？”例如，如果在应用程序初始化时加载了只读项的列表，这个列表不必是线性链接的列表。如果是动态分配的数组就非常好。动态分配的数组将减少内存中的堆块和碎片，从而增强性能。</P>
<P>减少需要的小对象的数量减少堆分配程序的负载。例如，我们在服务器的关键处理路径上使用五个不同的对象，每个对象单独分配和释放。一起高速缓存这些对象，把堆调用从五个减少到一个，显著减少了堆的负载，特别当每秒钟处理&nbsp;1,000&nbsp;个以上的请求时。</P>
<P>如果大量使用“Automation”结构，请考虑从主线代码中删除“Automation&nbsp;BSTR”，或至少避免重复的&nbsp;BSTR&nbsp;操作。（BSTR&nbsp;连接导致过多的重分配和分配/释放操作。）</P>
<P>摘要<BR>对所有平台往往都存在堆实现，因此有巨大的开销。每个单独代码都有特定的要求，但设计能采用本文讨论的基本理论来减少堆之间的相互作用。&nbsp;</P>
<P>评价您的代码中堆的使用。</P>
<P><BR>改进您的代码，以使用较少的堆调用：分析关键路径和固定数据结构。</P>
<P><BR>在实现自定义的包装程序之前使用量化堆调用成本的方法。</P>
<P><BR>如果对性能不满意，请要求&nbsp;OS&nbsp;组改进堆。更多这类请求意味着对改进堆的更多关注。</P>
<P><BR>要求&nbsp;C&nbsp;运行时组针对&nbsp;OS&nbsp;所提供的堆制作小巧的分配包装程序。随着&nbsp;OS&nbsp;堆的改进，C&nbsp;运行时堆调用的成本将减小。</P>
<P><BR>操作系统（Windows&nbsp;NT&nbsp;家族）正在不断改进堆。请随时关注和利用这些改进。<BR>Murali&nbsp;Krishnan&nbsp;是&nbsp;Internet&nbsp;Information&nbsp;Server&nbsp;(IIS)&nbsp;组的首席软件设计工程师。从&nbsp;1.0&nbsp;版本开始他就设计&nbsp;IIS，并成功发行了&nbsp;1.0&nbsp;版本到&nbsp;4.0&nbsp;版本。Murali&nbsp;组织并领导&nbsp;IIS&nbsp;性能组三年&nbsp;(1995-1998),&nbsp;从一开始就影响&nbsp;IIS&nbsp;性能。他拥有威斯康星州&nbsp;Madison&nbsp;大学的&nbsp;M.S.和印度&nbsp;Anna&nbsp;大学的&nbsp;B.S.。工作之外，他喜欢阅读、打排球和家庭烹饪。<BR><BR><BR><BR><IMG src="http://club.5ivb.net/pic/url.gif" align=absMiddle border=0 twffan="done"><A href="http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=172835" target=_blank><FONT color=#000000>http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=172835</FONT></A><BR>我在学习对象的生存方式的时候见到一种是在堆栈(stack)之中，如下&nbsp;&nbsp;<BR>CObject&nbsp;&nbsp;object;&nbsp;&nbsp;<BR>还有一种是在堆(heap)中&nbsp;&nbsp;如下&nbsp;&nbsp;<BR>CObject*&nbsp;&nbsp;pobject=new&nbsp;&nbsp;CObject();&nbsp;&nbsp;<BR>&nbsp;<BR>请问&nbsp;&nbsp;<BR>（1）这两种方式有什么区别？&nbsp;&nbsp;<BR>（2）堆栈与堆有什么区别？？&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>---------------------------------------------------------------&nbsp;&nbsp;<BR>&nbsp;<BR>1)&nbsp;&nbsp;about&nbsp;&nbsp;stack,&nbsp;&nbsp;system&nbsp;&nbsp;will&nbsp;&nbsp;allocate&nbsp;&nbsp;memory&nbsp;&nbsp;to&nbsp;&nbsp;the&nbsp;&nbsp;instance&nbsp;&nbsp;of&nbsp;&nbsp;object&nbsp;&nbsp;automatically,&nbsp;&nbsp;and&nbsp;&nbsp;to&nbsp;&nbsp;the&nbsp;&nbsp;heap,&nbsp;&nbsp;you&nbsp;&nbsp;must&nbsp;&nbsp;allocate&nbsp;&nbsp;memory&nbsp;&nbsp;to&nbsp;&nbsp;the&nbsp;&nbsp;instance&nbsp;&nbsp;of&nbsp;&nbsp;object&nbsp;&nbsp;with&nbsp;&nbsp;new&nbsp;&nbsp;or&nbsp;&nbsp;malloc&nbsp;&nbsp;manually.&nbsp;&nbsp;<BR>2)&nbsp;&nbsp;when&nbsp;&nbsp;function&nbsp;&nbsp;ends,&nbsp;&nbsp;system&nbsp;&nbsp;will&nbsp;&nbsp;automatically&nbsp;&nbsp;free&nbsp;&nbsp;the&nbsp;&nbsp;memory&nbsp;&nbsp;area&nbsp;&nbsp;of&nbsp;&nbsp;stack,&nbsp;&nbsp;but&nbsp;&nbsp;to&nbsp;&nbsp;the&nbsp;&nbsp;heap,&nbsp;&nbsp;you&nbsp;&nbsp;must&nbsp;&nbsp;free&nbsp;&nbsp;the&nbsp;&nbsp;memory&nbsp;&nbsp;area&nbsp;&nbsp;manually&nbsp;&nbsp;with&nbsp;&nbsp;free&nbsp;&nbsp;or&nbsp;&nbsp;delete,&nbsp;&nbsp;else&nbsp;&nbsp;it&nbsp;&nbsp;will&nbsp;&nbsp;result&nbsp;&nbsp;in&nbsp;&nbsp;memory&nbsp;&nbsp;leak.&nbsp;&nbsp;<BR>3)栈内存分配运算内置于处理器的指令集中，效率很高，但是分配的内存容量有限。&nbsp;&nbsp;<BR>4）堆上分配的内存可以有我们自己决定，使用非常灵活。&nbsp;&nbsp;<BR>---------------------------------------------------------------&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>堆和栈的比较&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的，栈主要是用来执行程序的.而这种不同又主要是由于堆和栈的特点决定的:&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在编程中，例如C/C++中，所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyor&nbsp;&nbsp;belt)一样,Stack&nbsp;&nbsp;Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候，修改栈指针就可以把栈中的内容销毁.这样的模式速度最快,当然要用来运行程序了.需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程序运行时进行的,但是分配的大小多少是确定的,不变的,而这个"大小多少"是在编译时确定的,不是在运行时.&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;堆是应用程序在运行的时候请求操作系统分配给自己内存，由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间，因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间，也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中，要求创建一个对象时，只需用new命令编制相关的代码即可。执行这些代码时，会在堆里自动进行数据的保存.当然，为达到这种灵活性，必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间！这也正是导致效率低的原因,&nbsp;&nbsp;<BR>&nbsp;<BR>我想你现在该明白了吧。：）&nbsp;&nbsp;</P></DIV><img src ="http://www.cppblog.com/fashion1840/aggbug/4221.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/fashion1840/" target="_blank">江南小生</a> 2006-03-16 09:59 <a href="http://www.cppblog.com/fashion1840/articles/4221.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>#pragma 预处理指令详解</title><link>http://www.cppblog.com/fashion1840/articles/4220.html</link><dc:creator>江南小生</dc:creator><author>江南小生</author><pubDate>Thu, 16 Mar 2006 01:40:00 GMT</pubDate><guid>http://www.cppblog.com/fashion1840/articles/4220.html</guid><wfw:comment>http://www.cppblog.com/fashion1840/comments/4220.html</wfw:comment><comments>http://www.cppblog.com/fashion1840/articles/4220.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/fashion1840/comments/commentRss/4220.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/fashion1840/services/trackbacks/4220.html</trackback:ping><description><![CDATA[<STRONG>#pragma 预处理指令详解<BR></STRONG><SPAN class=tpc_content><FONT size=2><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在所有的预处理指令中，#Pragma 指令可能是最复杂的了，它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。 <BR>其格式一般为: &nbsp; #Pragma Para <BR>&nbsp; 其中Para 为参数，下面来看一些常用的参数。 <BR><BR>&nbsp; (1)message 参数。 Message 参数是我最喜欢的一个参数，它能够在编译信息输出窗 <BR>口中输出相应的信息，这对于源代码信息的控制是非常重要的。其使用方法为： <BR>&nbsp; &nbsp; #Pragma message(“消息文本”) <BR>&nbsp; &nbsp; 当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。 <BR>&nbsp; 当我们在程序中定义了许多宏来控制源代码版本的时候，我们自己有可能都会忘记有没有正确的设置这些宏，此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法 <BR>&nbsp; &nbsp; #ifdef _X86 <BR>&nbsp; &nbsp; #Pragma message(“_X86 macro activated!”) <BR>&nbsp; &nbsp; #endif <BR>&nbsp; &nbsp; 当我们定义了_X86这个宏以后，应用程序在编译时就会在编译输出窗口里显示“_ <BR>X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了 <BR>。 <BR>&nbsp; <BR>&nbsp; (2)另一个使用得比较多的pragma参数是code_seg。格式如： <BR>&nbsp; &nbsp; #pragma code_seg( ["section-name"[,"section-class"] ] ) <BR>&nbsp; &nbsp; 它能够设置程序中函数代码存放的代码段，当我们开发驱动程序的时候就会使用到它。 <BR><BR>&nbsp; (3)#pragma once (比较常用） <BR>&nbsp; &nbsp; 只要在头文件的最开始加入这条指令就能够保证头文件被编译一次，这条指令实际上在VC6中就已经有了，但是考虑到兼容性并没有太多的使用它。 <BR>&nbsp; <BR>&nbsp; (4)#pragma hdrstop表示预编译头文件到此为止，后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度，但如果所有头文件都进行预编译又可能占太多磁盘空间，所以使用这个选项排除一些头文件。 &nbsp; <BR>&nbsp; 有时单元之间有依赖关系，比如单元A依赖单元B，所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级，如果使用了#pragma package(smart_init) ，BCB就会根据优先级的大小先后编译。 &nbsp; <BR>&nbsp; <BR>&nbsp; (5)#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体 <BR>外观的定义。 &nbsp; <BR>&nbsp; <BR>&nbsp; (6)#pragma warning( disable : 4507 34; once : 4385; error : 164 ) <BR>&nbsp; &nbsp; 等价于： <BR>&nbsp; &nbsp; #pragma warning(disable:4507 34) // 不显示4507和34号警告信息 <BR>&nbsp; &nbsp; #pragma warning(once:4385) &nbsp; &nbsp; // 4385号警告信息仅报告一次 <BR>&nbsp; &nbsp; #pragma warning(error:164) &nbsp; &nbsp; // 把164号警告信息作为一个错误。 <BR>&nbsp; &nbsp; 同时这个pragma warning 也支持如下格式： <BR>&nbsp; &nbsp; #pragma warning( push [ ,n ] ) <BR>&nbsp; &nbsp; #pragma warning( pop ) <BR>&nbsp; &nbsp; 这里n代表一个警告等级(1---4)。 <BR>&nbsp; &nbsp; #pragma warning( push )保存所有警告信息的现有的警告状态。 <BR>&nbsp; &nbsp; #pragma warning( push, n)保存所有警告信息的现有的警告状态，并且把全局警告 <BR>等级设定为n。 &nbsp; <BR>&nbsp; &nbsp; #pragma warning( pop )向栈中弹出最后一个警告信息，在入栈和出栈之间所作的 <BR>一切改动取消。例如： <BR>&nbsp; &nbsp; #pragma warning( push ) <BR>&nbsp; &nbsp; #pragma warning( disable : 4705 ) <BR>&nbsp; &nbsp; #pragma warning( disable : 4706 ) <BR>&nbsp; &nbsp; #pragma warning( disable : 4707 ) <BR>&nbsp; &nbsp; //....... <BR>&nbsp; &nbsp; #pragma warning( pop ) &nbsp; <BR>&nbsp; &nbsp; 在这段代码的最后，重新保存所有的警告信息(包括4705，4706和4707)。 <BR>&nbsp; （7）pragma comment(...) <BR>&nbsp; &nbsp; 该指令将一个注释记录放入一个对象文件或可执行文件中。 <BR>&nbsp; &nbsp; 常用的lib关键字，可以帮我们连入一个库文件。 <BR><BR><BR>每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。例如，对循环优化功能： <BR>#pragma loop_opt(on) &nbsp; &nbsp; // 激活 <BR>#pragma loop_opt(off) // 终止 <BR>有时，程序中会有些函数会使编译器发出你熟知而想忽略的警告，如“Parameter xxx is never used in function xxx”，可以这样： <BR>#pragma warn —100 &nbsp; &nbsp; // Turn off the warning message for warning #100 <BR>int insert_record(REC *r) <BR>{ /* function body */ } <BR>#pragma warn +100 &nbsp; &nbsp; &nbsp; &nbsp; // Turn the warning message for warning #100 back on <BR>函数会产生一条有唯一特征码100的警告信息，如此可暂时终止该警告。 <BR>每个编译器对#pragma的实现不同，在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。</FONT></SPAN><img src ="http://www.cppblog.com/fashion1840/aggbug/4220.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/fashion1840/" target="_blank">江南小生</a> 2006-03-16 09:40 <a href="http://www.cppblog.com/fashion1840/articles/4220.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>