﻿<?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++博客-Welcome to ErranLi's Blog!</title><link>http://www.cppblog.com/erran/</link><description /><language>zh-cn</language><lastBuildDate>Mon, 08 Sep 2008 00:14:41 GMT</lastBuildDate><pubDate>Mon, 08 Sep 2008 00:14:41 GMT</pubDate><ttl>60</ttl><item><title>ARM &amp; DSP 。。。。。。。。。</title><link>http://www.cppblog.com/erran/archive/2008/07/20/56671.html</link><dc:creator>erran</dc:creator><author>erran</author><pubDate>Sun, 20 Jul 2008 03:41:00 GMT</pubDate><guid>http://www.cppblog.com/erran/archive/2008/07/20/56671.html</guid><wfw:comment>http://www.cppblog.com/erran/comments/56671.html</wfw:comment><comments>http://www.cppblog.com/erran/archive/2008/07/20/56671.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/erran/comments/commentRss/56671.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/erran/services/trackbacks/56671.html</trackback:ping><description><![CDATA[ <br />想买套嵌入式的硬件玩玩，查了很长时间的资料，确定了两个方面arm，dsp。公司做的东西主要用到的是dsp，惭愧的做软件的没什么机会接触那些。<br /><br />看了arm和dsp的比较，arm对操作系统的支持很好，控制性能很好，dsp的数据处理能力......<br /><br />想先买个arm板试试，考虑了很久最后还是决定买周立功的，虽然价格贵了点，资料全些。 MagicARM2410没查到价钱，估计在5k以上吧，没法接受..... smartArm2200不到2k，勉强还行。。<br /><br />今天是星期日，打他们代理电话，说只能周一到周五才能买到，晕死，周立功不会这么牛吧......还有这样做生意的。。。。。<br /><br />《打听到的DSP价格 SEED-XDSusbUSB2.0（1.7k含税公司采购价） SEED-DPS2812/Kit（1.5k左右吧） 》<br /><br /><br />这里应该有很多是做这个的吧，毕竟是Cpp blog， o(∩_∩)o... 恳请给点建议....<br /><br />《《《不好意思，我发到了“首页原创区”，谅解啊 o(∩_∩)o...》》》》<br /><img src ="http://www.cppblog.com/erran/aggbug/56671.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/erran/" target="_blank">erran</a> 2008-07-20 11:41 <a href="http://www.cppblog.com/erran/archive/2008/07/20/56671.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：字节对齐详解</title><link>http://www.cppblog.com/erran/archive/2007/10/16/34383.html</link><dc:creator>erran</dc:creator><author>erran</author><pubDate>Tue, 16 Oct 2007 14:56:00 GMT</pubDate><guid>http://www.cppblog.com/erran/archive/2007/10/16/34383.html</guid><wfw:comment>http://www.cppblog.com/erran/comments/34383.html</wfw:comment><comments>http://www.cppblog.com/erran/archive/2007/10/16/34383.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/erran/comments/commentRss/34383.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/erran/services/trackbacks/34383.html</trackback:ping><description><![CDATA[
		<br />看了这篇（http://www.cppblog.com/Randy/archive/2007/10/15/34288.aspx）关于字节对其的随笔，觉得迷迷糊糊的，脑袋里只有字节对其这个概念，具体都忘的差不多了，又另外找了篇细读了下，总算理解了。<br /><br />来源：http://www.yuanma.org/data/2006/0723/article_1213.htm<br /><h3>一.什么是字节对齐,为什么要对齐?</h3><p>   
现代计算机中内存空间都是按照byte划分的，从理论上讲似乎对任何类型的变量的访问可以从任何地址开始，但实际情况是在访问特定类型变量的时候经常在特
定的内存地址访问，这就需要各种类型数据按照一定的规则在空间上排列，而不是顺序的一个接一个的排放，这就是对齐。<br />   
对齐的作用和原因：各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问
一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况，但是最常见的是如果不按照适合其平台要求对
数据存放进行对齐，会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始，如果一个int型（假设为32位系统）如果存放在偶地址开始的地方，那
么一个读周期就可以读出这32bit，而如果存放在奇地址开始的地方，就需要2个读周期，并对两次读出的结果的高低字节进行拼凑才能得到该32bit数
据。显然在读取效率上下降很多。</p><h3>二.字节对齐对程序的影响:</h3><p>    先让我们看几个例子吧(32bit,x86环境,gcc编译器):<br />设结构体如下定义：<br />struct A<br />{<br />    int a;<br />    char b;<br />    short c;<br />};<br />struct B<br />{<br />    char b;<br />    int a;<br />    short c;<br />};<br />现在已知32位机器上各种数据类型的长度如下:<br />char:1(有符号无符号同)    <br />short:2(有符号无符号同)    <br />int:4(有符号无符号同)    <br />long:4(有符号无符号同)    <br />float:4    double:8<br />那么上面两个结构大小如何呢?<br />结果是:<br />sizeof(strcut A)值为8<br />sizeof(struct B)的值却是12</p><p>结构体A中包含了4字节长度的int一个，1字节长度的char一个和2字节长度的short型数据一个,B也一样;按理说A,B大小应该都是7字节。<br />之所以出现上面的结果是因为编译器要对数据成员在空间上进行对齐。上面是按照编译器的默认设置进行对齐的结果,那么我们是不是可以改变编译器的这种默认对齐设置呢,当然可以.例如:<br />#pragma pack (2) /*指定按2字节对齐*/<br />struct C<br />{<br />    char b;<br />    int a;<br />    short c;<br />};<br />#pragma pack () /*取消指定对齐，恢复缺省对齐*/<br />sizeof(struct C)值是8。<br />修改对齐值为1：<br />#pragma pack (1) /*指定按1字节对齐*/<br />struct D<br />{<br />    char b;<br />    int a;<br />    short c;<br />};<br />#pragma pack () /*取消指定对齐，恢复缺省对齐*/<br />sizeof(struct D)值为7。<br />后面我们再讲解#pragma pack()的作用.</p><h3>三.编译器是按照什么样的原则进行对齐的?</h3><p>    先让我们看四个重要的基本概念：<br /><font color="#0000ff">1.数据类型自身的对齐值：<br /></font>  对于char型数据，其自身对齐值为1，对于short型为2，对于int,float,double类型，其自身对齐值为4，单位字节。<br /><font color="#3300ff">2.结构体或者类的自身对齐值：</font>其成员中自身对齐值最大的那个值。<br /><font color="#0000ff">3.指定对齐值</font>：#pragma pack (value)时的指定对齐值value。<br /><font color="#0000ff">4.数据成员、结构体和类的有效对齐值：</font>自身对齐值和指定对齐值中小的那个值。<br />有
了这些值，我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值，最重要。有效对齐N，就是
表示“对齐在N上”，也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数
据结构的起始地址。结构体的成员变量要对齐排放，结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数
倍，结合下面例子理解)。这样就不能理解上面的几个例子的值了。<br />例子分析：<br />分析例子B；<br />struct B<br />{<br />    char b;<br />    int a;<br />    short c;<br />};<br />假
设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值，在笔者环境下，该值默认为4。第一个成员变量b的自身对齐值是1，比指定或者默认指定
对齐值4小，所以其有效对齐值为1，所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a，其自身对齐值为4，所以有效对齐值也为4，
所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中，复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐值为
2，所以有效对齐值也是2，可以存放在0x0008到0x0009这两个字节空间中，符合0x0008%2=0。所以从0x0000到0x0009存放的
都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b）所以就是4，所以结构体的有效对齐值也是4。根据结构体圆整的要求，
0x0009到0x0000=10字节，（10＋2）％4＝0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B
共有12个字节,sizeof(struct B)=12;<font color="#3300ff">其实如果就这一个就来说它已将满足字节对齐了,
因为它的起始地址是0,因此肯定是对齐的,之所以在后面补充2个字节,是因为编译器为了实现结构数组的存取效率,试想如果我们定义了一个结构B的数组,那
么第一个结构起始地址是0没有问题,但是第二个结构呢?按照数组的定义,数组中所有元素都是紧挨着的,如果我们不把结构的大小补充为4的整数倍,那么下一
个结构的起始地址将是0x0000A,这显然不能满足结构的地址对齐了,因此我们要把结构补充成有效对齐大小的整数倍.其实诸如:对于char型数据，其
自身对齐值为1，对于short型为2，对于int,float,double类型，其自身对齐值为4，这些已有类型的自身对齐值也是基于数组考虑的,只
是因为这些类型的长度已知了,所以他们的自身对齐值也就已知了.<br /></font>同理,分析上面例子C：<br />#pragma pack (2) /*指定按2字节对齐*/<br />struct C<br />{<br />    char b;<br />    int a;<br />    short c;<br />};<br />#pragma pack () /*取消指定对齐，恢复缺省对齐*/<br />第
一个变量b的自身对齐值为1，指定对齐值为2，所以，其有效对齐值为1，假设C从0x0000开始，那么b存放在0x0000，符合0x0000%1=
0;第二个变量，自身对齐值为4，指定对齐值为2，所以有效对齐值为2，所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续
字节中，符合0x0002%2=0。第三个变量c的自身对齐值为2，所以有效对齐值为2，顺序存放<br />在0x0006、0x0007中，符合
0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量。又C的自身对齐值为4，所以C的有效对齐值为2。又8%2=0,C
只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8.</p><h3>四.如何修改编译器的默认对齐值?</h3><p>1.在VC IDE中，可以这样修改：[Project]|[Settings],c/c++选项卡Category的Code Generation选项的Struct Member Alignment中修改，默认是8字节。<br />2.在编码时，可以这样动态修改：#pragma pack .<font color="#ff0000">注意:是pragma而不是progma.</font></p><h3>五.针对字节对齐,我们在编程中如何考虑?</h3><p><br />   
如果在编程的时候要考虑节约空间的话,那么我们只需要假定结构的首地址是0,然后各个变量按照上面的原则进行排列即可,基本的原则就是把结构中的变量按照
类型大小从小到大声明,尽量减少中间的填补空间.还有一种就是为了以空间换取时间的效率,我们显示的进行填补空间进行对齐,比如:有一种使用空间换时间做
法是显式的插入reserved成员：<br />         struct A{<br />           char a;<br />           char reserved[3];//使用空间换时间<br />           int b;<br />}<br /><br />reserved成员对我们的程序没有什么意义,它只是起到填补空间以达到字节对齐的目的,当然即使不加这个成员通常编译器也会给我们自动填补对齐,我们自己加上它只是起到显式的提醒作用.</p><h3>六.字节对齐可能带来的隐患:</h3><p>    代码中关于对齐的隐患，很多是隐式的。比如在强制类型转换的时候。例如：<br />unsigned int i = 0x12345678;<br />unsigned char *p=NULL;<br />unsigned short *p1=NULL;</p><p>p=&amp;i;<br />*p=0x00;<br />p1=(unsigned short *)(p+1);<br />*p1=0x0000;<br />最后两句代码，从奇数边界去访问unsignedshort型变量，显然不符合对齐的规定。<br />在x86上，类似的操作只会影响效率，但是在MIPS或者sparc上，可能就是一个error,因为它们要求必须字节对齐.</p><h3>七.如何查找与字节对齐方面的问题:</h3><p>如果出现对齐或者赋值问题首先查看<br />1. 编译器的big little端设置<br />2. 看这种体系本身是否支持非对齐访问<br />3. 如果支持看设置了对齐与否,如果没有则看访问时需要加某些特殊的修饰来标志其特殊访问操作。</p><p>八.相关文章:转自<a href="http://blog.csdn.net/goodluckyxl/archive/2005/10/17/506827.aspx"><u>http://blog.csdn.net/goodluckyxl/archive/2005/10/17/506827.aspx</u></a></p><quote></quote><p> ARM下的对齐处理 <br />from DUI0067D_ADS1_2_CompLib </p><p>3.13 type  qulifiers </p><p>有部分摘自ARM编译器文档对齐部分</p><p>对齐的使用:<br />1.__align(num)<br />   这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD时<br />   就要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。<br />   这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节<br />   对齐,但是不能让4字节的对象2字节对齐。<br />   __align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。<br />   <br />2.__packed <br />  __packed是进行一字节对齐<br />  1.不能对packed的对象进行对齐<br />  2.所有对象的读写访问都进行非对齐访问<br />  3.float及包含float的结构联合及未用__packed的对象将不能字节对齐<br />  4.__packed对局部整形变量无影响<br />  5.强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定<br />  义为packed。<br />     __packed int* p;  //__packed int 则没有意义<br />  6.对齐或非对齐读写访问带来问题<br />  __packed struct STRUCT_TEST<br /> {<br />  char a;<br />  int b;<br />  char c;<br /> }  ;    //定义如下结构此时b的起始地址一定是不对齐的<br />         //在栈中访问b可能有问题,因为栈上数据肯定是对齐访问[from CL]<br />//将下面变量定义成全局静态不在栈上 <br />static char* p;<br />static struct STRUCT_TEST a;<br />void Main()<br />{<br /> __packed int* q;  //此时定义成__packed来修饰当前q指向为非对齐的数据地址下面的访问则可以</p><p> p = (char*)&amp;a;          <br /> q = (int*)(p+1);      <br /> <br /> *q = 0x87654321; <br />/*   <br />得到赋值的汇编指令很清楚<br />ldr      r5,0x20001590 ; = #0x12345678<br />[0xe1a00005]   mov      r0,r5<br />[0xeb0000b0]   bl       __rt_uwrite4  //在此处调用一个写4byte的操作函数 <br />      <br />[0xe5c10000]   strb     r0,[r1,#0]   //函数进行4次strb操作然后返回保证了数据正确的访问<br />[0xe1a02420]   mov      r2,r0,lsr #8<br />[0xe5c12001]   strb     r2,[r1,#1]<br />[0xe1a02820]   mov      r2,r0,lsr #16<br />[0xe5c12002]   strb     r2,[r1,#2]<br />[0xe1a02c20]   mov      r2,r0,lsr #24<br />[0xe5c12003]   strb     r2,[r1,#3]<br />[0xe1a0f00e]   mov      pc,r14<br />*/</p><p>/*<br />如果q没有加__packed修饰则汇编出来指令是这样直接会导致奇地址处访问失败<br />[0xe59f2018]   ldr      r2,0x20001594 ; = #0x87654321<br />[0xe5812000]   str      r2,[r1,#0]<br />*/</p>//这样可以很清楚的看到非对齐访问是如何产生错误的<br />//以及如何消除非对齐访问带来问题<br />//也可以看到非对齐访问和对齐访问的指令差异导致效率问题<br />}<br /><img src ="http://www.cppblog.com/erran/aggbug/34383.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/erran/" target="_blank">erran</a> 2007-10-16 22:56 <a href="http://www.cppblog.com/erran/archive/2007/10/16/34383.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：一个老工程师的话</title><link>http://www.cppblog.com/erran/archive/2007/10/14/34191.html</link><dc:creator>erran</dc:creator><author>erran</author><pubDate>Sat, 13 Oct 2007 16:55:00 GMT</pubDate><guid>http://www.cppblog.com/erran/archive/2007/10/14/34191.html</guid><wfw:comment>http://www.cppblog.com/erran/comments/34191.html</wfw:comment><comments>http://www.cppblog.com/erran/archive/2007/10/14/34191.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/erran/comments/commentRss/34191.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/erran/services/trackbacks/34191.html</trackback:ping><description><![CDATA[ 诸位，咱当电子工程师也是十余年了，不算有出息，环顾四周，也没有看见几个有出息的！回顾工程师生涯，感慨万千，愿意讲几句掏心窝子的话，也算给咱们师弟师妹们提个醒，希望他们比咱们强！<br /> <br />[1]
好好规划自己的路，不要跟着感觉走！根据个人的理想决策安排，绝大部分人并不指望成为什么院士或教授，而是希望活得滋润一些，爽一些。那么，就需要慎重安
排自己的轨迹。从哪个行业入手，逐渐对该行业深入了解，不要频繁跳槽，特别是不要为了一点工资而转移阵地，从长远看，这点钱根本不算什么，当你对一个行业
有那么几年的体会，以后钱根本不是问题。频繁地动荡不是上策，最后你对哪个行业都没有摸透，永远是新手！<br /> <br />[2]可以做技术，切不可沉湎于技术。千万不可一门心思钻研技术！给自己很大压力，如果你的心思全部放在这上面，那么注定你将成为孔乙己一类的人物！适可而止为之，因为技术只不过是你今后前途的支柱之一，而且还不是最大的支柱。<br /> <br />[3]
不要去做技术高手，只去做综合素质高手！在企业里混，我们时常瞧不起某人，说他“什么都不懂，凭啥拿那么多钱，凭啥升官！”这是普遍的典型的工程师的迂腐
之言。8051很牛吗？人家能上去必然有他的本事，而且是你没有的本事。你想想，老板搞经营那么多年，难道见识不如你这个新兵？人家或许善于管理，善于领
会老板意图，善于部门协调等等。因此务必培养自己多方面的能力，包括管理，亲和力，察言观色能力，攻关能力等，要成为综合素质的高手，则前途无量，否则只
能躲在角落看示波器！技术以外的技能才是更重要的本事！！从古到今，美国日本，一律如此！<br /> <br />[4]多交社会三教九流的朋友！不要只和工程
师交往，认为有共同语言，其实更重要的是和其他类人物交往，如果你希望有朝一日当老板或高层管理，那么你整日面对的就是这些人。了解他们的经历，思维习
惯，爱好，学习他们处理问题的模式，了解社会各个角落的现象和问题，这是以后发展的巨大的本钱。<br /> <br />[6]抓住时机向技术管理或市场销售方
面的转变！要想有前途就不能一直搞开发，适当时候要转变为管理或销售，前途会更大，以前搞技术也没有白搞，以后还用得着。搞管理可以培养自己的领导能力，
搞销售可以培养自己的市场概念和思维，同时为自己以后发展积累庞大的人脉！应该说这才是前途的真正支柱！<br /> <br />[7]逐渐克服自己的心里弱点
和性格缺陷！多疑，敏感，天真（贬义，并不可爱），犹豫不决，胆怯，多虑，脸皮太薄，心不够黑，教条式思维。。。这些工程师普遍存在的性格弱点必须改变！
很难吗？只在床上想一想当然不可能，去帮朋友守一个月地摊，包准有效果，去实践，而不要只想！不克服这些缺点，一切不可能，甚至连项目经理都当不好--尽
管你可能技术不错！<br /> <br />[8]工作的同时要为以后做准备！建立自己的工作环境！及早为自己配置一个工作环境，装备电脑，示波器（可以买个二
手的），仿真器，编程器等，业余可以接点活，一方面接触市场，培养市场感觉，同时也积累资金，更重要的是准备自己的产品，咱搞技术的没有钱，只有技术，技
术的代表不是学历和证书，而是产品，拿出象样的产品，就可技术转让或与人合作搞企业！先把东西准备好，等待机会，否则，有了机会也抓不住！<br /> <br />[9]
要学会善于推销自己！不仅要能干，还要能说，能写，善于利用一切机会推销自己，树立自己的品牌形象，很必要！要创造条件让别人了解自己，不然老板怎么知道
你能干？外面的投资人怎么相信你？提早把自己推销出去，机会自然会来找你！搞个个人主页是个好注意！！特别是培养自己在行业的名气，有了名气，高薪机会自
不在话下，更重要的是有合作的机会...... <br /><br />[10]该出手时便出手！永远不可能有100%把握！！！条件差不多就要大胆去干，去闯出自己的事业，不要犹豫，不要彷徨，干了不一定成功，但至少为下一次冲击积累了经验，不干永远没出息，而且要干成必然要经历失败。不经历风雨，怎么见彩虹，没有人能随随便便成功！<img src ="http://www.cppblog.com/erran/aggbug/34191.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/erran/" target="_blank">erran</a> 2007-10-14 00:55 <a href="http://www.cppblog.com/erran/archive/2007/10/14/34191.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：单片机汇编程序编码规范</title><link>http://www.cppblog.com/erran/archive/2007/10/14/34188.html</link><dc:creator>erran</dc:creator><author>erran</author><pubDate>Sat, 13 Oct 2007 16:43:00 GMT</pubDate><guid>http://www.cppblog.com/erran/archive/2007/10/14/34188.html</guid><wfw:comment>http://www.cppblog.com/erran/comments/34188.html</wfw:comment><comments>http://www.cppblog.com/erran/archive/2007/10/14/34188.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/erran/comments/commentRss/34188.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/erran/services/trackbacks/34188.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 单片机汇编程序编码规范																																		http://www.blogcn.com/u2/94/42/cbz6000/blog/43716860.html																																						引言										    		...&nbsp;&nbsp;<a href='http://www.cppblog.com/erran/archive/2007/10/14/34188.html'>阅读全文</a><img src ="http://www.cppblog.com/erran/aggbug/34188.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/erran/" target="_blank">erran</a> 2007-10-14 00:43 <a href="http://www.cppblog.com/erran/archive/2007/10/14/34188.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC中的一些常用方法</title><link>http://www.cppblog.com/erran/archive/2007/10/14/34185.html</link><dc:creator>erran</dc:creator><author>erran</author><pubDate>Sat, 13 Oct 2007 16:38:00 GMT</pubDate><guid>http://www.cppblog.com/erran/archive/2007/10/14/34185.html</guid><wfw:comment>http://www.cppblog.com/erran/comments/34185.html</wfw:comment><comments>http://www.cppblog.com/erran/archive/2007/10/14/34185.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/erran/comments/commentRss/34185.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/erran/services/trackbacks/34185.html</trackback:ping><description><![CDATA[
		<font face="Arial">
				<br />
				<strong>一、打开CD-ROM <br /></strong>mciSendString("Set cdAudio door open wait",NULL,0,NULL); <br /><br /><strong>二、关闭CD_ROM</strong> <br />mciSendString("Set cdAudio door closed wait",NULL,0,NULL); <br /><br /><strong>三、关闭计算机 <br /></strong>OSVERSIONINFO OsVersionInfo; //包含操作系统版本信息的数据结构 <br />OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); <br />GetVersionEx(&amp;OsVersionInfo); //获取操作系统版本信息 <br />if(OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) <br />{ <br />//Windows98,调用ExitWindowsEx()函数重新启动计算机 <br /><br />DWORD dwReserved; <br />ExitWindowsEx(EWX_REBOOT,dwReserved); //可以改变第一个参数，实现注销用户、 <br />//关机、关闭电源等操作 <br />// 退出前的一些处理程序 <br />} <br /><br /><strong>四、重启计算机 <br /></strong>typedef int (CALLBACK *SHUTDOWNDLG)(int); //显示关机对话框函数的指针 <br />HINSTANCE hInst = LoadLibrary("shell32.dll"); //装入shell32.dll <br />SHUTDOWNDLG ShutDownDialog; //指向shell32.dll库中显示关机对话框函数的指针 <br />if(hInst != NULL) <br />{ <br />//获得函数的地址并调用之 <br />ShutDownDialog = (SHUTDOWNDLG)GetProcAddress(hInst,(LPSTR)60); <br /><br />(*ShutDownDialog)(0); <br />} <br /><br /><strong>五、枚举所有字体 <br /></strong>LOGFONT lf; <br />lf.lfCharSet = DEFAULT_CHARSET; // Initialize the LOGFONT structure <br />strcpy(lf.lfFaceName,""); <br />CClientDC dc (this); <br />// Enumerate the font families <br />::EnumFontFamiliesEx((HDC) dc,&amp;lf,                                                                                       <br />(FONTENUMPROC) EnumFontFamProc,(LPARAM) this,0); <br />//枚举函数 <br />int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf, <br />LPNEWTEXTMETRIC lpntm,DWORD nFontType,long lparam) <br /><br />{ <br />// Create a pointer to the dialog window <br />CDay7Dlg* pWnd = (CDay7Dlg*) lparam; <br />// add the font name to the list box <br />pWnd -&gt;m_ctlFontList.AddString(lpelf -&gt;elfLogFont.lfFaceName); <br />// Return 1 to continue font enumeration <br />return 1; <br />} <br />其中m_ctlFontList是一个列表控件变量 <br /><br /><strong>六、一次只运行一个程序实例，如果已运行则退出</strong> <br />if( FindWindow(NULL,"程序标题")) exit(0); <br /><br /><strong>七、得到当前鼠标所在位置</strong> <br />CPoint pt; <br />GetCursorPos(&amp;pt); //得到位置 <br /><br /><strong>八、上下文菜单事件触发事件：OnContextMenu事件 <br /></strong><br /><strong>九、显示和隐藏程序菜单 <br /></strong>CWnd *pWnd=AfxGetMainWnd(); <br />if(b_m) //隐藏菜单 <br />{ <br />pWnd-&gt;SetMenu(NULL); <br />pWnd-&gt;DrawMenuBar(); <br />b_m=false; <br />} <br />else <br />{ <br />CMenu menu; <br />menu.LoadMenu(IDR_MAINFRAME); ////显示菜单 也可改变菜单项 <br />pWnd-&gt;SetMenu(&amp;menu); <br />pWnd-&gt;DrawMenuBar(); <br />b_m=true; <br />menu.Detach(); <br />} <br /><br /><strong>十、获取可执行文件的图标</strong> <br />HICON hIcon=::ExtractIcon(AfxGetInstanceHandle(),_T("NotePad.exe"),0); <br />if (hIcon &amp;&amp;hIcon!=(HICON)-1) <br />{ <br />pDC-&gt;DrawIcon(10,10,hIcon); <br /><br />} <br />DestroyIcon(hIcon); <br /><br /><strong>十一、窗口自动靠边程序演示</strong> <br />BOOL AdjustPos(CRect* lpRect) <br />{//自动靠边 <br />int iSX=GetSystemMetrics(SM_CXFULLSCREEN); <br />int iSY=GetSystemMetrics(SM_CYFULLSCREEN); <br />RECT rWorkArea; <br />BOOL bResult = SystemParametersInfo(SPI_GETWORKAREA, sizeof(RECT), &amp;rWorkAre <br />a, 0); <br />CRect rcWA; <br />if(!bResult) <br />{//如果调用不成功就利用GetSystemMetrics获取屏幕面积 <br />rcWA=CRect(0,0,iSX,iSY); <br />} <br />else <br />rcWA=rWorkArea; <br />int iX=lpRect-&gt;left; <br />int iY=lpRect-&gt;top; <br /><br />if(iX &lt; rcWA.left + DETASTEP &amp;&amp; iX!=rcWA.left) <br />{//调整左 <br />//pWnd-&gt;SetWindowPos(NULL,rcWA.left,iY,0,0,SWP_NOSIZE); <br />lpRect-&gt;OffsetRect(rcWA.left-iX,0); <br />AdjustPos(lpRect); <br />return TRUE; <br />} <br />if(iY &lt; rcWA.top + DETASTEP &amp;&amp; iY!=rcWA.top) <br />{//调整上 <br />//pWnd-&gt;SetWindowPos(NULL ,iX,rcWA.top,0,0,SWP_NOSIZE); <br />lpRect-&gt;OffsetRect(0,rcWA.top-iY); <br />AdjustPos(lpRect); <br />return TRUE; <br />} <br />if(iX + lpRect-&gt;Width() &gt; rcWA.right - DETASTEP &amp;&amp; iX !=rcWA.right-lpRect-&gt;W <br /><br />idth()) <br />{//调整右 <br />//pWnd-&gt;SetWindowPos(NULL ,rcWA.right-rcW.Width(),iY,0,0,SWP_NOSIZE); <br />lpRect-&gt;OffsetRect(rcWA.right-lpRect-&gt;right,0); <br />AdjustPos(lpRect); <br />return TRUE; <br />} <br />if(iY + lpRect-&gt;Height() &gt; rcWA.bottom - DETASTEP &amp;&amp; iY !=rcWA.bottom-lpRect <br />-&gt;Height()) <br />{//调整下 <br />//pWnd-&gt;SetWindowPos(NULL ,iX,rcWA.bottom-rcW.Height(),0,0,SWP_NOSIZE); <br />lpRect-&gt;OffsetRect(0,rcWA.bottom-lpRect-&gt;bottom); <br />return TRUE; <br />} <br />return FALSE; <br />} <br />//然后在ONMOVEING事件中使用所下过程调用 <br /><br />CRect r=*pRect; <br />AdjustPos(&amp;r); <br />*pRect=(RECT)r; <br /><br /><strong>十二、给系统菜单添加一个菜单项 <br /></strong>给系统菜单添加一个菜单项需要进行下述三个步骤： <br />首先，使用Resource Symbols对话（在View菜单中选择Resource Symbols．．．可以显 <br />示该对话）定义菜单项ID，该ID应大于0x0F而小于0xF000； <br />其次，调用CWnd::GetSystemMenu获取系统菜单的指针并调用CWnd:: Appendmenu将菜单 <br />项添加到菜单中。下例给系统菜单添加两个新的 <br />int CMainFrame:: OnCreate (LPCREATESTRUCT lpCreateStruct) <br />{ <br />… <br />//Make sure system menu item is in the right range. <br /><br />ASSERT(IDM_MYSYSITEM&lt;0xF000); <br />//Get pointer to system menu. <br />CMenu* pSysMenu=GetSystemMenu(FALSE); <br />ASSERT_VALID(pSysMenu); <br />//Add a separator and our menu item to system menu. <br />CString StrMenuItem(_T ("New menu item")); <br />pSysMenu-&gt;AppendMenu(MF_SEPARATOR); <br />pSysMenu-&gt;AppendMenu(MF_STRING, IDM_MYSYSITEM, StrMenuItem); <br />… <br />} <br /><br /><strong>十三、运行其它程序</strong> <br />//1、运行EMAIL或网址 <br />char szMailAddress[80]; <br />strcpy(szMailAddress,"mailto:netvc@21cn.com"); <br />ShellExecute(NULL, "open", szMailAddress, NULL, NULL, SW_SHOWNORMAL); <br /><br />//2、运行可执行程序 <br />WinExec("notepad.exe",SW_SHOW); //运行计事本 <br /><br /><strong>十四、动态增加或删除菜单 <br /></strong>1、 增加菜单 <br />//添加 <br />CMenu *mainmenu; <br />mainmenu=AfxGetMainWnd()-&gt;GetMenu(); //得到主菜单 <br />(mainmenu-&gt;GetSubMenu (0))-&gt;AppendMenu (MF_SEPARATOR);//添加分隔符 <br />(mainmenu-&gt;GetSubMenu (0))-&gt;AppendMenu(MF_STRING,ID_APP_ABOUT,_T("Always on <br />&amp;Top")); //添加新的菜单项 <br />DrawMenuBar(); //重画菜单 <br />2、 删除菜单 <br />//删除 <br />CMenu *mainmenu; <br />mainmenu=AfxGetMainWnd()-&gt;GetMenu(); //得到主菜单 <br /><br />CString str ; <br />for(int i=(mainmenu-&gt;GetSubMenu (0))-&gt;GetMenuItemCount()-1;i&gt;=0;i--) //取得菜 <br />单的项数。 <br />{ <br />(mainmenu-&gt;GetSubMenu (0))-&gt;GetMenuString(i,str,MF_BYPOSITION); <br />//将指定菜单项的标签拷贝到指定的缓冲区。MF_BYPOSITION的解释见上。 <br />if(str=="Always on &amp;Top") //如果是刚才我们增加的菜单项，则删除。 <br />{ <br />(mainmenu-&gt;GetSubMenu (0))-&gt;DeleteMenu(i,MF_BYPOSITION); <br />break; <br />} <br /><br /><strong>十五、改变应用程序的图标</strong> <br />静态更改： 修改图标资源IDR_MAINFRAME。它有两个图标，一个是16*16的，另一个是3 <br /><br />2*32的，注意要一起修改。 <br />动态更改： 向主窗口发送WM_SETICON消息.代码如下： <br />HICON hIcon=AfxGetApp()-&gt;LoadIcon(IDI_ICON); <br />ASSERT(hIcon); <br />AfxGetMainWnd()-&gt;SendMessage(WM_SETICON,TRUE,(LPARAM)hIcon); <br /><br /><strong>十六、另一种改变窗口标题的方法</strong> <br />使用语句 CWnd* m_pCWnd = AfxGetMainWnd( )，然后，再以如下形式调用SetWindowTe <br />xt()函数： <br />SetWindowText( *m_pCWnd,(LPCTSTR)m_WindowText)；// m_WindowText可以是一个CSt <br />ring类的变量。 <br /><br /><strong>十七、剪切板上通过增强元文件拷贝图像数据 <br /></strong>下面代码拷贝通过元文件拷贝图像数据到任何应用程序，其可以放置在CView派生类的函 <br /><br />数中。 <br />CMetaFileDC * m_pMetaDC = new CMetaFileDC(); <br />m_pMetaDC-&gt;CreateEnhanced(GetDC(),NULL,NULL,"whatever"); <br />//draw meta file <br />//do what ever you want to do: bitmaps, lines, text... <br />//close meta file dc and prepare for clipboard; <br />HENHMETAFILE hMF = m_pMetaDC-&gt;CloseEnhanced(); <br />//copy to clipboard <br />OpenClipboard(); <br />EmptyClipboard(); <br />::SetClipboardData(CF_ENHMETAFILE,hMF);                                                                                 <br />CloseClipboard(); <br /><br />//DeleteMetaFile(hMF); <br />delete m_pMetaDC; <br /><br /><strong>十八、剪切板上文本数据的传送</strong> <br />把文本放置到剪接板上： <br />CString source; <br />//put your text in source <br />if(OpenClipboard()) <br />{ <br />HGLOBAL clipbuffer; <br />char * buffer; <br />EmptyClipboard(); <br />clipbuffer = GlobalAlloc(GMEM_DDESHARE, source.GetLength()+1); <br />buffer = (char*)GlobalLock(clipbuffer); <br />strcpy(buffer, LPCSTR(source)); <br />GlobalUnlock(clipbuffer); <br />SetClipboardData(CF_TEXT,clipbuffer); <br />CloseClipboard(); <br />} <br />从剪接板上获取文本： <br /><br />char * buffer; <br />if(OpenClipboard()) <br />{ <br />buffer = (char*)GetClipboardData(CF_TEXT); <br />//do something with buffer here <br />//before it goes out of scope <br />} <br />CloseClipboard(); <br /><br /><strong>十九、将捕捉屏幕图像到剪切版中 <br /></strong>void CShowBmpInDlgDlg::OnCutScreen() <br />{ <br />ShowWindow(SW_HIDE); <br />RECT r_bmp={0,0,::GetSystemMetrics(SM_CXSCREEN), <br />::GetSystemMetrics(SM_CYSCREEN)};                                                                                       <br />HBITMAP hBitmap; <br />hBitmap=CopyScreenToBitmap(&amp;r_bmp); <br /><br />//hWnd为程序窗口句柄 <br />if (OpenClipboard()) <br />{ <br />EmptyClipboard(); <br />SetClipboardData(CF_BITMAP, hBitmap); <br />CloseClipboard(); <br />} <br />ShowWindow(SW_SHOW); <br />} <br />HBITMAP CShowBmpInDlgDlg::CopyScreenToBitmap(LPRECT lpRect) <br />{ <br />//lpRect 代表选定区域 <br />{ <br />HDC hScrDC, hMemDC; <br />// 屏幕和内存设备描述表 <br />HBITMAP hBitmap, hOldBitmap; <br />// 位图句柄 <br />int nX, nY, nX2, nY2; <br />// 选定区域坐标 <br />int nWidth, nHeight; <br />// 位图宽度和高度 <br />int xScrn, yScrn; <br />// 屏幕分辨率 <br /><br />// 确保选定区域不为空矩形 <br />if (IsRectEmpty(lpRect)) <br />return NULL; <br />//为屏幕创建设备描述表 <br />hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL); <br />//为屏幕设备描述表创建兼容的内存设备描述表 <br />hMemDC = CreateCompatibleDC(hScrDC); <br />// 获得选定区域坐标 <br />nX = lpRect-&gt;left; <br />nY = lpRect-&gt;top; <br />nX2 = lpRect-&gt;right; <br />nY2 = lpRect-&gt;bottom; <br />// 获得屏幕分辨率 <br />xScrn = GetDeviceCaps(hScrDC, HORZRES); <br />yScrn = GetDeviceCaps(hScrDC, VERTRES); <br />//确保选定区域是可见的 <br />if (nX&lt;0) <br /><br />nX = 0; <br />if (nY&lt;0) <br />nY = 0; <br />if (nX2&gt;xScrn) <br />nX2 = xScrn; <br />if (nY2&gt;yScrn) <br />nY2 = yScrn; <br />nWidth = nX2 - nX; <br />nHeight = nY2 - nY; <br />// 创建一个与屏幕设备描述表兼容的位图 <br />hBitmap = CreateCompatibleBitmap <br />(hScrDC, nWidth, nHeight); <br />// 把新位图选到内存设备描述表中 <br />hOldBitmap =(HBITMAP)SelectObject(hMemDC, hBitmap); <br />// 把屏幕设备描述表拷贝到内存设备描述表中 <br />BitBlt(hMemDC, 0, 0, nWidth, nHeight, <br />hScrDC, nX, nY, SRCCOPY); <br />//得到屏幕位图的句柄 <br />hBitmap = (HBITMAP)SelectObject(hMemDC, hOldBitmap); <br /><br />//清除 <br />DeleteDC(hScrDC); <br />DeleteDC(hMemDC); <br />// 返回位图句柄 <br />return hBitmap; <br />} <br />} <br /><br /><strong>二十、如何将位图缩放显示在Static控件中 <br /></strong>//在Staic控件内显示位图 <br />void CShowBmpInDlgDlg::ShowBmpInStaic() <br />{ <br />CBitmap hbmp; <br />HBITMAP hbitmap; <br />//将pStatic指向要显示的地方 <br />CStatic *pStaic; <br />pStaic=(CStatic*)GetDlgItem(IDC_IMAGE); <br />//装载资源 MM.bmp是我的一个文件名，用你的替换 <br />hbitmap=(HBITMAP)::LoadImage (::AfxGetInstanceHandle(),"MM.bmp", <br />IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION); <br /><br />hbmp.Attach(hbitmap); <br />//获取图片格式 <br />BITMAP bm; <br />hbmp.GetBitmap(&amp;bm); <br />CDC dcMem; <br />dcMem.CreateCompatibleDC(GetDC()); <br />CBitmap *poldBitmap=(CBitmap*)dcMem.SelectObject(hbmp); <br />CRect lRect; <br />pStaic-&gt;GetClientRect(&amp;lRect); <br />//显示位图 <br />pStaic-&gt;GetDC()-&gt;StretchBlt(lRect.left ,lRect.top ,lRect.Width(),lRect.Heigh <br />t(), <br />&amp;dcMem,0 ,0,bm.bmWidth,bm.bmHeight,SRCCOPY); <br />dcMem.SelectObject(&amp;poldBitmap); <br />}<br /></font>
<img src ="http://www.cppblog.com/erran/aggbug/34185.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/erran/" target="_blank">erran</a> 2007-10-14 00:38 <a href="http://www.cppblog.com/erran/archive/2007/10/14/34185.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：C++对象内存布局</title><link>http://www.cppblog.com/erran/archive/2007/10/14/34184.html</link><dc:creator>erran</dc:creator><author>erran</author><pubDate>Sat, 13 Oct 2007 16:37:00 GMT</pubDate><guid>http://www.cppblog.com/erran/archive/2007/10/14/34184.html</guid><wfw:comment>http://www.cppblog.com/erran/comments/34184.html</wfw:comment><comments>http://www.cppblog.com/erran/archive/2007/10/14/34184.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/erran/comments/commentRss/34184.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/erran/services/trackbacks/34184.html</trackback:ping><description><![CDATA[
		<br />引用：http://www.cppblog.com/stdyh/archive/2007/01/08/17442.html<br /><br />C++对象内存布局<br /><br /><br />
			写这个文章完全是因为想要搞清楚 vc 怎么布局每个 c++ 对象,以及怎样完成指针的转换的过程.<br />　　先问一个问题,两个不同类型的指针相互转换以后,他们在数值上是一样的吗?比如:<br /><br />　　　　<font color="#000066">int nValue = 10;<br />　　　　int *pInt = &amp;nValue;<br />　　　　void *pVoid = pInt;<br />　　　　char *pChar = (char*)pInt;</font><br /><br />　
　这些指针的值(不是说指针指向的内存的内容)是一样的吗? 如果你的回答是
yes,那如果是一个类的继承体系呢?在继承类向基类转换的过程中,指针的数值还是不变化的么?如果你的回答是"不一定会变化,要看类的体系是怎么设计的
"的话,那恭喜你,不用看下去了.如果你还不确定究竟变还是不变,究竟哪些变,哪些不变,究竟为什么要变为什么不变的话,接着看下来.<br /><br />　
　c++ 标准不规定 c++ 实现的时候的对象的具体的内存布局,除了在某些方面有小的限制以外,c++
对象在内存里面的布局完全是由编译器自行决定,这里我也只是讨论 vc++ .net 2003 build 7.1.3091
的实现方式,我并没有在 vc5 vc6 vc.net 2002 以及其他的 2003 build
上面做过测试,结论也许不适合那些编译平台.这些属于编译器具体实现,ms 保留有在不通知你我的情况下作出更改的权利.废话这么多,马上开始.<br /><br />　　对于 c 的内建指针的转换,结果是不用多讨论的,我们只是讨论 c++ 的对象.从最简单的开始.<br /><br />　　　　<font color="#000066">class CBase<br />　　　　{<br />　　　　public:<br />　　　　　　int m_nBaseValue;<br />　　　　};</font><br /><br />　　这样的一个类在内存里放置是非常简单的,他占有4个 bytes 的空间,不用多说,我们从他派生一个类出来.<br /><br />　　　　<font color="#000066">class CDerive1 : public CBase<br />　　　　{<br />　　　　public:<br />　　　　　　int m_nDerive1Value;<br />　　　　};</font><br /><br />　
　CDerive1 的对象在内存里面是怎么放的呢? 也很简单,占有8个 bytes 的空间,前4个 bytes 属于 CBase 类,后四个
bytes 属于自己.一个CDerive1 的指针转换成一个 CBase 的指针,结果是一样的.下面我们加上多重继承看看.<br /><br />　　　　<font color="#000066">class CFinal : public CDerive,public CBase <font color="#ff0000">// 这里的 CDerive 是一个和 CBase 差不多的基类</font><br />　　　　{<br />　　　　public:<br />　　　　　　int m_nFinalValue;<br />　　　　}; </font><br /><br />　
　CFinal 的对象在内存里面的布局稍微复杂一点,但是也很容易想象,他占有 12 个 bytes 的空间,前4个属于
CDerive,中间4个属于 CBase,后面4个才是自己的.那一个 CFinal 的指针转换成一个 CDerive 指针,数值会变么?
转换成一个 CBase 指针呢?又会变化么?答案是,前一个不变,后一个要变化,道理非常的明显,CFinal 对象的开头刚好是一个
CDerive 对象,而 CBase 对象却在 CFinal 对象的中间,自然是要变化的了,具体怎么变化呢? 加 4 就
ok(自然要检查是否是空指针).<br /><br />　　　　<font color="#ff0000">CBase *pBase = pFinal ? (CBase*)((char*)pFinal + sizeof(CDerive)) : 0;// 当你写下 pBase = pFinal 的时候,其实是这样的</font><br /><br />　　这种不带 virtual 的继承就这么简单,只是加上一个 offset 而已.下面我们看看如果加上 virtual function 的时候是什么样子的呢?<br />还是从简单类开始.<br /><br />　　　　<font color="#000066">class CBase<br />　　　　{<br />　　　　public:<br />　　　　　　virtual void VirtualBaseFunction(){}<br />　　　　　　int m_nBaseValue;<br />　　　　}; </font><br /><br />　
　这里刻意没有使用 virtual destructor,因为这个函数稍微有些不同.还是同样的问题,CBase 类在内存上占多大的空间?还是
4 bytes 么? 答案是 no, 在我的编译器上面是 8 bytes,多出来的 4 bytes 是 __vfptr(watch
窗口看见的名字),他是一个指针,指向了类的 vtable,那什么是 vtable 呢,他是用来干什么的呢? vtable 是用来支援
virtual function
机制的,他其实是一个函数指针数组(并不等同于c/c++语言里面的指针数组,因为他们的类型并不一定是一样的.)他的每一个元素都指向了一个你定义的
virtual
function,这样通过一个中间层来到达动态连编的效果,这些指针是在程序运行的时候准备妥当的,而不是在编译的时候准备妥当的,这个就是动态联编的
目的,具体是由谁来设置这些指针的呢?constructor/destructor/copy constructor/assignment
operator他们完成的,不用奇怪,编译器会在你写的这些函数里面安插些必要的代码用来设置 vtable
的值,如果你没有写这些函数,编译器会在适当的时候帮你生成这些函数.明白一点, vtable 是用来支持 virtual function
机制的,而需要 virtual 机制的类基本上都会由一个 __vfptr 指向他自己的 vtable.在调用 virtual
function的时候,编译器这样完成:<br /><br />　　　<font color="#000099">pBase-&gt;VirtualBaseFunction(); =&gt; pBase-&gt;__vfptr[0]();</font><font color="#ff0000">// 0 是你的virtual function 在 vtable 中的 slot number,编译器决定</font><br /><br />　
　现在应该很想象 CBase 的大小了吧,那这个 __vfptr 是放到什么位置的呢? 在 m_nBaseValue 之前还是之后呢?
在我的编译器上看来,是在之前,为什么要放到之前,是因为在通过 指向类成员函数的指针调用 virtual function
的时候能少些代码(指汇编代码),这个原因这里就不深入讨论了,有兴趣的同学可以看看 inside the c++ object model 一书.<br />　　接下来,我们加上继承来看看.<br /><br />　　　　<font color="#000099">class CDerive1 : public CBase<br />　　　　{<br />　　　　public:<br />　　　　　　virtual void VirtualDerive1Function();<br />　　　　};</font><br /><br />　
　这个时候你也许要说,内存布局跟没有 virtual 是一样的,只不过每个类多了一个 __vfptr
而已,呃...这个是不对的,在我的编译器上面 两个类共享同一个 __vfptr, vtable
里面放有两个指针,一个是两个类共享的,一个只属于 CDerive1 类,调用的时候如何呢?<br /><br />　　　<font color="#000099">pDerive1-&gt;VirtualDerive1Function() =&gt; pDerive1-&gt;__vfptr[1]();<br />　　　pDerive1-&gt;VirtualBaseFunction() =&gt; pDerive1-&gt;__vfptr[0]();</font><br /><br />　　至于指针的相互转换,数值还是没有变化的(也正是追求这种效果,所以把 __vfptr 放到类的开头,因为调整 this 指针也是要占有运行时的时间的).<br /><br />　
　现在加上多重继承瞧瞧,代码我不写上来了,就跟上面的 CFinal, CDerive, CBase
体系一样,只是每个类多一个VirtualxxxFunction出来,这个时候的指针调整还是没有什么变化,所以我们只是看看 vtable
的情况,你会说 CDerive 和 CFinal 共享一个 __vfptr,而 CBase 有一个自己的 __vfptr,而 CFinal 的
__vfptr 有 2 个slot,这个结论是正确的. 同时你也会说 通过 CFinal 类调用 CBase 的函数是要进行指针调整的,yes
you'r right,不仅仅是 this 指针调整(呃,this 指针会成为 function 的一个参数),还要调整 vtable 的值:<br /><br />　　　<font color="#0000cc">pFinal-&gt;VirtualBaseFunction() =&gt; (CBase*)((char*)pFinal + sizeof(CDerive))-&gt;__vfptr[0]();</font><br /><br />　　　转换成 asm 的代码大约是这样的:<br /><br /><font color="#000099">　　　mov eax,[pFinal] <font color="#ff00ff">; pFinal is a local object,pFinal will be epb - xx</font><br />　　　add eax,8 <font color="#ff00ff">; 8 = sizeof(CDerive)</font><br />　　　mov ecx,eax <font color="#ff00ff">; ecx is this pointer</font><br />　　　mov edx,[eax] <font color="#ff00ff">; edx = vtable address</font><br />　　　call [edx] <font color="#ff00ff">; call vtable[0]</font></font><br /><br />　　写到这里也就明白this指针是怎么调整的.带 virtual function 的继承也不复杂,this指针调整也是很简单的,下面看最复杂的部分 virtual inheritance.<br /><br />　
　我的编译器支持虚拟继承的方式和虚函数的方式差不多,都是通过一个 table 完成,只是这个就看不到 vc 赋予的名字了,我们叫他
vbtable 吧,编译器同样在类里面加入一个指向 vbtable 的指针,我们叫他 __vbptr 吧,这个指针指向了 vbtable ,而
vbtable 里面的每一项对应了一个基类,vbtable
记录了每个基类的某一个偏移量,通过这个偏移量就能计算出具体类的指针的位置.看个简单的例子:<br /><br />　　　<font color="#000099">class CBase<br />　　　{<br />　　　public:<br />　　　　　virtual ~CBase(){}<br />　　　}; <br /><br />　　　class CMid1 : public virtual CBase<br />　　　{<br />　　　public:<br />　　　　　virtual ~CMid1(){}<br />　　　　　int m_nMid1;<br />　　　}; <br /><br />　　　class CMid2 : public virtual CBase<br />　　　{<br />　　　public:<br />　　　　　virtual ~CMid2(){}<br />　　　　　int m_nMid2;<br />　　　}; <br /><br />　　　class CFinal : public CMid1,public CMid2<br />　　　{<br />　　　public:<br />　　　　　virtual ~CFinal(){}<br />　　　　　int m_nFinal;<br />　　　}; <br /><br />　　　CFinal final;<br />　　　CFinal *pFinal = &amp;final;    <font color="#ff00ff">// pFinal = 0x0012feb4;</font><br />　　　CBase *pBase = pFinal; <font color="#ff00ff">// pBase = 0x0012fec8 = pFinal + 0x14;</font><br />　　　CMid1 *pMid1 = pFinal; <font color="#ff00ff">// pMid1 = 0x0012feb4 = pFinal;</font><br />　　　CMid2 *pMid2 = pFinal; <font color="#ff00ff">// pMid2 = 0x004210b4 = pFinal;</font></font><br /><br />　
　结果让你吃惊吗? 最奇怪的地方居然是 CMid2 和 CMid1 的地址居然是一样的,这个是因为 vc 把 vbtable 放到了
CFinal 类的开头的原因,而CMid1 和 CMid2 也同样要使用这个 vbtable, 所以 这个三个的地址也就必须相同了.那
CBase 的地址是怎么出来的呢? 呃...刚刚我们说了 vbtable 放到了CFinal 的开头(vc
一定会放在开头吗?答案是不一定,这个稍后解释).在我的机器上面 final 对应内存的第一个 dword 是
0x00426030,查看这个地址,第一个dword 是 0 ,第二个就是 0x14,刚好和 pBase
的偏移相同,这个只是巧合,也许你换个类的继承体系就完全不同了,但是我只是想说明一点,基类的偏移计算是和 vbtable
的值相关联的.下面我们就来看看 vc 是怎么计算这些偏移的.<br />　　vc 在分析我们的代码的时候,生成了一份类的继承体系信息,其中有一个叫 thisDisplacement 的_PMD结构:<br /><br />　　　　<font color="#000099">struct _PMD <font color="#ff00ff">// total undocumented</font><br />　　　　{<br />　　　　　　int mdisp; <font color="#ff00ff">// i think the meaning is Multiinheritance DISPlacement</font><br />　　　　　　int pdisp; <font color="#ff00ff">// Pointer to vbtable DISPlacement</font><br />　　　　　　int vdisp; <font color="#ff00ff">// Vbtable DISPlacement</font><br />　　　　}; </font><br /><br />　
　结构的名字和成员变量的名字确确实实是 vc 的名字(在 watch 窗口输入 (_PMD*)0
就能看到这个结构的详细信息),每个字段的含义却是我自己猜测出来的.mdisp 大概用来表示多重继承(包括单一继承)的时候的偏移量,pdisp
表示 vbtable 的偏移量,而 vdisp 表示类在 vbtable
里面的下标.那么有了这个结构怎样才能完成指针的转换呢?假如我们有一个派生类指针
pFinal,要转换成一个特定的基础类,我们首先要知道和这个基类对应的 _PMD
结构的信息(这个信息的获取,我暂时没有找到一个非常方便的方法,现在我使用的方法下面会有描述),有了这个信息以后,转换就方便了.首先找到
vbtabel 的地址 *(pFinal + pdisp),然后找到基类的偏移 *(*(pFinal + pdisp) + vdisp)
这个偏移值是相对vbtable的,所以还要加上 vbtable的偏移,最后加上 mdisp的偏移,如下:<br /><br />　　<font color="#000099">char *pFinal = xxx; <font color="#ff00ff">// need a init value</font><br />　　char *pBase; <font color="#ff00ff">// we must calc</font><br />　　pBase = pFinal + mdisp + *(int *)(*(int *)(pFinal + pdisp) + vdisp) + pdisp;</font><br /><br />　　<font color="#ff0000">注意: 当 pdisp &lt; 0 的时候就表示这个类没有 vbtable 直接使用 pFinal + mdisp 就得到结果了.<br />　　<font color="#00ff00">所以这个结构是一个通用的结构,专门用作类型转换,不管是有无虚继承都能使用这个结构进行类型转换.</font></font><br />　　通过这个结构,我们也能看到 vc 是怎样布局这个 object 的.<br /><br />　　看到这里,也许你要大呼一口气,妈妈呀,一个类型转换要这么的麻烦吗?我直接写 pBase = pFinal 不就可以了吗? 恭喜你还没有被我忽悠得晕头转向,哈哈.其实你写下那行语句的时候,编译器在帮你做这个转换,大约生成下面的代码<br /><br />　　　　<font color="#000099">mov eax,[pFinal] <font color="#ff00ff">;final address</font><br />　　　　mov ecx,[eax] <font color="#ff00ff">; vbtable address *(int *)(pFinal + pdisp)</font><br />　　　　mov edx,eax <font color="#ff00ff">; save to edx</font><br />　　　　add edx,[ecx + 4] <font color="#ff00ff">; ecx + 4 is (*(int *)(pFinal + pdisp) + vdisp)</font><br />　　　　mov [pBase],edx <font color="#ff00ff">; edx = pFinal + mdisp + *(int *)(*(int *)(pFinal + pdisp) + vdisp) + pdisp;</font><br />　　　　<font color="#ff00ff">; here mdisp = 0, pdisp = 0, vdisp = 4</font></font><br /><br />　
　也许你要说了,我要这些东西来干什么?要转换的时候直接转换就好了,编译器会帮做,的确,大多数的时候确实是这样,但是,在某些时候却并不如此,现在你
要实现一个功能,输入一个指针,输入一个 _PMD 结构,你要实现一个AdjustPointer
的函数来生成另一个指针.这个时候你也只能这样完成了,因为我没有给你两个指针的名字,就算给了你字符串形式的名字也没有用,呃....你也许会说,办法
是有的,的确是有,模板就能实现这种功能,呵..这个我们暂时不讨论具体的实现细节.也许你要问了,究竟什么时候会去实现这种听都没有听过的功能,其实这
个函数是真正存在的,只不过不是由你来实现的,而是 ms 的人实现的,你只用写一个 带有 c++ 异常的程序,使用 ida
反汇编,然后查找函数,就能找到这个函数了,他用来在异常处理时创建 catch 所需要的
object.至于这个详细的信息,请期待.我会最快速度写出关于 vc 是怎样实现 c++ 异常的文章来.<br /><br />　　最后了,说说那个
_PMD 结构的获取方式.看的时候不要吃惊,方法比较的麻烦,比如我想知道和 CFinal 类相关的 _PMD 信息,先新建工作,写下
throw pFinal 这样的语句,编译,在这个语句的地方设置断点,运行,转到反汇编,进入 __CxxThrowException@8
函数,这个时候不出意外你能看到一个叫 pThrowInfo 的东西(如果看不到,请打开"显示符号名"选项),在 watch
窗口里面输入pThrowInfo,展开他,看到一个pCatchableTypeArray,记录下他的
nCacthableTypes的值,然后在 watch 里面输入<br />pThrowInfo-&gt;pCatchableTypeArray-&gt;arrayOfCatchableTypes[0]
到 pThrowInfo-&gt;pCatchableTypeArray-&gt;arrayOfCatchableTypes[n], n
就是你刚刚记录的值减1,再展开他们,你就能看到一个 thisDisplacement 的数据,继续展开就是 mdisp
等等了,很是麻烦吧.哈..你已经猜到了,这个是和异常有关系的.<br /><br />　　后记:
这段时间,我一直在读些反汇编之后的代码,也颇有些心得,所以才有想法写一些文章,探讨 vc
编译器鲜为人知(太过狂妄了)的秘密,这个方面的文章也有人写过,那些文章也给我不少的启发,我不认为自己是第一个发现这些秘密的人,但是至少我自己知道
的,我是第一个把这些东西写出来的人.文章里面作墨多的部分都是自己发现的.就这个文章里面的内容来说,inside the c++ object
model 是有比较详细的描写,但是他并不是转换针对 vc 这个编译器的实现,而 _PMD 这个结构我也没有在什么地方见有人描述过,只是在
windows develop network
的2002年12月的杂志上看有人提到过这个结构,可惜他却没有了解(至少他在他发表文章的时候是如是说的)这个结构的用处(正是因为这个原因,我才有写
这个文章以及后续文章的冲动).所以,这个文章也算是我自己的原创吧.这个文件虽然和游戏制造没有太大的关系,但是小 T
自视清高,不愿意自己的文章被一帮不懂的人评价来评价去的,所以也没有发到那些著名的 xxx 网站,只发 goldpoint.转载请注明出处(小 T 对自己的第一个原创文章比较珍惜,比较重视,谢谢).<br /><br /><br /><img src ="http://www.cppblog.com/erran/aggbug/34184.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/erran/" target="_blank">erran</a> 2007-10-14 00:37 <a href="http://www.cppblog.com/erran/archive/2007/10/14/34184.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：关于调用约定(cdecl、fastcall、、thiscall) 的一点知识</title><link>http://www.cppblog.com/erran/archive/2007/10/14/34182.html</link><dc:creator>erran</dc:creator><author>erran</author><pubDate>Sat, 13 Oct 2007 16:33:00 GMT</pubDate><guid>http://www.cppblog.com/erran/archive/2007/10/14/34182.html</guid><wfw:comment>http://www.cppblog.com/erran/comments/34182.html</wfw:comment><comments>http://www.cppblog.com/erran/archive/2007/10/14/34182.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/erran/comments/commentRss/34182.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/erran/services/trackbacks/34182.html</trackback:ping><description><![CDATA[
		<div class="postbody">
				<div class="tit">引用：http://www.cppblog.com/oosky/archive/2007/01/08/17422.html<br /><br />函数调用规范<br /><br /><div class="cnt"><p align="left">当高级语言函数被编译成机器码时，有一个问题就必须解决：因为CPU没有办法知道一个函数调用需要多少个、什么样的参数。即计算机不知道怎么给这个函数传递参数，<font color="#0000ff">传递参数的工作必须由函数调用者和函数本身来协调</font>。为此，计算机提供了一种被称为栈的数据结构来支持参数传递。 </p><p align="left">   函数调用时，调用者依次把参数压栈，然后调用函数，函数被调用以后，在堆栈中取得数据，并进行计算。函数计算结束以后，或者调用者、或者函数本身修改堆栈，使堆栈恢复原装。在参数传递中，有两个很重要的问题必须得到明确说明：</p><p align="left">
            																  1) 当参数个数多于一个时，按照什么顺序把参数压入堆栈；<br />2) 函数调用后，由谁来把堆栈恢复原装。<br /><font color="#0000ff" size="2">3）函数的返回值放在什么地方</font></p>在高级语言中，通过函数<font color="#0000ff">调用规范(Calling Conventions)</font>来说明这两个问题。常见的调用规范有： 
            <div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font color="#800080">stdcall</font><br /><font color="#800080">cdecl<br /></font><font color="#800080">fastcall<br /></font><font color="#800080">thiscall<br /></font><font color="#800080">naked call</font></pre></div><h2 align="left"><font size="4">stdcall调用规范</font></h2><p align="left">stdcall
很多时候被称为pascal调用规范，因为pascal是早期很常见的一种教学用计算机程序设计语言，其语法严谨，使用的函数调用约定是stdcall。
在Microsoft C++系列的C/C++编译器中，常常用PASCAL宏来声明这个调用约定，类似的宏还有WINAPI和CALLBACK。 </p><p align="left">stdcall调用规范声明的语法为：</p><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2"><font color="#0000ff">int</font> __stdcall function(<font color="#0000ff">int</font> a,<font color="#0000ff">int</font> b)</font><br /></pre></div><div align="left">stdcall的调用约定意味着： </div><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: medium; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2"> 1）参数从右向左压入堆栈；<br /> 2）函数自身修改堆栈；<br /> 3) 函数名自动加前导的下划线，后面紧跟一个@符号，其后紧跟着参数的尺寸。</font><br /></pre></div><div align="left">以上述这个函数为例，参数b首先被压栈，然后是参数a，函数调用function(1,2)调用处翻译成汇编语言将变成： </div><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2">  push 2          第二个参数入栈<br />              push 1          第一个参数入栈<br />              call function   调用参数，注意此时自动把cs:eip入栈</font></pre></div><div align="left">而对于函数自身，则可以翻译为： </div><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2">      push  ebp               保存ebp寄存器，该寄存器将用来保存堆栈的栈顶指针，可以在函数退出时恢复<br />              mov   ebp,esp           保存堆栈指针<br />              mov   eax,[ebp + 8H]    堆栈中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a<br />              add   eax,[ebp + 0CH]   堆栈中ebp + 12处保存了b<br />              mov   esp,ebp           恢复esp<br />              pop   ebp<br />              ret   8</font></pre></div><p align="left">而在编译时，这个函数的名字被翻译成_function@8 </p><p align="left">注意不同编译器会插入自己的汇编代码以提供编译的通用性，但是大体代码如此。其中在函数开始处保留esp到ebp中，在函数结束恢复是编译器常用的方法。 </p><p align="left">从函数调用看，2和1依次被push进堆栈，而在函数中又通过相对于ebp(即刚进函数时的堆栈指针）的偏移量存取参数。函数结束后，ret 8表示清理8个字节的堆栈，函数自己恢复了堆栈。 </p><h2 align="left"> <font size="4">cdecl调用规范</font></h2><p align="left">cdecl调用约定又称为C调用约定，是C语言缺省的调用约定，它的定义语法是： </p><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2"><font color="#0000ff">              int</font> function (<font color="#0000ff">int</font> a ,<font color="#0000ff">int</font> b)           <font color="#ff0000">// 不加修饰就是C调用约定</font><br />              <font color="#0000ff">int</font> __cdecl function(<font color="#0000ff">int</font> a,<font color="#0000ff">int</font> b)     <font color="#ff0000">// 明确指出C调用约定</font></font></pre></div><p align="left"> 
cdecl调用约定的参数压栈顺序是和stdcall是一样的，参数首先由有向左压入堆栈。所不同的是，函数本身不清理堆栈，调用者负责清理堆栈。由于这
种变化，C调用约定允许函数的参数的个数是不固定的，这也是C语言的一大特色。对于前面的function函数，使用cdecl后的汇编码变成： </p><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2">调用处<br />              push   1<br />              push   2<br />              call   function<br />              add    esp,8              注意：这里调用者在恢复堆栈<br />              被调用函数_function处<br />              push   ebp                保存ebp寄存器，该寄存器将用来保存堆栈的栈顶指针，可以在函数退出时恢复<br />              mov    ebp,esp            保存堆栈指针<br />              mov    eax,[ebp + 8H]     堆栈中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a<br />              add    eax,[ebp + 0CH]    堆栈中ebp + 12处保存了b<br />              mov    esp,ebp            恢复esp<br />              pop    ebp<br />              ret                       注意，这里没有修改堆栈</font></pre></div><p align="left">MSDN中说，该修饰自动在函数名前加前导的下划线，因此函数名在符号表中被记录为_function。 <br /></p><p align="left">由于参数按照从右向左顺序压栈，因此最开始的参数在最接近栈顶的位置，因此当采用不定个数参数时，第一个参数在栈中的位置肯定能知道，只要不定的参数个数能够根据第一个后者后续的明确的参数确定下来，就可以使用不定参数，例如对于sprintf函数，定义为：</p><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2"><font color="#0000ff">int</font> sprintf(<font color="#0000ff">char</font>* buffer,<font color="#0000ff">const</font><font color="#0000ff">char</font>* format,...)</font></pre></div><div align="left">由于所有的不定参数都可以通过format确定，因此使用不定个数的参数是没有问题的。 </div><h2 align="left"> <font size="4">fastcall调用规范</font></h2><div align="left">fastcall调用约定和stdcall类似，它意味着： </div><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: medium; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2">            1) 函数的第一个和第二个DWORD参数（或者尺寸更小的）通过ecx和edx传递，其他参数通过从右向左的顺序压栈；<br />            2) 被调用函数清理堆栈；<br />            3) 函数名修改规则同stdcall。</font></pre></div><div align="left">其声明语法为：int __fastcall function(int a,int b) </div><h2 align="left"> <font size="4">thiscall调用规范</font></h2><p align="left">thiscall是唯一一个不能明确指明的函数修饰，因为thiscall不是关键字。它是C++类成员函数缺省的调用约定。由于成员函数调用还有一个this指针，因此必须特殊处理，thiscall意味着： </p><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: medium; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2">            1) 参数从右向左入栈；<br />            2) 如果参数个数确定，this指针通过ecx传递给被调用者；如果参数个数不确定，this指针在所有参数压栈后被压入堆栈；<br />            3) 对参数个数不定的，调用者清理堆栈，否则函数自己清理堆栈。</font></pre></div><div align="left">为了说明这个调用约定，定义如下类和使用代码： </div><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2"><br /><font color="#0000ff">class</font> A<br />            {<br /><font color="#804040">public</font>:<br /><font color="#0000ff">int</font> function1(<font color="#0000ff">int</font> a,<font color="#0000ff">int</font> b);<br /><font color="#0000ff">int</font> function2(<font color="#0000ff">int</font> a,...);<br />            };<br /><font color="#0000ff">int</font> A::function1 (<font color="#0000ff">int</font> a,<font color="#0000ff">int</font> b)<br />            {<br /><font color="#804040">return</font> a+b;<br />            }<br /><font color="#0000ff">int</font> A::function2(<font color="#0000ff">int</font> a,...)<br />            {<br /><font color="#0000ff">va_list</font> ap;<br />            va_start(ap,a);<br /><font color="#0000ff">int</font> i;<br /><font color="#0000ff">int</font> result = <font color="#ff00ff">0</font>;<br /><font color="#804040">for</font>(i = <font color="#ff00ff">0</font> ; i &lt; a ; i ++)<br />            {<br />            result += va_arg(ap,<font color="#0000ff">int</font>);<br />            }<br /><font color="#804040">return</font> result;<br />            }<br /><font color="#0000ff">void</font> callee()<br />            {<br />            A a;<br />            a.function1(<font color="#ff00ff">1</font>,<font color="#ff00ff">2</font>);<br />            a.function2(<font color="#ff00ff">3</font>,<font color="#ff00ff">1</font>,<font color="#ff00ff">2</font>,<font color="#ff00ff">3</font>);<br />            }</font></pre></div><div align="left">callee函数被翻译成汇编后就变成： </div><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2">// 函数function1调用<br />              0401C1D    push        2<br />              00401C1F   push        1<br />              00401C21   lea         ecx,[ebp-8]<br />              00401C24   call   function1             注意，这里this没有被入栈<br />              // 函数function2调用<br />              00401C29   push        3<br />              00401C2B   push        2<br />              00401C2D   push        1<br />              00401C2F   push        3<br />              00401C31   lea         eax,[ebp-8]      这里引入this指针<br />              00401C34   push        eax<br />              00401C35   call   function2<br />              00401C3A   add         esp,14h</font></pre></div><div align="left"><font size="2">可见，对于参数个数固定情况下，它类似于stdcall，不定时则类似cdecl</font></div><h2 align="left"> <font size="4">naked call调用规范</font></h2><div align="left">这是一个很少见的调用约定，一般程序设计者建议不要使用。编译器不会给这种函数增加初始化和清理代码，更特殊的是，不能用return返回返回值，只能用插入汇编返回结果。这一般用于实模式驱动程序设计，假设定义一个求和的加法程序，可以定义为： </div><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2"> __declspec(naked) <font color="#0000ff">int</font>  add(<font color="#0000ff">int</font> a,<font color="#0000ff">int</font> b)<br />               {<br />                   __asm mov eax,a<br />                   __asm add eax,b<br />                   __asm ret<br />               }</font></pre></div><div align="left">注意，这个函数没有显式的return返回值，返回通过修改eax寄存器实现，而且连退出函数的ret指令都必须显式插入。上面代码被翻译成汇编以后变成： </div><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;">  <font size="2"> mov    eax,[ebp+8]<br />   add    eax,[ebp+12]<br />   ret    8</font></pre></div><div align="left">注意这个修饰是和__stdcall及cdecl结合使用的，前面是它和cdecl结合使用的代码，对于和stdcall结合的代码，则变成： </div><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: small; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2"> __declspec(naked) <font color="#0000ff">int</font> __stdcall function(<font color="#0000ff">int</font> a,<font color="#0000ff">int</font> b)<br />               {<br />                   __asm mov eax,a<br />                   __asm add eax,b<br />                   __asm ret <font color="#ff00ff">8</font><font color="#ff0000">//注意后面的8</font><br />               }</font></pre></div><div align="left">至于这种函数被调用，则和普通的cdecl及stdcall调用函数一致。 </div><h2 align="left"> <font size="4">函数调用约定导致的常见问题</font></h2><div align="left">如果定义的约定和使用的约定不一致，则将导致堆栈被破坏，导致严重问题，下面是两种常见的问题： </div><div align="left"><pre style="padding: 8pt; background: rgb(239, 239, 239) none repeat scroll 0% 50%; font-size: medium; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; margin-left: 2em; margin-right: 10%; font-family: -Lucida Sans Typewriter-;"><font size="2">1) 函数原型声明和函数体定义不一致<br />            2) DLL导入函数时声明了不同的函数约定</font></pre></div></div></div>
		</div>
		<br />
		<br />
<img src ="http://www.cppblog.com/erran/aggbug/34182.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/erran/" target="_blank">erran</a> 2007-10-14 00:33 <a href="http://www.cppblog.com/erran/archive/2007/10/14/34182.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Microsoft's HTML Help (.chm) format</title><link>http://www.cppblog.com/erran/archive/2007/10/14/34180.html</link><dc:creator>erran</dc:creator><author>erran</author><pubDate>Sat, 13 Oct 2007 16:21:00 GMT</pubDate><guid>http://www.cppblog.com/erran/archive/2007/10/14/34180.html</guid><wfw:comment>http://www.cppblog.com/erran/comments/34180.html</wfw:comment><comments>http://www.cppblog.com/erran/archive/2007/10/14/34180.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/erran/comments/commentRss/34180.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/erran/services/trackbacks/34180.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Microsoft's HTML Help (.chm) format																				Preface																				This is documentation on the .chm format used by Microsoft HTMLHelp. This format has been reverse engine...&nbsp;&nbsp;<a href='http://www.cppblog.com/erran/archive/2007/10/14/34180.html'>阅读全文</a><img src ="http://www.cppblog.com/erran/aggbug/34180.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/erran/" target="_blank">erran</a> 2007-10-14 00:21 <a href="http://www.cppblog.com/erran/archive/2007/10/14/34180.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转：C++资源之不完全导引（完整版）</title><link>http://www.cppblog.com/erran/archive/2007/10/14/34178.html</link><dc:creator>erran</dc:creator><author>erran</author><pubDate>Sat, 13 Oct 2007 16:14:00 GMT</pubDate><guid>http://www.cppblog.com/erran/archive/2007/10/14/34178.html</guid><wfw:comment>http://www.cppblog.com/erran/comments/34178.html</wfw:comment><comments>http://www.cppblog.com/erran/archive/2007/10/14/34178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/erran/comments/commentRss/34178.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/erran/services/trackbacks/34178.html</trackback:ping><description><![CDATA[
		<br />
		<table class="fixedTable blogpost" border="0" cellspacing="0" width="100%">
				<tbody>
						<tr>
								<td class="ellipse">
										<span class="bvTitle" id="subjcns!1p5qP-LmQlonNx_ujZCY3OgA!128">
												<strong>
														<font size="3">C++资源之不完全导引（完整版）</font>
												</strong>
										</span>
								</td>
						</tr>
						<tr>
								<td class="bvh8">
										<strong>
												<font size="3">
												</font>
										</strong>
										<br />
								</td>
						</tr>
						<tr>
								<td id="msgcns!1p5qP-LmQlonNx_ujZCY3OgA!128">
										<p>C++资源之不完全导引（完整版）</p>
										<p>来源：<a href="http://www.csdn.net/"><u><font color="#0000ff">www.csdn.net</font></u></a></p>
										<p>撰文：曾毅、陶文</p>
										<p>声明：本文2004年5月首发于《CSDN开发高手》，版权归该杂志与《程序员》杂志社<br />所有。</p>
										<p>------------------------------------------------------------------------<br />--------</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>　　1，前言</p>
		<p>　　无数次听到“我要开始学习C++!”的呐喊，无数次听到“C++太复杂了，我真的<br />学不会”的无奈。Stan Lippman先生曾在《C++ Primer》一书中指出“C++是最为难<br />学的高级程序设计语言之一”，人们常将“之一”去掉以表达自己对C++的敬畏。诚<br />然，C++程序设计语言对于学习者的确有很多难以逾越的鸿沟，体系结构的庞大，应<br />接不暇并不断扩充的特性……除此之外，参考资料之多与冗杂使它的学习者望而却<br />步，欲求深入者苦不堪言。希望这一份不完全导引能够成为您C++学习之路上的引路<br />灯。</p>
		<p>　　撰写本文的初衷并不打算带领大家体验古老的C++历史，如果你想了解C++的历<br />史与其前期发展中诸多技术的演变，你应当去参考Bjarne的《The Design and Evo<br />lution of C++》。当然也不打算给大家一个无所不包的宝典（并非不想：其一是因<br />水平有限，其二无奈C++之博大精深），所给出的仅仅是一些我们认为对于想学习C<br />++的广大读者来说最重要并且触手可及的开发与学习资源。</p>
		<p>　　本文介绍并分析了一些编译器，开发环境，库，少量的书籍以及参考网站，并<br />且尽可能尝试着给出一个利用这些资源的导引，望对如同我们一样的初学者能够有<br />所裨益。</p>
		<p>------------------------------------------------------------------------<br />--------</p>
		<p>　　2，编译器</p>
		<p>　　在C++之外的任何语言中，编译器都从来没有受到过如此之重视。因为C++是一<br />门相当复杂的语言，所以编译器也难于构建。直到最近我们才开始能够使用上完全<br />符合C++标准的编译器（哦，你可能会责怪那些编译器厂商不能尽早的提供符合标准<br />的编译器，这只能怪他们各自维系着自身的一套别人不愿接受的标准）。什么？你<br />说这无关紧要？哦，不，你所需要的是和标准化C++高度兼容的编译环境。长远来看<br />，只有这样的编译器对C++开发人员来说才是最有意义的工具，尤其是对于程序设计<br />语言的学习者。一至性让代码具备可移植性，并让一门语言及其库的应用更为广泛<br />。嗯，是的，我们这里只打算介绍一些公认的优秀编译器。</p>
		<p>　　2.1 Borland C++</p>
		<p>　　这个是Borland C++ Builder和Borland C++ Builder X这两种开发环境的后台<br />编译器。（哦，我之所以将之分为两种开发环境你应当能明白为什么，正如Delphi<br />7到Delphi8的转变，是革命性的两代。）Borland C++由老牌开发工具厂商Borland<br />倾力打造。该公司的编译器素以速度快，空间效率高著称，Borland C++ 系列编译<br />器秉承了这个传统，属于非常优质的编译器。标准化方面早在5.5版本的编译器中对<br />标准化C++的兼容就达到了92.73%。目前最新版本是Borland C++ Builder X中的6.<br />0版本，官方称100%符合ANSI/ISO的C++标准以及C99标准。嗯…这正是我前面所指的<br />“完全符合C++标准的编译器”。</p>
		<p>　　2.2 Visual C++</p>
		<p>　　这个正是我们熟知的Visual Studio 和 Visual Studio.net 2002, 2003以及2<br />005 Whidbey中带的C++编译器。由Microsoft公司研制。在Visual Studio 6.0中，<br />因为编译器有太多地方不能与后来出现的C++标准相吻合而饱受批评（想想你在使用<br />STL的时候编译时报出的那些令人厌恶的error和warning吧）。VC++6.0对标准化C+<br />+的兼容只有83.43%。但是随着C++编译器设计大师Stanley Lippman以及诸多C++社<br />群达人的加盟，在Visual Studio.NET 2003中，Visual C++编译器已经成为一个非<br />常成熟可靠的C++编译器了。Dr.Dobb's Journal的评测显示Visual C++7.1对标准C<br />++的兼容性高达98.22%，一度成为CBX之前兼容性最好的编译器。结合强大的Visua<br />l Studio.NET开发环境，是一个非常不错的选择。至于Whidbey时代的Visual C++,<br />似乎微软所最关注的是C++/CLI……我们不想评论微软下一代的C++编译器对标准化<br />兼容如何，但他确实越来越适合.NET (其实你和我的感觉可能是一样的，微软不应<br />当把标准C++这块肥肉丢给Borland,然而微软可能并不这样认为)。</p>
		<p>　　2.3 GNU C++</p>
		<p>　　著名的开源C++编译器。是类Unix操作系统下编写C++程序的首选。特点是有非<br />常好的移植性，你可以在非常广泛的平台上使用它，同时也是编写跨平台，嵌入式<br />程序很好的选择。另外在符合标准这个方面一直都非常好，GCC3.3大概能够达到96<br />.15%。但是由于其跨平台的特性，在代码尺寸速度等优化上略微差一点。</p>
		<p>　　基于GNU C++的编译器有很多，比如：</p>
		<p>　　(1) Mingw</p>
		<p>　　<a href="http://www.mingw.org/"><u><font color="#0000ff">http://www.mingw.org/</font></u></a></p>
		<p>　　GCC的一个Windows的移植版本（Dev-C++的后台）</p>
		<p>　　(2) Cygwin</p>
		<p>　　<a href="http://sources.redhat.com/cygwin/"><u><font color="#0000ff">http://sources.redhat.com/cygwin/</font></u></a></p>
		<p>　　GCC的另外一个Windows移植版本是Cygwin的一部分，Cygwin是Windows下的一个<br />Unix仿真环境。严格的说是模拟GNU的环境，这也就是"Gnu's Not Unix"要表达的意<br />思，噢，扯远了，这并不是我们在这里关心的实质内容。</p>
		<p>　　(3) Djgpp</p>
		<p>　　<a href="http://www.delorie.com/djgpp/"><u><font color="#0000ff">http://www.delorie.com/djgpp/</font></u></a></p>
		<p>　　这是GCC的DOS移植版本。</p>
		<p>　　(4) RSXNT</p>
		<p>　　<a href="http://www.mathematik.uni-bielefeld.de/%7Erainer/"><u><font color="#0000ff">http://www.mathematik.uni-bielefeld.de/~rainer/</font></u></a></p>
		<p>　　这是GCC的DOS和Windows移植版本。</p>
		<p>　　(5) Intel C++</p>
		<p>　　著名CPU制造厂商Intel出品的编译器，Special Design for Intel x86！对于<br />Intel x86结构的CPU经过特别的优化。在有些应用情况下，特别是数值计算等高性<br />能应用，仅仅采用Intel的编译器编译就能大幅度的提高性能。</p>
		<p>　　(6) Digital Mars C++</p>
		<p>　　网络上提供免费下载，Zortech/Symantec C++的继承者，其前身在当年惨烈的<br />C++四国战中也是主角之一。</p>
		<p>------------------------------------------------------------------------<br />--------</p>
		<p>　　3，开发环境</p>
		<p>　　开发环境对于程序员的作用不言而喻。选择自己朝夕相处的环境也不是容易的<br />事情，特别是在IDE如此丰富的情况下。下面就是我们推荐的一些常见的C++开发环<br />境，并没有包括一些小型的，罕见的IDE。其中任何一款都是功能丰富，可以用作日<br />常开发使用的。对于不同层面的开发者，请参见内文关于适用对象的描述。</p>
		<p>　　3.1 Visual Studio 6.0</p>
		<p>　　这个虽然是Microsoft公司的老版本的开发环境，但是鉴于其后继版本Visual<br />Studio.NET的庞大身躯，以及初学者并不那么高的功能要求，所以推荐这个开发环<br />境给C++的初学者，供其学习C++的最基本的部分，比如C的那部分子集，当然你别指<br />望他能够支持最新的C99标准。在日常的开发中，仍然有很多公司使用这个经典稳定<br />的环境，比如笔者就看曾亲见有些公司将其编译器替换为GCC做手机开发之用。</p>
		<p>　　3.2 Visual Studio.NET 2003</p>
		<p>　　作为Microsoft公司官方正式发布的最新版本开发环境，其中有太多激动人心的<br />功能。结合其最新的C++编译器。对于机器配置比较好的开发人员来说，使用这个开<br />发环境将能满足其大部分的要求。这里不打算单独说Visual Studio Whidbey,虽然<br />Visual Studio .NET 2005 - Whidbey社区预览版已经推出，但暂不是很稳定，读者<br />可以亲身去体验。</p>
		<p>　　3.3 Borland C++ Builder 6</p>
		<p>　　这个并不是Borland的C++开发环境的最新版本。选择它的原因是它不是用Java<br />写的IDE，速度比较快。它有一个很完善的GUI窗体设计器，和Delphi共用一个VCL。<br />由于这些特点，比较适合初学者上手。但是由于其GUI的中心位置，可能不利于对于<br />C++语言的学习。而且其为了支持VCL这个Object Pascal写的库也对C++进行了一些<br />私有的扩充。使得人们有一个不得不接受的事实：“Borland C++ Builder 6的高手<br />几乎都是Delphi高手”。</p>
		<p>　　3.4 Borland C++ Builder X</p>
		<p>　　正如前文所述，虽然版本号上和前面那个IDE非常相象，但是其实它们是完全不<br />同的两个集成开发环境。C++Builder更多的是一个和Delphi同步的C++版本的开发环<br />境，C++BuilderX则是完全从C++的角度思考得出的一个功能丰富的IDE。其最大的特<br />点是跨平台，跨编译器，多种Framework的集成，并且有一个WxWindows为基础的GU<br />I设计器。尤其是采用了纯C++来重写了整个Framework,摒弃了以前令人无奈的版本<br />。对于C++的开发来说，从编译器，到库，到功能集成都是非常理想的。可以预见，<br />Borland C++ Builder X 2.0很值得C++爱好者期待。唯一令人难堪之处是作为一个<br />C++的开发工具，其IDE是用Java写的，在配置不够理想的机器上请慎重考虑再安装<br />。</p>
		<p>　　3.5 Emacs + GCC</p>
		<p>　　前面讲的大部分是Windows环境下的集成开发环境。Linux上的开发者更倾向于<br />使用Emacs来编辑C++的文件，用Makefile来命令GCC做编译。虽然看上去比较松散，<br />但是这些东西综合起来还是一个开0发环境。如果你能够娴熟的使用这样的环境写程<br />序，你的水平应该足够指导我们来写这篇陋文了。</p>
		<p>　　3.6 Dev C++</p>
		<p>　　GCC是一个很好的编译器。在Windows上的C++编译器一直和标准有着一段距离的<br />时候，GCC就是一个让Windows下开发者流口水的编译器。Dev-C++就是能够让GCC跑<br />在Windows下的工具，作为集成开发环境，还提供了同专业IDE相媲美的语法高亮，<br />代码提示，调试等功能。由于使用Delphi开发，占用内存少，速度很快，比较适合<br />轻量级的学习和使用。</p>
		<p>　　3.7 Eclipse + CDT</p>
		<p>　　Eclipse可是近来大名鼎鼎的开发工具。最新一期的Jolt大奖就颁给了这个杰出<br />的神物。说其神奇是因为，它本身是用Java写的，但是拥有比一般Java写的程序快<br />得多的速度。而且因为其基于插件组装一切的原则，使得能够有CDT这样的插件把E<br />clipse变成一个C/C++的开发环境。如果你一直用Eclipse写Java的程序，不妨用它<br />体验一下C++开发的乐趣。</p>
		<p>------------------------------------------------------------------------<br />--------</p>
		<p>　　4，工具</p>
		<p>　　C++的辅助工具繁多，我们分门别类的为大家作介绍：</p>
		<p>　　4.1 文档类</p>
		<p>　　(1) Doxygen</p>
		<p>　　参考站点：<a href="http://www.doxygen.org/"><u><font color="#0000ff">http://www.doxygen.org</font></u></a></p>
		<p>　　Doxygen是一种适合C风格语言（如C++、C、IDL、Java甚至包括C#和PHP）的、<br />开放源码的、基于命令行的文档产生器。</p>
		<p>　　(2) C++2HTML</p>
		<p>　　参考站点：<a href="http://www.bedaux.net/cpp2html/"><u><font color="#0000ff">http://www.bedaux.net/cpp2html/</font></u></a></p>
		<p>　　把C++代码变成语法高亮的HTML</p>
		<p>　　(3) CodeColorizer</p>
		<p>　　参考站点：<a href="http://www.chami.com/colorizer/"><u><font color="#0000ff">http://www.chami.com/colorizer/</font></u></a></p>
		<p>　　它能把好几种语言的源代码着色为HTML</p>
		<p>　　(4) Doc-O-Matic</p>
		<p>　　参考站点：<a href="http://www.doc-o-matic.com/"><u><font color="#0000ff">http://www.doc-o-matic.com/</font></u></a></p>
		<p>　　Doc-O_Matic为你的C/C++，C++.net，Delphi/Pascal, VB.NET，C#和Java程序<br />或者组件产生准确的文档。Doc-O-Matic使用源代码中的符号和注释以及外部的文档<br />文件创建与流行的文档样式一致的文档。</p>
		<p>　　(5) DocVizor</p>
		<p>　　参考站点：<a href="http://www.ucancode.net/Products/DocBuilder/Features.htm"><u><font color="#0000ff">http://www.ucancode.net/Products/DocBuilder/Features.htm</font></u></a></p>
		<p>　　DocVizor满足了面向对象软件开发者的基本要求——它让我们能够看到C++工程<br />中的类层次结构。DocVizor快速地产生完整可供打印的类层次结构图，包括从第三<br />方库中来的那些类，除此之外DocVizor还能从类信息中产生HTML文件。</p>
		<p>　　(6) SourcePublisher C++</p>
		<p>　　参考站点：<a href="http://www.scitools.com/sourcepublisher_c.html"><u><font color="#0000ff">http://www.scitools.com/sourcepublisher_c.html</font></u></a></p>
		<p>　　给源代码产生提供快速直观的HTML报表，包括代码，类层次结构，调用和被调<br />用树，包含和被包含树。支持多种操作系统。</p>
		<p>　　(7) Understand</p>
		<p>　　参考站点：<a href="http://www.scitools.com/ucpp.html"><u><font color="#0000ff">http://www.scitools.com/ucpp.html</font></u></a></p>
		<p>　　分析任何规模的C或者C++工程，帮助我们更好的理解以及编写文档。</p>
		<p>　　4.2 代码类</p>
		<p>　　(1) CC-Rider</p>
		<p>　　参考站点：<a href="http://www.cc-rider.com/"><u><font color="#0000ff">http://www.cc-rider.com</font></u></a></p>
		<p>　　CC-Rider是用于C/C++程序强大的代码可视化工具，通过交互式浏览、编辑及自<br />动文件来促进程序的维持和发展。</p>
		<p>　　(2) CodeInspect</p>
		<p>　　参考站点：<a href="http://www.yokasoft.com/"><u><font color="#0000ff">http://www.yokasoft.com/</font></u></a></p>
		<p>　　一种新的C/C++代码分析工具。它检查我们的源代码找出非标准的，可能的，以<br />及普通的错误代码。</p>
		<p>　　(3) CodeWizard</p>
		<p>　　参考站点：<a href="http://www.parasoft.com/"><u><font color="#0000ff">http://www.parasoft.com</font></u></a></p>
		<p>　　先进的C/C++源代码分析工具，使用超过500个编码规范自动化地标明危险的，<br />但是编译器不能检查到的代码结构。</p>
		<p>　　(4) C++ Validation Test Suites</p>
		<p>　　参考站点：<a href="http://www.plumhall.com/suites.html"><u><font color="#0000ff">http://www.plumhall.com/suites.html</font></u></a></p>
		<p>　　一组用于测试编译器和库对于标准吻合程度的代码库。</p>
		<p>　　(5) CppRefactory</p>
		<p>　　参考站点：<a href="http://cpptool.sourceforge.net/"><u><font color="#0000ff">http://cpptool.sourceforge.net/</font></u></a></p>
		<p>　　CPPRefactory是一个使得开发者能够重构他们的C++代码的程序。目的是使得C<br />++代码的重构能够尽可能的有效率和简单。</p>
		<p>　　(6) Lzz</p>
		<p>　　参考站点：<a href="http://www.lazycplusplus.com/"><u><font color="#0000ff">http://www.lazycplusplus.com/</font></u></a></p>
		<p>　　Lzz是一个自动化许多C++编程中的体力活的工具。它能够节省我们许多事件并<br />且使得编码更加有乐趣。给出一系列的声明，Lzz会给我们创建头文件和源文件。</p>
		<p>　　(7) QA C++ Generation 2000</p>
		<p>　　参考站点：<a href="http://www.programmingresearch.com/solutions/qacpp.htm"><u><font color="#0000ff">http://www.programmingresearch.com/solutions/qacpp.htm</font></u></a></p>
		<p>　　它关注面向对象的C++源代码，对有关于设计，效率，可靠性，可维护性的部分<br />提出警告信息。</p>
		<p>　　(8) s-mail project - Java to C++DOL</p>
		<p>　　参考站点：<a href="http://sadlocha.strefa.pl/s-mail/ja2dol.html"><u><font color="#0000ff">http://sadlocha.strefa.pl/s-mail/ja2dol.html</font></u></a></p>
		<p>　　把Java源代码翻译为相应的C++源代码的命令行工具。</p>
		<p>　　(9) SNIP from Cleanscape Software International</p>
		<p>　　参考站点：<a href="http://www.cleanscape.net/stdprod/snip/index.html"><u><font color="#0000ff">http://www.cleanscape.net/stdprod/snip/index.html</font></u></a></p>
		<p>　　一个填平编码和设计之间沟壑的易于使用的C++开发工具，节省大量编辑和调试<br />的事件，它还使得开发者能够指定设计模式作为对象模型，自动从对象模型中产生<br />C++的类。</p>
		<p>　　(10) SourceStyler C++</p>
		<p>　　参考站点：<a href="http://www.ochresoftware.com/"><u><font color="#0000ff">http://www.ochresoftware.com/</font></u></a></p>
		<p>　　对C/C++源代码提供完整的格式化和排版控制的工具。提供多于75个的格式化选<br />项以及完全支持ANSI C++。</p>
		<p>　　4.3 编译类</p>
		<p>　　(1) Compilercache</p>
		<p>　　参考站点：<a href="http://www.erikyyy.de/compilercache/"><u><font color="#0000ff">http://www.erikyyy.de/compilercache/</font></u></a></p>
		<p>　　Compilercache是一个对你的C和C++编译器的封装脚本。每次我们进行编译，封<br />装脚本，把编译的结果放入缓存，一旦编译相同的东西，结果将从缓存中取出而不<br />是再次编译。</p>
		<p>　　(2) Ccache</p>
		<p>　　参考站点：<a href="http://ccache.samba.org/"><u><font color="#0000ff">http://ccache.samba.org/</font></u></a></p>
		<p>　　Ccache是一个编译器缓存。它使用起来就像C/C++编译器的缓存预处理器，编译<br />速度通常能提高普通编译过程的5~10倍。</p>
		<p>　　(3) Cmm (C++ with MultiMethods)</p>
		<p>　　参考站点：<a href="http://www.op59.net/cmm/cmm-0.28/users.html"><u><font color="#0000ff">http://www.op59.net/cmm/cmm-0.28/users.html</font></u></a></p>
		<p>　　这是一种C++语言的扩展。读入Cmm源代码输出C++的源代码，功能是对C++语言<br />添加了对multimethod的支持。</p>
		<p>　　(4) The Frost Project</p>
		<p>　　参考站点：<a href="http://frost.flewid.de/"><u><font color="#0000ff">http://frost.flewid.de/</font></u></a></p>
		<p>　　Forst使得你能够在C++程序中像原生的C++特性一样使用multimethod以及虚函<br />数参数。它是一个编译器的外壳。</p>
		<p>　　4.4 测试和调试类</p>
		<p>　　(1) CPPUnit</p>
		<p>　　CppUnit 是个基于 LGPL 的开源项目，最初版本移植自 JUnit，是一个非常优<br />秀的开源测试框架。CppUnit 和 JUnit 一样主要思想来源于极限编程。主要功能就<br />是对单元测试进行管理，并可进行自动化测试。</p>
		<p>　　(2) C++Test</p>
		<p>　　参考站点：<a href="http://www.parasoft.com/"><u><font color="#0000ff">http://www.parasoft.com/</font></u></a></p>
		<p>　　C++ Test是一个单元测试工具，它自动化了C和C++类，函数或者组件的测试。</p>
		<p>
				<br />　　(3) Cantata++</p>
		<p>　　参考站点：<a href="http://www.iplbath.com/products/tools/pt400.shtml"><u><font color="#0000ff">http://www.iplbath.com/products/tools/pt400.shtml</font></u></a></p>
		<p>　　设计的目的是为了满足在合理的经济开销下使用这个工具可以让开发工程师开<br />展单元测试和集成测试的需求.</p>
		<p>　　(4) Purify</p>
		<p>　　参考站点：<a href="http://www-900.ibm.com/cn/software/rational/products/purif"><u><font color="#0000ff">http://www-900.ibm.com/cn/software/rational/products/purif</font></u></a><br />yplus/index.shtml</p>
		<p>　　IBM Rational PurifyPlus是一套完整的运行时分析工具，旨在提高应用程序的<br />可靠性和性能。PurifyPlus将内存错误和泄漏检测、应用程序性能描述、代码覆盖<br />分析等功能组合在一个单一、完整的工具包中。</p>
		<p>　　(5) BoundsChecker</p>
		<p>　　BoundsChecker是一个C++运行时错误检测和调试工具。它通过在Visual Studi<br />o内自动化调试过程加速开发并且缩短上市的周期。BoundsChecker提供清楚，详细<br />的程序错误分析，许多是对C++独有的并且在static，stack和heap内存中检测和诊<br />断错误，以及发现内存和资源的泄漏。　　(6) Insure++</p>
		<p>　　参考站点：<a href="http://www.parasoft.com/"><u><font color="#0000ff">http://www.parasoft.com/</font></u></a></p>
		<p>　　一个自动化的运行时程序测试工具，检查难以察觉的错误,如内存覆盖，内存泄<br />漏，内存分配错误，变量初始化错误，变量定义冲突，指针错误，库错误，逻辑错<br />误和算法错误等。</p>
		<p>　　(7) GlowCode</p>
		<p>　　参考站点：<a href="http://www.glowcode.com/"><u><font color="#0000ff">http://www.glowcode.com/</font></u></a></p>
		<p>　　GlowCode包括内存泄漏检查，code profiler，函数调用跟踪等功能。给C++开<br />发者提供完整的错误诊断，和运行时性能分析工具包。</p>
		<p>　　(8) Stack Spy</p>
		<p>　　参考站点：<a href="http://www.imperioustech.com/"><u><font color="#0000ff">http://www.imperioustech.com/</font></u></a></p>
		<p>　　它能捕捉stack corruption, stack over run, stack overflow等有关栈的错<br />误。</p>
		<p>------------------------------------------------------------------------<br />--------</p>
		<p>　　5，库</p>
		<p>　　在C++中，库的地位是非常高的。C++之父 Bjarne Stroustrup先生多次表示了<br />设计库来扩充功能要好过设计更多的语法的言论。现实中，C++的库门类繁多，解决<br />的问题也是极其广泛，库从轻量级到重量级的都有。不少都是让人眼界大开，亦或<br />是望而生叹的思维杰作。由于库的数量非常庞大，而且限于笔者水平，其中很多并<br />不了解。所以文中所提的一些库都是比较著名的大型库。</p>
		<p>　　5.1 标准库</p>
		<p>　　标准库中提供了C++程序的基本设施。虽然C++标准库随着C++标准折腾了许多年<br />，直到标准的出台才正式定型，但是在标准库的实现上却很令人欣慰得看到多种实<br />现，并且已被实践证明为有工业级别强度的佳作。</p>
		<p>　　(1) Dinkumware C++ Library</p>
		<p>　　参考站点：<a href="http://www.dinkumware.com/"><u><font color="#0000ff">http://www.dinkumware.com/</font></u></a></p>
		<p>　　P.J. Plauger编写的高品质的标准库。P.J. Plauger博士是Dr. Dobb's程序设<br />计杰出奖的获得者。其编写的库长期被Microsoft采用，并且最近Borland也取得了<br />其OEM的license，在其C/C++的产品中采用Dinkumware的库。</p>
		<p>　　(2) RogueWave Standard C++ Library</p>
		<p>　　参考站点：<a href="http://www.roguewave.com/"><u><font color="#0000ff">http://www.roguewave.com/</font></u></a></p>
		<p>　　这个库在Borland C++ Builder的早期版本中曾经被采用，后来被其他的库给替<br />换了。笔者不推荐使用。</p>
		<p>　　(3) SGI STL</p>
		<p>　　参考站点：<a href="http://www.roguewave.com/"><u><font color="#0000ff">http://www.roguewave.com/</font></u></a></p>
		<p>　　SGI公司的C++标准模版库。</p>
		<p>　　(4) STLport</p>
		<p>　　参考站点：<a href="http://www.stlport.org/"><u><font color="#0000ff">http://www.stlport.org/</font></u></a></p>
		<p>　　SGI STL库的跨平台可移植版本。</p>
		<p>　　5.2 “准”标准库 - Boost</p>
		<p>　　参考站点：<a href="http://www.boost.org/"><u><font color="#0000ff">http://www.boost.org</font></u></a></p>
		<p>　　国内镜像：<a href="http://www.c-view.org/tech/lib/boost/index.htm"><u><font color="#0000ff">http://www.c-view.org/tech/lib/boost/index.htm</font></u></a></p>
		<p>　　Boost库是一个经过千锤百炼、可移植、提供源代码的C++库，作为标准库的后<br />备，是C++标准化进程的发动机之一。 Boost库由C++标准委员会库工作组成员发起<br />，在C++社区中影响甚大，其成员已近2000人。 Boost库为我们带来了最新、最酷、<br />最实用的技术，是不折不扣的“准”标准库。</p>
		<p>　　Boost中比较有名气的有这么几个库：</p>
		<p>　　Regex</p>
		<p>　　正则表达式库</p>
		<p>　　Spirit</p>
		<p>　　LL parser framework，用C++代码直接表达EBNF</p>
		<p>　　Graph</p>
		<p>　　图组件和算法</p>
		<p>　　Lambda</p>
		<p>　　在调用的地方定义短小匿名的函数对象，很实用的functional功能</p>
		<p>　　concept check</p>
		<p>　　检查泛型编程中的concept</p>
		<p> </p>
		<p>　　Mpl</p>
		<p>　　用模板实现的元编程框架</p>
		<p> </p>
		<p>　　Thread</p>
		<p>　　可移植的C++多线程库</p>
		<p> </p>
		<p>　　Python</p>
		<p>　　把C++类和函数映射到Python之中</p>
		<p>　　Pool</p>
		<p>　　内存池管理</p>
		<p> </p>
		<p>　　smart_ptr</p>
		<p>　　5个智能指针，学习智能指针必读，一份不错的参考是来自CUJ的文章：</p>
		<p>　　Smart Pointers in Boost，哦，这篇文章可以查到，CUJ是提供在线浏览的。<br />中文版见笔者在《Dr. Dobb's Journal软件研发杂志》第7辑上的译文。</p>
		<p>　　Boost总体来说是实用价值很高，质量很高的库。并且由于其对跨平台的强调，<br />对标准C++的强调，是编写平台无关，现代C++的开发者必备的工具。但是Boost中也<br />有很多是实验性质的东西，在实际的开发中实用需要谨慎。并且很多Boost中的库功<br />能堪称对语言功能的扩展，其构造用尽精巧的手法，不要贸然的花费时间研读。Bo<br />ost另外一面，比如Graph这样的库则是具有工业强度，结构良好，非常值得研读的<br />精品代码，并且也可以放心的在产品代码中多多利用。</p>
		<p>　　5.3 GUI</p>
		<p>　　在众多C++的库中，GUI部分的库算是比较繁荣，也比较引人注目的。在实际开<br />发中，GUI库的选择也是非常重要的一件事情，下面我们综述一下可选择的GUI库，<br />各自的特点以及相关工具的支持。</p>
		<p>　　(1) MFC</p>
		<p>　　大名鼎鼎的微软基础类库（Microsoft Foundation Class）。大凡学过VC++的<br />人都应该知道这个库。虽然从技术角度讲，MFC是不大漂亮的，但是它构建于Windo<br />ws API 之上，能够使程序员的工作更容易,编程效率高，减少了大量在建立 Windo<br />ws 程序时必须编写的代码，同时它还提供了所有一般 C++ 编程的优点，例如继承<br />和封装。MFC 编写的程序在各个版本的Windows操作系统上是可移植的，例如，在<br />Windows 3.1下编写的代码可以很容易地移植到 Windows NT 或 Windows 95 上。但<br />是在最近发展以及官方支持上日渐势微。</p>
		<p>　　(2) QT</p>
		<p>　　参考网站：<a href="http://www.trolltech.com/"><u><font color="#0000ff">http://www.trolltech.com/</font></u></a></p>
		<p>　　Qt是Trolltech公司的一个多平台的C++图形用户界面应用程序框架。它提供给<br />应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的<br />很容易扩展，并且允许真正地组件编程。自从1996年早些时候，Qt进入商业领域，<br />它已经成为全世界范围内数千种成功的应用程序的基础。Qt也是流行的Linux桌面环<br />境KDE 的基础，同时它还支持Windows、Macintosh、Unix/X11等多种平台。</p>
		<p>　　(3) WxWindows</p>
		<p>　　参考网站：<a href="http://www.wxwindows.org/"><u><font color="#0000ff">http://www.wxwindows.org/</font></u></a></p>
		<p>　　跨平台的GUI库。因为其类层次极像MFC，所以有文章介绍从MFC到WxWindows的<br />代码移植以实现跨平台的功能。通过多年的开发也是一个日趋完善的GUI库，支持同<br />样不弱于前面两个库。并且是完全开放源代码的。新近的C++ Builder X的GUI设计<br />器就是基于这个库的。</p>
		<p>　　(4) Fox</p>
		<p>　　参考网站：<a href="http://www.fox-toolkit.org/"><u><font color="#0000ff">http://www.fox-toolkit.org/</font></u></a></p>
		<p>　　开放源代码的GUI库。作者从自己亲身的开发经验中得出了一个理想的GUI库应<br />该是什么样子的感受出发，从而开始了对这个库的开发。有兴趣的可以尝试一下。</p>
		<p>
				<br />　　(5) WTL</p>
		<p>　　基于ATL的一个库。因为使用了大量ATL的轻量级手法，模板等技术，在代码尺<br />寸，以及速度优化方面做得非常到位。主要面向的使用群体是开发COM轻量级供网络<br />下载的可视化控件的开发者。</p>
		<p>　　(6) GTK</p>
		<p>　　参考网站：<a href="http://gtkmm.sourceforge.net/"><u><font color="#0000ff">http://gtkmm.sourceforge.net/</font></u></a></p>
		<p>　　GTK是一个大名鼎鼎的C的开源GUI库。在Linux世界中有Gnome这样的杀手应用。<br />而GTK就是这个库的C++封装版本。</p>
		<p>　　5.4 网络通信</p>
		<p>　　(1) ACE</p>
		<p>　　参考网站：<a href="http://www.cs.wustl.edu/%7Eschmidt/ACE.html"><u><font color="#0000ff">http://www.cs.wustl.edu/~schmidt/ACE.html</font></u></a></p>
		<p>　　C++库的代表，超重量级的网络通信开发框架。ACE自适配通信环境（Adaptive<br /> Communication Environment）是可以自由使用、开放源代码的面向对象框架，在<br />其中实现了许多用于并发通信软件的核心模式。ACE提供了一组丰富的可复用C++包<br />装外观（Wrapper Facade）和框架组件，可跨越多种平台完成通用的通信软件任务<br />，其中包括：事件多路分离和事件处理器分派、信号处理、服务初始化、进程间通<br />信、共享内存管理、消息路由、分布式服务动态（重）配置、并发执行和同步，等<br />等。</p>
		<p>　　(2) StreamModule</p>
		<p>　　参考网站：<a href="http://www.omnifarious.org/StrMod/"><u><font color="#0000ff">http://www.omnifarious.org/StrMod/</font></u></a></p>
		<p>　　设计用于简化编写分布式程序的库。尝试着使得编写处理异步行为的程序更容<br />易，而不是用同步的外壳包起异步的本质。</p>
		<p>　　(3) SimpleSocket</p>
		<p>　　参考网站：<a href="http://home.hetnet.nl/%7Elcbokkers/simsock.htm"><u><font color="#0000ff">http://home.hetnet.nl/~lcbokkers/simsock.htm</font></u></a></p>
		<p>　　这个类库让编写基于socket的客户/服务器程序更加容易。</p>
		<p>　　(4) A Stream Socket API for C++</p>
		<p>　　参考网站：<a href="http://www.pcs.cnu.edu/%7Edgame/sockets/socketsC++/sockets.h"><u><font color="#0000ff">http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.h</font></u></a><br />tml</p>
		<p>　　又一个对Socket的封装库。</p>
		<p>　　5.5 XML</p>
		<p>　　(1) Xerces</p>
		<p>　　参考网站：<a href="http://xml.apache.org/xerces-c/"><u><font color="#0000ff">http://xml.apache.org/xerces-c/</font></u></a></p>
		<p>　　Xerces-C++ 是一个非常健壮的XML解析器，它提供了验证，以及SAX和DOM API<br />。XML验证在文档类型定义(Document Type Definition，DTD)方面有很好的支持，<br />并且在2001年12月增加了支持W3C XML Schema 的基本完整的开放标准。</p>
		<p>　　(2) XMLBooster</p>
		<p>　　参考网站：<a href="http://www.xmlbooster.com/"><u><font color="#0000ff">http://www.xmlbooster.com/</font></u></a></p>
		<p>　　这个库通过产生特制的parser的办法极大的提高了XML解析的速度，并且能够产<br />生相应的GUI程序来修改这个parser。在DOM和SAX两大主流XML解析办法之外提供了<br />另外一个可行的解决方案。</p>
		<p>　　(3) Pull Parser</p>
		<p>　　参考网站：<a href="http://www.extreme.indiana.edu/xgws/xsoap/xpp/"><u><font color="#0000ff">http://www.extreme.indiana.edu/xgws/xsoap/xpp/</font></u></a></p>
		<p>　　这个库采用pull方法的parser。在每个SAX的parser底层都有一个pull的parse<br />r，这个xpp把这层暴露出来直接给大家使用。在要充分考虑速度的时候值得尝试。</p>
		<p>
				<br />　　(4) Xalan</p>
		<p>　　参考网站：<a href="http://xml.apache.org/xalan-c/"><u><font color="#0000ff">http://xml.apache.org/xalan-c/</font></u></a></p>
		<p>　　Xalan是一个用于把XML文档转换为HTML，纯文本或者其他XML类型文档的XSLT处<br />理器。</p>
		<p>　　(5) CMarkup</p>
		<p>　　参考网站：<a href="http://www.firstobject.com/xml.htm"><u><font color="#0000ff">http://www.firstobject.com/xml.htm</font></u></a></p>
		<p>　　这是一种使用EDOM的XML解析器。在很多思路上面非常灵活实用。值得大家在D<br />OM和SAX之外寻求一点灵感。</p>
		<p>　　(6) libxml++</p>
		<p>　　<a href="http://libxmlplusplus.sourceforge.net/"><u><font color="#0000ff">http://libxmlplusplus.sourceforge.net/</font></u></a></p>
		<p>　　libxml++是对著名的libxml XML解析器的C++封装版本</p>
		<p>　　5.6 科学计算</p>
		<p>　　(1) Blitz++</p>
		<p>　　参考网站：<a href="http://www.oonumerics.org/blitz/"><u><font color="#0000ff">http://www.oonumerics.org/blitz/</font></u></a></p>
		<p>　　Blitz++ 是一个高效率的数值计算函数库，它的设计目的是希望建立一套既具<br />像C++ 一样方便，同时又比Fortran速度更快的数值计算环境。通常，用C++所写出<br />的数值程序，比 Fortran慢20%左右，因此Blitz++正是要改掉这个缺点。方法是利<br />用C++的template技术，程序执行甚至可以比Fortran更快。Blitz++目前仍在发展中<br />，对于常见的SVD，FFTs，QMRES等常见的线性代数方法并不提供，不过使用者可以<br />很容易地利用Blitz++所提供的函数来构建。</p>
		<p>　　(2) POOMA</p>
		<p>　　参考网站：<a href="http://www.codesourcery.com/pooma/pooma"><u><font color="#0000ff">http://www.codesourcery.com/pooma/pooma</font></u></a></p>
		<p>　　POOMA是一个免费的高性能的C++库，用于处理并行式科学计算。POOMA的面向对<br />象设计方便了快速的程序开发，对并行机器进行了优化以达到最高的效率，方便在<br />工业和研究环境中使用。</p>
		<p>　　(3) MTL</p>
		<p>　　参考网站：<a href="http://www.osl.iu.edu/research/mtl/"><u><font color="#0000ff">http://www.osl.iu.edu/research/mtl/</font></u></a></p>
		<p>　　Matrix Template Library(MTL)是一个高性能的泛型组件库，提供了各种格式<br />矩阵的大量线性代数方面的功能。在某些应用使用高性能编译器的情况下，比如In<br />tel的编译器，从产生的汇编代码可以看出其与手写几乎没有两样的效能。</p>
		<p>　　(4) CGAL</p>
		<p>　　参考网站：<a href="http://www.cgal.org/"><u><font color="#0000ff">www.cgal.org</font></u></a></p>
		<p>　　Computational Geometry Algorithms Library的目的是把在计算几何方面的大<br />部分重要的解决方案和方法以C++库的形式提供给工业和学术界的用户。</p>
		<p>　　5.7 游戏开发</p>
		<p>　　(1) Audio/Video 3D C++ Programming Library</p>
		<p>　　参考网站：<a href="http://www.galacticasoftware.com/products/av/"><u><font color="#0000ff">http://www.galacticasoftware.com/products/av/</font></u></a></p>
		<p>　　***3D是一个跨平台，高性能的C++库。主要的特性是提供3D图形，声效支持（S<br />B,以及S3M），控制接口（键盘，鼠标和遥感），XMS。</p>
		<p>　　(2) KlayGE</p>
		<p>　　参考网站：<a href="http://home.g365.net/enginedev/"><u><font color="#0000ff">http://home.g365.net/enginedev/</font></u></a></p>
		<p>　　国内游戏开发高手自己用C++开发的游戏引擎。KlayGE是一个开放源代码、跨平<br />台的游戏引擎，并使用Python作脚本语言。KlayGE在LGPL协议下发行。感谢龚敏敏<br />先生为中国游戏开发事业所做出的贡献。</p>
		<p>　　(3) OGRE</p>
		<p>　　参考网站：<a href="http://www.ogre3d.org/"><u><font color="#0000ff">http://www.ogre3d.org</font></u></a></p>
		<p>　　OGRE（面向对象的图形渲染引擎）是用C++开发的，使用灵活的面向对象3D引擎<br />。它的目的是让开发者能更方便和直接地开发基于3D硬件设备的应用程序或游戏。<br />引擎中的类库对更底层的系统库（如：Direct3D和OpenGL）的全部使用细节进行了<br />抽象，并提供了基于现实世界对象的接口和其它类。</p>
		<p>　　5.8 线程</p>
		<p>　　(1) C++ Threads</p>
		<p>　　参考网站：<a href="http://threads.sourceforge.net/"><u><font color="#0000ff">http://threads.sourceforge.net/</font></u></a></p>
		<p>　　这个库的目标是给程序员提供易于使用的类，这些类被继承以提供在Linux环境<br />中很难看到的大量的线程方面的功能。</p>
		<p>　　(2) ZThreads</p>
		<p>　　参考网站：<a href="http://zthread.sourceforge.net/"><u><font color="#0000ff">http://zthread.sourceforge.net/</font></u></a></p>
		<p>　　一个先进的面向对象，跨平台的C++线程和同步库。</p>
		<p>　　5.9 序列化</p>
		<p>　　(1) s11n</p>
		<p>　　参考网站：<a href="http://s11n.net/"><u><font color="#0000ff">http://s11n.net/</font></u></a></p>
		<p>　　一个基于STL的C++库，用于序列化POD，STL容器以及用户定义的类型。</p>
		<p>　　(2) Simple XML Persistence Library</p>
		<p>　　参考网站：<a href="http://sxp.sourceforge.net/"><u><font color="#0000ff">http://sxp.sourceforge.net/</font></u></a></p>
		<p>　　这是一个把对象序列化为XML的轻量级的C++库。</p>
		<p>　　5.10 字符串</p>
		<p>　　(1) C++ Str Library</p>
		<p>　　参考网站：<a href="http://www.utilitycode.com/str/"><u><font color="#0000ff">http://www.utilitycode.com/str/</font></u></a></p>
		<p>　　操作字符串和字符的库，支持Windows和支持gcc的多种平台。提供高度优化的<br />代码，并且支持多线程环境和Unicode，同时还有正则表达式的支持。</p>
		<p>　　(2) Common Text Transformation Library</p>
		<p>　　参考网站：<a href="http://cttl.sourceforge.net/"><u><font color="#0000ff">http://cttl.sourceforge.net/</font></u></a></p>
		<p>　　这是一个解析和修改STL字符串的库。CTTL substring类可以用来比较，插入，<br />替换以及用EBNF的语法进行解析。</p>
		<p>　　(3) GRETA</p>
		<p>　　参考网站：<a href="http://research.microsoft.com/projects/greta/"><u><font color="#0000ff">http://research.microsoft.com/projects/greta/</font></u></a></p>
		<p>　　这是由微软研究院的研究人员开发的处理正则表达式的库。在小型匹配的情况<br />下有非常优秀的表现。</p>
		<p>　　5.11 综合</p>
		<p>　　(1) P::Classes</p>
		<p>　　参考网站：<a href="http://pclasses.com/"><u><font color="#0000ff">http://pclasses.com/</font></u></a></p>
		<p>　　一个高度可移植的C++应用程序框架。当前关注类型和线程安全的signal/slot<br />机制，i/o系统包括基于插件的网络协议透明的i/o架构，基于插件的应用程序消息<br />日志框架，访问sql数据库的类等等。</p>
		<p>　　(2) ACDK - Artefaktur Component Development Kit</p>
		<p>　　参考网站：<a href="http://acdk.sourceforge.net/"><u><font color="#0000ff">http://acdk.sourceforge.net/</font></u></a></p>
		<p>　　这是一个平台无关的C++组件框架，类似于Java或者.NET中的框架（反射机制，<br />线程，Unicode，废料收集，I/O，网络，实用工具，XML，等等），以及对Java, P<br />erl, Python, TCL, Lisp, COM 和 CORBA的集成。</p>
		<p>　　(3) dlib C++ library</p>
		<p>　　参考网站：<a href="http://www.cis.ohio-state.edu/%7Ekingd/dlib/"><u><font color="#0000ff">http://www.cis.ohio-state.edu/~kingd/dlib/</font></u></a></p>
		<p>　　各种各样的类的一个综合。大整数，Socket，线程，GUI，容器类,以及浏览目<br />录的API等等。</p>
		<p>　　(4) Chilkat C++ Libraries</p>
		<p>　　参考网站：<a href="http://www.chilkatsoft.com/cpp_libraries.asp"><u><font color="#0000ff">http://www.chilkatsoft.com/cpp_libraries.asp</font></u></a></p>
		<p>　　这是提供zip，e-mail，编码，S/MIME，XML等方面的库。</p>
		<p>　　(5) C++ Portable Types Library (PTypes)</p>
		<p>　　参考网站：<a href="http://www.melikyan.com/ptypes/"><u><font color="#0000ff">http://www.melikyan.com/ptypes/</font></u></a></p>
		<p>　　这是STL的比较简单的替代品，以及可移植的多线程和网络库。</p>
		<p>　　(6) LFC</p>
		<p>　　参考网站：<a href="http://lfc.sourceforge.net/"><u><font color="#0000ff">http://lfc.sourceforge.net/</font></u></a></p>
		<p>　　哦，这又是一个尝试提供一切的C++库</p>
		<p>　　5.12 其他库</p>
		<p>　　(1) Loki</p>
		<p>　　参考网站：<a href="http://www.moderncppdesign.com/"><u><font color="#0000ff">http://www.moderncppdesign.com/</font></u></a></p>
		<p>　　哦，你可能抱怨我早该和Boost一起介绍它，一个实验性质的库。作者在loki中<br />把C++模板的功能发挥到了极致。并且尝试把类似设计模式这样思想层面的东西通过<br />库来提供。同时还提供了智能指针这样比较实用的功能。</p>
		<p>　　(2) ATL</p>
		<p>　　ATL(Active Template Library)</p>
		<p>　　是一组小巧、高效、灵活的类，这些类为创建可互操作的COM组件提供了基本的<br />设施。</p>
		<p>　　(3) FC++: The Functional C++ Library</p>
		<p>　　这个库提供了一些函数式语言中才有的要素。属于用库来扩充语言的一个代表<br />作。如果想要在OOP之外寻找另一分的乐趣，可以去看看函数式程序设计的世界。大<br />师Peter Norvig在 “Teach Yourself Programming in Ten Years”一文中就将函<br />数式语言列为至少应当学习的6类编程语言之一。</p>
		<p>　　(4) FACT!</p>
		<p>　　参考网站：<a href="http://www.kfa-juelich.de/zam/FACT/start/index.html"><u><font color="#0000ff">http://www.kfa-juelich.de/zam/FACT/start/index.html</font></u></a></p>
		<p>　　另外一个实现函数式语言特性的库</p>
		<p>　　(5) Crypto++</p>
		<p>　　提供处理密码，消息验证，单向hash，公匙加密系统等功能的免费库。</p>
		<p>　　还有很多非常激动人心或者是极其实用的C++库，限于我们的水平以及文章的篇<br />幅不能包括进来。在对于这些已经包含近来的库的介绍中，由于并不是每一个我们<br />都使用过，所以难免有偏颇之处，请读者见谅。</p>
		<p>------------------------------------------------------------------------<br />--------</p>
		<p>　　6，书籍</p>
		<p>　　以前熊节先生曾撰文评论相对于Java程序设计语言，C++的好书多如牛毛。荣耀<br />先生在《程序员》杂志上撰文《C++程序设计之四书五经》也将本领域内几乎所有的<br />经典书籍作了全面的介绍,任何关于书的评论此时看来便是很多余的了。个人浅见，<br />除非你打算以C++作为唯一兴趣或者生存之本，一般读者确实没有足够的时间和必要<br />将20余本书籍全部阅读。更有参考价值的是荣耀先生的另一篇文章：《至少应该阅<br />读的九本C++著作》，可以从下面的地址浏览到此文：</p>
		<p>　　<a href="http://www.royaloo.com/articles/articles_2003/9CppBooks.htm"><u><font color="#0000ff">http://www.royaloo.com/articles/articles_2003/9CppBooks.htm</font></u></a></p>
		<p>　　下面几本书对于走在C++初学之路上的读者是我们最愿意推荐给大家的：</p>
		<p>　　(1) 《C++ Primer》</p>
		<p>　　哦，也许你会抱怨我们为什么不先介绍TCPL,但对于走在学习之路上的入门者，<br />本书内容更为全面，更为详细易懂，我们称它为“C++的超级宝典”并不过分。配有<br />一本不错的习题解答《C++ Primer Answer Book》可以辅助你的学习之路。</p>
		<p>　　(2) 《Essential C++》</p>
		<p>　　如果说《C++ Primer》是C++领域的超级宝典，那么此书作为掌握C++的大局观<br />当之无愧。正如《.NET大局观》一书能够让读者全揽.NET，本书讲述了C++中最核心<br />的全部主题。书虽不厚，内容精炼，不失为《C++ Primer》读者茶余饭后的主题回<br />顾之作。</p>
		<p>　　(3) 《The C++ Programming Language》</p>
		<p>　　Bjarne为你带来的C++教程，真正能够告诉你怎么用才叫真正的C++的唯一一本<br />书。虽然如同“某某程序设计语言”这样的书籍会给大家一个内容全揽，入门到精<br />通的感觉，但本书确实不太适合初学者阅读。如果你自认为是一名很有经验的C++程<br />序员，那至少也要反复咀嚼Bjarne先生所强调的若干内容。</p>
		<p>　　(4) 《Effective C++》，《More Effective C++》</p>
		<p>　　是的，正如一些C++爱好者经常以读过与没有读过上述两本作品来区分你是否是<br />C++高手。我们也极力推崇这两本著作。在各种介绍C++专家经验的书籍里面，这两<br />本是最贴近语言本质，看后最能够有脱胎换骨感觉的书，读此书你需每日三省汝身<br />。</p>
		<p>　　技术书籍仁者见仁，过多的评论反无太多意义，由读者喜好选择最适合自己的<br />书方为上策。</p>
		<p>------------------------------------------------------------------------<br />--------</p>
		<p>　　7，资源网站</p>
		<p>　　正如我们可以通过计算机历史上的重要人物了解计算机史的发展，C++相关人物<br />的网站也可以使我们得到最有价值的参考与借鉴，下面的人物我们认为没有介绍的<br />必要，只因下面的人物在C++领域的地位众所周知，我们只将相关的资源进行罗列以<br />供读者学习，他们有的工作于贝尔实验室，有的工作于知名编译器厂商，有的在不<br />断推进语言的标准化，有的为读者撰写了多部千古奇作……<br />　　(1) Bjarne Stroustrup<br />　　<a href="http://www.research.att.com/%7Ebs/"><u><font color="#0000ff">http://www.research.att.com/~bs/</font></u></a></p>
		<p>　　(2) Stanley B. Lippman<br />　　<a href="http://blogs.msdn.com/slippman/"><u><font color="#0000ff">http://blogs.msdn.com/slippman/</font></u></a><br />　　中文版 <a href="http://www.zengyihome.net/slippman/index.htm"><u><font color="#0000ff">http://www.zengyihome.net/slippman/index.htm</font></u></a></p>
		<p>　　(3) Scott Meyers<br />　　<a href="http://www.aristeia.com/"><u><font color="#0000ff">http://www.aristeia.com/</font></u></a></p>
		<p>　　(4) David Musser<br />　　<a href="http://www.cs.rpi.edu/%7Emusser/"><u><font color="#0000ff">http://www.cs.rpi.edu/~musser/</font></u></a></p>
		<p>　　(5) Bruce Eckel<br />　　<a href="http://www.bruceeckel.com/"><u><font color="#0000ff">http://www.bruceeckel.com</font></u></a></p>
		<p>　　(6) Nicolai M. Josuttis<br />　　<a href="http://www.josuttis.com/"><u><font color="#0000ff">http://www.josuttis.com/</font></u></a></p>
		<p>　　(7) Herb Sutter<br />　　<a href="http://www.gotw.ca/"><u><font color="#0000ff">http://www.gotw.ca/</font></u></a></p>
		<p>　　(8) Andrei Alexandrescu<br />　　<a href="http://www.coderncppdesign.com/"><u><font color="#0000ff">http://www.coderncppdesign.com/</font></u></a></p>
		<p>　　(9) 侯捷先生<br />　　<a href="http://www.jjhou.com/"><u><font color="#0000ff">http://www.jjhou.com</font></u></a></p>
		<p>　　(10) 孟岩先生<br />　　先生繁忙于工作，痴迷于技术，暂无个人主页，关于先生的作品可以通过CSDN<br />的专栏和侯先生的主页访问到。</p>
		<p>　　(11) 荣耀先生<br />　　<a href="http://www.royaloo.com/"><u><font color="#0000ff">http://www.royaloo.com/</font></u></a></p>
		<p>　　(12) 潘爱民先生<br />　　<a href="http://www.icst.pku.edu.cn/panaimin/pam_homepage.htm"><u><font color="#0000ff">http://www.icst.pku.edu.cn/panaimin/pam_homepage.htm</font></u></a></p>
		<p>　　除了上述大师的主页外，以下的综合类C++学习参考站点是我们非常愿意向大家<br />推荐的：</p>
		<p>　　(1) CodeProject<br />　　<a href="http://www.codeproject.com/"><u><font color="#0000ff">http://www.codepr