<?xml version="1.0" encoding="GB2312"?>  
<rss version="2.0" 
xmlns:dc="http://purl.org/dc/elements/1.1/" 
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" 
xmlns:admin="http://webns.net/mvcb/" 
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> 
  
<channel> 
<title><![CDATA[Yachun Miao]]></title> 
<link>http://http88.bokee.com/index.html</link> 
<description><![CDATA[<a href="http://ycmilo.bokee.com"><b>Welcome to Yachun Miao's bokee!  </b></a>
<p><b>看书　笔记　实践　笔记　看书　笔记　实践　笔记．．．．．．</b></p>

]]></description> 
<dc:language>zh-cn</dc:language> 
<dc:creator>miaoyachun@163.com</dc:creator> 
<dc:date>2007-01-30T17:50:38Z</dc:date> 
<admin:generatorAgent rdf:resource="http://blog.bokee.com.com" /> 

<item> 
<title><![CDATA[字符编码问题]]></title> 
<link>http://http88.bokee.com/6078852.html</link> 
<description><![CDATA[<span id="BlogViewId" be:lasthandle="cns!93B1069870A68F03!126" be:firsthandle="cns!93B1069870A68F03!128" be:sortkey="" be:sortmode="Category"><p>这是一篇程序员写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念，增进知识，类似于打RPG游戏的升级。整理这篇文章的动机是两个问题：<br /><br /><strong>问题一：</strong><br />&amp;nbsp;&amp;nbsp;&amp;nbsp; 使用Windows记事本的“另存为”，可以在GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件，Windows是怎样识别编码方式的呢？<br /><br />&amp;nbsp;&amp;nbsp;&amp;nbsp; 我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节，分别是FF、FE（Unicode）,FE、FF（Unicode big endian）,EF、BB、BF（UTF-8）。但这些标记是基于什么标准呢？ </p><p><br /><strong>问题二：<br /></strong>&amp;nbsp;&amp;nbsp;&amp;nbsp; 最近在网上看到一个ConvertUTF.c，实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互转换。对于Unicode (UCS2)、 GBK、UTF-8这些编码方式，我原来就了解。但这个程序让我有些糊涂，想不起来UTF-16和UCS2有什么关系。<br /><br />查了查相关资料，总算将这些问题弄清楚了，顺带也了解了一些Unicode的细节。写成一篇文章，送给有过类似疑问的朋友。本文在写作时尽量做到通俗易懂，但要求读者知道什么是字节，什么是十六进制。<br /></p><p><strong>0、big endian和little endian<br /></strong><br />big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时，究竟是将6C写在前面，还是将49写在前面？如果将6C写在前面，就是big endian。如果将49写在前面，就是little endian。<br /><br />“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开，由此曾发生过六次叛乱，一个皇帝送了命，另一个丢了王位。<br /><br />我们一般将endian翻译成“字节序”，将big endian和little endian称作“大尾”和“小尾”。 </p><p><br /><strong>1、字符编码、内码，顺带介绍汉字编码</strong><br /><br />字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码，为了处理汉字，程序员设计了用于简体中文的GB2312和用于繁体中文的big5。<br /><br />GB2312(1980年)一共收录了7445个字符，包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7，低字节从A1-FE，占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。<br /><br />GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号，它分为汉字区和图形符号区。汉字区包括21003个字符。<br /><br />从ASCII、 GB2312到GBK，这些编码方法是向下兼容的，即同一个字符在这些方案中总是有相同的编码，后面的标准支持更多的字符。在这些编码中，英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼，GB2312、GBK都属于双字节字符集 (DBCS)。<br /><br />2000 年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字，同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。从汉字字汇上说，GB18030在GB13000.1的20902个汉字的基础上增加了CJK扩展A的6582个汉字（Unicode码 0x3400-0x4db5），一共收录了27484个汉字。<br /><br />CJK就是中日韩的意思。Unicode为了节省码位，将中日韩三国语言中的文字统一编码。GB13000.1就是ISO/IEC 10646-1的中文版，相当于Unicode 1.1。<br /><br />GB18030 的编码采用单字节、双字节和4字节方案。其中单字节、双字节和GBK是完全兼容的。4字节编码的码位就是收录了CJK扩展A的6582个汉字。例如：UCS的0x3400在GB18030中的编码应该是8139EF30，UCS的0x3401在GB18030中的编码应该是8139EF31。<br /><br />微软提供了GB18030的升级包，但这个升级包只是提供了一套支持CJK扩展A的6582个汉字的新字体：新宋体-18030，并不改变内码。Windows 的内码仍然是GBK。<br /><br />这里还有一些细节：<br /><br />&amp;nbsp;&amp;nbsp;&amp;nbsp; *GB2312的原文还是区位码，从区位码到内码，需要在高字节和低字节上分别加上A0。<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; *对于任何字符编码，编码单元的顺序是由编码方案指定的，与endian无关。例如GBK的编码单元是字节，用两个字节表示一个汉字。这两个字节的顺序是固定的，不受CPU字节序的影响。UTF-16的编码单元是word（双字节），word之间的顺序是编码方案指定的，word内部的字节排列才会受到endian的影响。后面还会介绍UTF-16。<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; *GB2312 的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析：在读取DBCS字符流时，只要遇到高位为1的字节，就可以将下两个字节作为一个双字节编码，而不用管低字节的高位是什么。<br /><br /><strong>2、Unicode、UCS和UTF</strong><br /><br />前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容（更准确地说，是与ISO-8859-1兼容），与GB码不兼容。例如“汉”字的Unicode编码是6C49，而GB码是BABA。<br /><br />Unicode 也是一种字符编码方法，不过它是由国际组织设计，可以容纳全世界所有语言文字的编码方案。Unicode的学名是&amp;quot;Universal Multiple-Octet Coded Character Set&amp;quot;，简称为UCS。UCS可以看作是&amp;quot;Unicode Character Set&amp;quot;的缩写。<br /><br />根据维基百科全书(<a href="http://zh.wikipedia.org/wiki/%29%E7%9A%84%E8%AE%B0%E8%BD%BD%EF%BC%9A%E5%8E%86%E5%8F%B2%E4%B8%8A%E5%AD%98%E5%9C%A8%E4%B8%A4%E4%B8%AA%E8%AF%95%E5%9B%BE%E7%8B%AC%E7%AB%8B%E8%AE%BE%E8%AE%A1Unicode%E7%9A%84%E7%BB%84%E7%BB%87" target="_blank"><u><font color="#0000ff">http://zh.wikipedia.org/wiki/)的记载：历史上存在两个试图独立设计Unicode的组织</font></u></a>，即国际标准化组织（ISO）和一个软件制造商的协会（unicode.org）。ISO开发了ISO 10646项目，Unicode协会开发了Unicode项目。<br /><br />在1991年前后，双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果，并为创立一个单一编码表而协同工作。从Unicode2.0开始，Unicode项目采用了与ISO 10646-1相同的字库和字码。<br /><br />目前两个项目仍都存在，并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是ISO 10646-3:2003。<br /><br />UCS 只是规定如何编码，并没有规定如何传输、保存这个编码。例如“汉”字的UCS编码是6C49，我可以用4个ascii数字来传输、保存这个编码；也可以用 utf-8编码:3个连续的字节E6B1 89来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO- 8859-1完全兼容。UTF是“UCS Transformation Format”的缩写。<br /><br />IETF的RFC2781和 RFC3629以RFC的一贯风格，清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。 </p><p><br /><strong>2.1、内码和code page</strong><br /><br />目前Windows的内核已经支持Unicode字符集，这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特定语言的编码，例如GBK，Windows不可能不支持现有的编码，而全部改用Unicode。<br /><br />Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到的内码。GBK对应的code page是CP936。<br /><br />微软也为GB18030定义了code page：CP54936。但是由于GB18030有一部分4字节编码，而Windows的代码页只支持单字节和双字节编码，所以这个code page是无法真正使用的。 </p><p><br /><strong>3、UCS-2、UCS-4、BMP<br /></strong><br />UCS有两种格式：UCS-2和UCS-4。顾名思义，UCS-2就是用两个字节编码，UCS-4就是用4个字节（实际上只用了31位，最高位必须为0）编码。下面让我们做一些简单的数学游戏：<br /><br />UCS-2有2^16=65536个码位，UCS-4有2^31=2147483648个码位。<br /><br />UCS -4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为 256行 (rows)，每行包含256个cells。当然同一行的cells只是最后一个字节不同，其余都相同。<br /><br />group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中，高两个字节为0的码位被称作BMP。<br /><br />将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节，就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。 </p><p><br /><strong>4、UTF编码<br /></strong><br />UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下：<br />UCS-2编码(16进制)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UTF-8 字节流(二进制)<br />0000 - 007F&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0xxxxxxx<br />0080 - 07FF&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 110xxxxx 10xxxxxx<br />0800 - FFFF&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1110xxxx 10xxxxxx 10xxxxxx<br /><br />例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间，所以肯定要用3字节模板了：1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是：0110 110001 001001， 用这个比特流依次代替模板中的x，得到：11100110 10110001 10001001，即E6 B1 89。<br /><br />读者可以用记事本测试一下我们的编码是否正确。需要注意，UltraEdit在打开utf-8编码的文本文件时会自动转换为UTF-16，可能产生混淆。你可以在设置中关掉这个选项。更好的工具是Hex Workshop。<br /><br />UTF -16以16位为单元对UCS进行编码。对于小于0x10000的UCS码，UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于 0x10000的UCS码，定义了一个算法。不过由于实际使用的UCS2，或者UCS4的BMP必然小于0x10000，所以就目前而言，可以认为 UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案，UTF-16却要用于实际的传输，所以就不得不考虑字节序的问题。 </p><p><br /><strong>5、UTF的字节序和BOM<br /></strong><br />UTF-8以字节为编码单元，没有字节序的问题。UTF-16以两个字节为编码单元，在解释一个UTF-16文本前，首先要弄清楚每个编码单元的字节序。例如“奎”的Unicode编码是594E， “乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”，那么这是“奎” 还是“乙”？<br /><br />Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表，而是Byte Order Mark。BOM是一个有点小聪明的想法：<br /><br />在UCS 编码中有一个叫做&amp;quot;ZERO WIDTH NO-BREAK SPACE&amp;quot;的字符，它的编码是FEFF。而FFFE在UCS中是不存在的字符，所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前，先传输字符&amp;quot;ZERO WIDTH NO-BREAK SPACE&amp;quot;。<br /><br />这样如果接收者收到FEFF，就表明这个字节流是Big-Endian的；如果收到FFFE，就表明这个字节流是Little-Endian的。因此字符&amp;quot;ZERO WIDTH NO-BREAK SPACE&amp;quot;又被称作BOM。<br /><br />UTF -8不需要BOM来表明字节顺序，但可以用BOM来表明编码方式。字符&amp;quot;ZERO WIDTH NO-BREAK SPACE&amp;quot;的UTF-8编码是EF BB BF（读者可以用我们前面介绍的编码方法验证一下）。所以如果接收者收到以EF BB BF开头的字节流，就知道这是UTF-8编码了。<br /><br />Windows就是使用BOM来标记文本文件的编码方式的。 </p><p><br /><strong>6、进一步的参考资料<br /></strong><br />本文主要参考的资料是 &amp;quot;<a href="http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html">Short overview of ISO-IEC 10646 and Unicode</a>&amp;quot; 。<br /><br />我还找了两篇看上去不错的资料，不过因为我开始的疑问都找到了答案，所以就没有看：<br /><br />&amp;nbsp;&amp;nbsp; 1. &amp;quot;<a href="http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-Chapter04a">Understanding Unicode A general introduction to the Unicode Standard</a>&amp;quot;&amp;nbsp; </p><p>&amp;nbsp;&amp;nbsp; 2. &amp;quot;<a href="http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-Chapter03">Character set encoding basics Understanding character set encodings and l<br />egacy encodings</a>&amp;quot; <br /><br />我写过UTF-8、UCS-2、GBK相互转换的软件包，包括使用Windows API和不使用Windows API的版本。以后有时间的话，我会整理一下放到我的个人主页上(<a href="http://fmddlmyy.home4u.ch/" target="_blank"><u><font color="#0000ff"></font></u></a><u><font color="#0000ff"><a href="http://fmddlmyy.home4u.china.com/">http://fmddlmyy.home4u.ch</a></font></u>ina.com)。<br /><br />我是想清楚所有问题后才开始写这篇文章的，原以为一会儿就能写好。没想到考虑措辞和查证细节花费了很长时间，竟然从下午1:30写到9:00。希望有读者能从中受益。 </p><p><br /><strong>附录1 再说说区位码、GB2312、内码和代码页<br /></strong><br />有的朋友对文章中这句话还有疑问：“GB2312的原文还是区位码，从区位码到内码，需要在高字节和低字节上分别加上A0。”<br /><br />我再详细解释一下：<br /><br />“GB2312 的原文”是指国家1980年的一个标准《中华人民共和国国家标准 信息交换用汉字编码字符集 基本集 GB 2312-80》。这个标准用两个数来编码汉字和中文符号。第一个数称为“区”，第二个数称为“位”。所以也称为区位码。1-9区是中文符号，16-55 区是一级汉字，56-87区是二级汉字。现在Windows也还有区位输入法，例如输入1601得到“啊”。（这个区位输入法可以自动识别16进制的 GB2312和10进制的区位码，也就是说输入B0A1同样会得到“啊”。）<br /><br />内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的。现在的Windows在系统内部支持Unicode，然后用代码页适应各种语言，“内码”的概念就比较模糊了。微软一般将缺省代码页指定的编码说成是内码。<br /><br />内码这个词汇，并没有什么官方的定义，代码页也只是微软这个公司的叫法。作为程序员，我们只要知道它们是什么东西，没有必要过多地考证这些名词。<br /><br />所谓代码页(code page)就是针对一种语言文字的字符编码。例如GBK的code page是CP936，BIG5的code page是CP950，GB2312的code page是CP20936。<br /><br />Windows中有缺省代码页的概念，即缺省用什么编码来解释字符。例如Windows的记事本打开了一个文本文件，里面的内容是字节流：BA、BA、D7、D6。Windows应该去怎么解释它呢？<br /><br />是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释，还是按照ISO8859-1去解释？如果按GBK去解释，就会得到“汉字”两个字。按照其它编码解释，可能找不到对应的字符，也可能找到错误的字符。所谓“错误”是指与文本作者的本意不符，这时就产生了乱码。<br /><br />答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控制面板的区域选项设置。记事本的另存为中有一项ANSI，其实就是按照缺省代码页的编码方法保存。<br /><br />Windows的内码是Unicode，它在技术上可以同时支持多个代码页。只要文件能说明自己使用什么编码，用户又安装了对应的代码页，Windows就能正确显示，例如在HTML文件中就可以指定charset。<br /><br />有的HTML文件作者，特别是英文作者，认为世界上所有人都使用英文，在文件中不指定charset。如果他使用了0x80-0xff之间的字符，中文 Windows又按照缺省的GBK去解释，就会出现乱码。这时只要在这个html文件中加上指定charset的语句，例如：&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=ISO8859-1&amp;quot;&amp;gt;如果原作者使用的代码页和ISO8859-1兼容，就不会出现乱码了。<br /><br />再说区位码，啊的区位码是1601，写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的ASCII 编码，我们在区位码的高、低字节上分别加上A0。这样“啊”的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码，虽然GB2312的原文根本没提到这一点。 </p></span>]]></description> 
<guid isPermaLink="false">6078852@http://http88.bokee.com/</guid> 
<dc:subject>项目开发经验</dc:subject> 
<dc:date>2007-01-30T17:50:37Z</dc:date> 
</item> 
<item> 
<title><![CDATA[比较常用的Oracle的SQL语句语法]]></title> 
<link>http://http88.bokee.com/6076265.html</link> 
<description><![CDATA[<div style="FONT-SIZE: 12px; LINE-HEIGHT: 160%">一.数据控制语句 (DML) 部分<br /><br />1.INSERT&amp;nbsp;&amp;nbsp;(往数据表里插入记录的语句)<br /><br />INSERT INTO 表名(字段名1, 字段名2, ……) VALUES ( 值1, 值2, ……); <br />INSERT INTO 表名(字段名1, 字段名2, ……)&amp;nbsp;&amp;nbsp;SELECT 字段名1, 字段名2, …… FROM 另外的表名;<br /><br />字符串类型的字段值必须用单引号括起来, 例如: ’GOOD DAY’<br />如果字段值里包含单引号’ 需要进行字符串转换, 我们把它替换成两个单引号''. <br />字符串类型的字段值超过定义的长度会出错, 最好在插入前进行长度校验.<br /><br />日期字段的字段值可以用当前数据库的系统时间SYSDATE, 精确到秒<br />或者用字符串转换成日期型函数TO_DATE(‘2001-08-01’,’YYYY-MM-DD’)<br />TO_DATE()还有很多种日期格式, 可以参看ORACLE DOC. <br />年-月-日 小时:分钟:秒 的格式YYYY-MM-DD HH24:MI:SS<br /><br />INSERT时最大可操作的字符串长度小于等于4000个单字节, 如果要插入更长的字符串, 请考虑字段用CLOB类型,<br />方法借用ORACLE里自带的DBMS_LOB程序包.<br /><br />INSERT时如果要用到从1开始自动增长的序列号, 应该先建立一个序列号<br />CREATE SEQUENCE 序列号的名称 (最好是表名+序列号标记) INCREMENT BY 1&amp;nbsp;&amp;nbsp;START&amp;nbsp;&amp;nbsp;WITH&amp;nbsp;&amp;nbsp;1 <br />MAXVALUE&amp;nbsp;&amp;nbsp;99999&amp;nbsp;&amp;nbsp;CYCLE&amp;nbsp;&amp;nbsp;NOCACHE;<br />其中最大的值按字段的长度来定, 如果定义的自动增长的序列号 NUMBER(6) , 最大值为999999<br />INSERT 语句插入这个字段值为: 序列号的名称.NEXTVAL<br /><br />2.DELETE&amp;nbsp;&amp;nbsp;(删除数据表里记录的语句)<br /><br />DELETE FROM表名 WHERE 条件;<br /><br />注意：删除记录并不能释放ORACLE里被占用的数据块表空间. 它只把那些被删除的数据块标成unused.<br /><br />如果确实要删除一个大表里的全部记录, 可以用 TRUNCATE 命令, 它可以释放占用的数据块表空间<br />TRUNCATE TABLE 表名; <br />此操作不可回退.<br /><br />3.UPDATE&amp;nbsp;&amp;nbsp;(修改数据表里记录的语句)<br /><br />UPDATE表名 SET 字段名1=值1, 字段名2=值2, …… WHERE 条件;<br /><br />如果修改的值N没有赋值或定义时, 将把原来的记录内容清为NULL, 最好在修改前进行非空校验; <br />值N超过定义的长度会出错, 最好在插入前进行长度校验..<br /><br />注意事项: <br />A.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 以上SQL语句对表都加上了行级锁,<br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 确认完成后, 必须加上事物处理结束的命令 COMMIT 才能正式生效, <br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 否则改变不一定写入数据库里.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; <br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 如果想撤回这些操作, 可以用命令 ROLLBACK 复原.<br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; <br />B.&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 在运行INSERT, DELETE 和 UPDATE 语句前最好估算一下可能操作的记录范围, <br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 应该把它限定在较小 (一万条记录) 范围内,. 否则ORACLE处理这个事物用到很大的回退段. <br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 程序响应慢甚至失去响应. 如果记录数上十万以上这些操作, 可以把这些SQL语句分段分次完成, <br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 其间加上COMMIT 确认事物处理.<br /><br />二.数据定义 (DDL) 部分<br /><br />1.CREATE (创建表, 索引, 视图, 同义词, 过程, 函数, 数据库链接等)<br /><br />ORACLE常用的字段类型有<br />CHAR&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 固定长度的字符串<br />VARCHAR2&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 可变长度的字符串<br />NUMBER(M,N)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 数字型M是位数总长度, N是小数的长度<br />DATE&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 日期类型<br /><br />创建表时要把较小的不为空的字段放在前面, 可能为空的字段放在后面<br /><br />创建表时可以用中文的字段名, 但最好还是用英文的字段名<br /><br />创建表时可以给字段加上默认值, 例如 DEFAULT SYSDATE<br />这样每次插入和修改时, 不用程序操作这个字段都能得到动作的时间<br /><br />创建表时可以给字段加上约束条件<br />例如 不允许重复 UNIQUE, 关键字 PRIMARY KEY<br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; <br />2.ALTER&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (改变表, 索引, 视图等)<br /><br />改变表的名称<br />ALTER TABLE 表名1&amp;nbsp;&amp;nbsp;TO 表名2;<br /><br />在表的后面增加一个字段<br />ALTER TABLE表名 ADD 字段名 字段名描述;<br /><br />修改表里字段的定义描述<br />ALTER TABLE表名 MODIFY字段名 字段名描述;<br /><br />给表里的字段加上约束条件<br />ALTER TABLE 表名 ADD CONSTRAINT 约束名 PRIMARY KEY (字段名);<br />ALTER TABLE 表名 ADD CONSTRAINT 约束名 UNIQUE (字段名);<br /><br />把表放在或取出数据库的内存区<br />ALTER TABLE 表名 CACHE;<br />ALTER TABLE 表名 NOCACHE;<br /><br />3.DROP&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (删除表, 索引, 视图, 同义词, 过程, 函数, 数据库链接等)<br /><br />删除表和它所有的约束条件<br />DROP TABLE 表名 CASCADE CONSTRAINTS;<br /><br />4.TRUNCATE (清空表里的所有记录, 保留表的结构)<br /><br />TRUNCATE 表名;<br /><br />三.查询语句 (SELECT) 部分<br /><br />SELECT字段名1, 字段名2, …… FROM 表名1, [表名2, ……] WHERE 条件; <br /><br />字段名可以带入函数<br />&amp;nbsp;&amp;nbsp;例如:&amp;nbsp;&amp;nbsp;COUNT(*), MIN(字段名),&amp;nbsp;&amp;nbsp;MAX(字段名),&amp;nbsp;&amp;nbsp;AVG(字段名), DISTINCT(字段名), <br />&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;TO_CHAR(DATE字段名,'YYYY-MM-DD HH24:MI:SS')<br /><br />NVL(EXPR1, EXPR2)函数<br />解释:&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; <br />IF EXPR1=NULL<br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; RETURN EXPR2<br />ELSE<br />&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; RETURN EXPR1<br /><br />DECODE(AA﹐V1﹐R1﹐V2﹐R2....)函数<br />解释: <br />IF AA=V1 THEN RETURN R1<br />IF AA=V2 THEN RETURN R2<br />..…<br />ELSE<br />RETURN NULL<br /><br />LPAD(char1,n,char2)函数<br />解释:<br />字符char1按制定的位数n显示，不足的位数用char2字符串替换左边的空位<br /><br />字段名之间可以进行算术运算<br />例如:&amp;nbsp;&amp;nbsp;(字段名1*字段名1)/3<br /><br />查询语句可以嵌套<br />例如: SELECT …… FROM <br />(SELECT …… FROM表名1, [表名2, ……] WHERE 条件) WHERE 条件2;<br /><br />两个查询语句的结果可以做集合操作<br />例如: 并集UNION(去掉重复记录), 并集UNION ALL(不去掉重复记录), 差集MINUS,&amp;nbsp;&amp;nbsp;交集INTERSECT<br /><br />分组查询<br />SELECT字段名1, 字段名2, …… FROM 表名1, [表名2, ……] GROUP BY字段名1 <br />[HAVING 条件] ;<br /><br />两个以上表之间的连接查询<br /><br />SELECT字段名1, 字段名2, …… FROM 表名1, [表名2, ……] WHERE <br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 表名1.字段名 = 表名2. 字段名 [ AND ……] ;<br /><br />SELECT字段名1, 字段名2, …… FROM 表名1, [表名2, ……] WHERE <br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 表名1.字段名 = 表名2. 字段名(+) [ AND ……] ;<br /><br />有(+)号的字段位置自动补空值<br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; <br />查询结果集的排序操作, 默认的排序是升序ASC, 降序是DESC<br /><br />SELECT字段名1, 字段名2, …… FROM 表名1, [表名2, ……] <br />ORDER BY字段名1, 字段名2 DESC;<br /><br />字符串模糊比较的方法<br /><br />INSTR(字段名, ‘字符串’)&amp;gt;0&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; <br />字段名 LIKE&amp;nbsp;&amp;nbsp;‘字符串%’&amp;nbsp;&amp;nbsp;[‘%字符串%’]<br /><br />每个表都有一个隐含的字段ROWID, 它标记着记录的唯一性. <br /><br />四.ORACLE里常用的数据对象 (SCHEMA)<br /><br />1.索引 (INDEX)<br /><br />CREATE INDEX 索引名ON 表名 ( 字段1, [字段2, ……] );<br />ALTER INDEX 索引名 REBUILD;<br /><br />一个表的索引最好不要超过三个 (特殊的大表除外), 最好用单字段索引, 结合SQL语句的分析执行情况, <br />也可以建立多字段的组合索引和基于函数的索引<br /><br />ORACLE8.1.7字符串可以索引的最大长度为1578 单字节<br />ORACLE8.0.6字符串可以索引的最大长度为758 单字节<br /><br />ORACLE DOC上说字符串最大可以建索引的长度约是:数据块的大小(db_block_size)*40%<br /><br />2.视图 (VIEW)<br /><br />CREATE VIEW 视图名AS SELECT …. FROM …..;<br />ALTER VIEW视图名 COMPILE;<br /><br />视图仅是一个SQL查询语句, 它可以把表之间复杂的关系简洁化.<br /><br />3.同义词 (SYNONMY)<br />CREATE SYNONYM同义词名FOR 表名;<br />CREATE SYNONYM同义词名FOR 表名@数据库链接名;<br /><br />4.数据库链接 (DATABASE LINK)<br />CREATE DATABASE LINK数据库链接名CONNECT TO 用户名 IDENTIFIED BY 密码 USING ‘数据库连接字符串’;<br />&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; <br />数据库连接字符串可以用NET8 EASY CONFIG或者直接修改TNSNAMES.ORA里定义.<br /><br />数据库参数global_name=true时要求数据库链接名称跟远端数据库名称一样<br /><br />数据库全局名称可以用以下命令查出<br />SELECT * FROM GLOBAL_NAME;<br /><br />查询远端数据库里的表<br />SELECT …… FROM 表名@数据库链接名;<br /><br />五.权限管理 (DCL) 语句<br /><br />1.GRANT&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 赋于权限<br />常用的系统权限集合有以下三个:<br />CONNECT(基本的连接), RESOURCE(程序开发), DBA(数据库管理)<br />常用的数据对象权限有以下五个:<br />ALL &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ON 数据对象名, &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SELECT ON 数据对象名, &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; UPDATE ON 数据对象名,<br />DELETE &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ON 数据对象名,&amp;nbsp;&amp;nbsp;INSERT ON 数据对象名,&amp;nbsp; &amp;nbsp;ALTER&amp;nbsp;&amp;nbsp;ON 数据对象名<br /><br />GRANT CONNECT, RESOURCE TO 用户名;<br />GRANT SELECT ON 表名 TO 用户名;<br />GRANT SELECT, INSERT, DELETE ON表名 TO 用户名1, 用户名2;<br /><br />2.REVOKE 回收权限<br /><br />REVOKE CONNECT, RESOURCE FROM 用户名;<br />REVOKE SELECT ON 表名 FROM 用户名;<br />REVOKE SELECT, INSERT, DELETE ON表名 FROM 用户名1, 用户名2;&amp;nbsp;</div><div style="FONT-SIZE: 12px; LINE-HEIGHT: 160%"><div style="FONT-SIZE: 12px; LINE-HEIGHT: 160%">以下是MS SQL<br /><br />SQL分类：<br />DDL—数据定义语言(CREATE，ALTER，DROP，DECLARE)<br />DML—数据操纵语言(SELECT，DELETE，UPDATE，INSERT)<br />DCL—数据控制语言(GRANT，REVOKE，COMMIT，ROLLBACK)<br /><br />首先,简要介绍基础语句：<br />1、说明：创建数据库<br />CREATE DATABASE database-name<br />2、说明：删除数据库<br />drop database dbname<br />3、说明：备份sql server<br />--- 创建 备份数据的 device<br />USE master<br />EXEC sp_addumpdevice 'disk', 'testBack', <br /><br />'c:\mssql7backup\MyNwind_1.dat'<br />--- 开始 备份<br />BACKUP DATABASE pubs TO testBack<br />4、说明：创建新表<br />create table tabname(col1 type1 [not null] [primary key],col2 <br /><br />type2 [not null],..)<br />根据已有的表创建新表：<br />A：create table tab_new like tab_old (使用旧表创建新表)<br />B：create table tab_new as select col1,col2… from tab_old <br /><br />definition only<br />5、说明：删除新表drop table tabname<br />6、说明：增加一个列<br />Alter table tabname add column col type<br />注：列增加后将不能删除。DB2中列加上后数据类型也不能改变，唯一能改<br /><br />变的是增加varchar类型的长度。<br />7、说明：添加主键： Alter table tabname add primary key(col)<br />说明：删除主键： Alter table tabname drop primary key(col)<br />8、说明：创建索引：create [unique] index idxname on tabname(col<br /><br />….)<br />删除索引：drop index idxname<br />注：索引是不可更改的，想更改必须删除重新建。<br />9、说明：创建视图：create view viewname as select statement<br />删除视图：drop view viewname<br />10、说明：几个简单的基本的sql语句<br />选择：select * from table1 where 范围<br />插入：insert into table1(field1,field2) values(value1,value2)<br />删除：delete from table1 where 范围<br />更新：update table1 set field1=value1 where 范围<br />查找：select * from table1 where field1 like ’%value1%’ --<br /><br />-like的语法很精妙，查资料!<br />排序：select * from table1 order by field1,field2 [desc]<br />总数：select count * as totalcount from table1<br />求和：select sum(field1) as sumvalue from table1<br />平均：select avg(field1) as avgvalue from table1<br />最大：select max(field1) as maxvalue from table1<br />最小：select min(field1) as minvalue from table1<br />11、说明：几个高级查询运算词<br />A： UNION 运算符<br />UNION 运算符通过组合其他两个结果表（例如 TABLE1 和 TABLE2）并消去<br /><br />表中任何重复行而派生出一个结果表。当 ALL 随 UNION 一起使用时（即 <br /><br />UNION ALL），不消除重复行。两种情况下，派生表的每一行不是来自 <br /><br />TABLE1 就是来自 TABLE2。<br />B： EXCEPT 运算符<br />EXCEPT 运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所<br /><br />有重复行而派生出一个结果表。当 ALL 随 EXCEPT 一起使用时 (EXCEPT <br /><br />ALL)，不消除重复行。<br />C： INTERSECT 运算符<br />INTERSECT 运算符通过只包括 TABLE1 和 TABLE2 中都有的行并消除所有<br /><br />重复行而派生出一个结果表。当 ALL 随 INTERSECT 一起使用时 <br /><br />(INTERSECT ALL)，不消除重复行。<br />注：使用运算词的几个查询结果行必须是一致的。<br />12、说明：使用外连接<br />A、left outer join：<br />左外连接（左连接）：结果集几包括连接表的匹配行，也包括左连接表的所<br /><br />有行。<br />SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN <br /><br />b ON a.a = b.c<br />B：right outer join:<br />右外连接(右连接)：结果集既包括连接表的匹配连接行，也包括右连接表的<br /><br />所有行。<br />C：full outer join：<br />全外连接：不仅包括符号连接表的匹配行，还包括两个连接表中的所有记录<br /><br />。<br /><br />其次，大家来看一些不错的sql语句<br />1、说明：复制表(只复制结构,源表名：a 新表名：b) (Access可用)<br />法一：select * into b from a where 1&amp;lt;&amp;gt;1<br />法二：select top 0 * into b from a<br /><br />2、说明：拷贝表(拷贝数据,源表名：a 目标表名：b) (Access可用)<br />insert into b(a, b, c) select d,e,f from b;<br /><br />3、说明：跨数据库之间表的拷贝(具体数据使用绝对路径) (Access可用)<br />insert into b(a, b, c) select d,e,f from b in ‘具体数据库’ <br /><br />where 条件<br />例子：..from b in '&amp;quot;&amp;amp;Server.MapPath(&amp;quot;.&amp;quot;)&amp;amp;&amp;quot;\data.mdb&amp;quot; &amp;amp;&amp;quot;' <br /><br />where..<br /><br />4、说明：子查询(表名1：a 表名2：b)<br />select a,b,c from a where a IN (select d from b ) 或者: <br /><br />select a,b,c from a where a IN (1,2,3)<br /><br />5、说明：显示文章、提交人和最后回复时间<br />select a.title,a.username,b.adddate from table a,(select <br /><br />max(adddate) adddate from table where table.title=a.title) b<br /><br />6、说明：外连接查询(表名1：a 表名2：b)<br />select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON <br /><br />a.a = b.c<br /><br />7、说明：在线视图查询(表名1：a )<br />select * from (SELECT a,b,c FROM a) T where t.a &amp;gt; 1;<br /><br />8、说明：between的用法,between限制查询数据范围时包括了边界值,not <br /><br />between不包括<br />select * from table1 where time between time1 and time2<br />select a,b,c, from table1 where a not between 数值1 and 数值2<br /><br />9、说明：in 的使用方法<br />select * from table1 where a [not] in (‘值1’,’值2’,’值4’,<br /><br />’值6’)<br /><br />10、说明：两张关联表，删除主表中已经在副表中没有的信息<br />delete from table1 where not exists ( select * from table2 <br /><br />where table1.field1=table2.field1 )<br /><br />11、说明：四表联查问题：<br />select * from a left inner join b on a.a=b.b right inner join <br /><br />c on a.a=c.c inner join d on a.a=d.d where .....<br /><br />12、说明：日程安排提前五分钟提醒<br />SQL: select * from 日程安排 where datediff('minute',f开始时<br /><br />间,getdate())&amp;gt;5<br /><br />13、说明：一条sql 语句搞定数据库分页<br />select top 10 b.* from (select top 20 主键字段,排序字段 from 表<br /><br />名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字<br /><br />段 order by a.排序字段<br /><br />14、说明：前10条记录<br />select top 10 * form table1 where 范围<br /><br />15、说明：选择在每一组b值相同的数据中对应的a最大的记录的所有信息(<br /><br />类似这样的用法可以用于论坛每月排行榜,每月热销产品分析,按科目成绩排<br /><br />名,等等.)<br />select a,b,c from tablename ta where a=(select max(a) from <br /><br />tablename tb where tb.b=ta.b)<br /><br />16、说明：包括所有在 TableA 中但不在 TableB和TableC 中的行并消除<br /><br />所有重复行而派生出一个结果表<br />(select a from tableA ) except (select a from tableB) except <br /><br />(select a from tableC)<br /><br />17、说明：随机取出10条数据<br />select top 10 * from tablename order by newid()<br /><br />18、说明：随机选择记录<br />select newid()<br /><br />19、说明：删除重复记录<br />Delete from tablename where id not in (select max(id) from <br /><br />tablename group by col1,col2,...)<br /><br />20、说明：列出数据库里所有的表名<br />select name from sysobjects where type='U'<br /><br />21、说明：列出表里的所有的<br />select name from syscolumns where id=object_id('TableName')<br /><br />22、说明：列示type、vender、pcs字段，以type字段排列，case可以方便<br /><br />地实现多重选择，类似select 中的case。<br />select type,sum(case vender when 'A' then pcs else 0 <br /><br />end),sum(case vender when 'C' then pcs else 0 end),sum(case <br /><br />vender when 'B' then pcs else 0 end) FROM tablename group by <br /><br />type<br />显示结果：<br />type vender pcs<br />电脑 A 1<br />电脑 A 1<br />光盘 B 2<br />光盘 A 2<br />手机 B 3<br />手机 C 3<br /><br />23、说明：初始化表table1<br />TRUNCATE TABLE table1<br /><br />24、说明：选择从10到15的记录<br />select top 5 * from (select top 15 * from table order by id <br /><br />asc) table_别名 order by id desc<br />　　<br />随机选择数据库记录的方法（使用Randomize函数，通过SQL语句实现）<br />　　对存储在数据库中的数据来说，随机数特性能给出上面的效果，但它们<br /><br />可能太慢了些。你不能要求ASP“找个随机数”然后打印出来。实际上常见<br /><br />的解决方案是建立如下所示的循环：<br />Randomize<br />RNumber = Int(Rnd*499) +1<br />　<br />While Not objRec.EOF<br />If objRec(&amp;quot;ID&amp;quot;) = RNumber THEN<br />... 这里是执行脚本 ...<br />end if<br />objRec.MoveNext<br />Wend<br />　<br />　　这很容易理解。首先，你取出1到500范围之内的一个随机数（假设500<br /><br />就是数据库内记录的总数）。然后，你遍历每一记录来测试ID 的值、检查<br /><br />其是否匹配RNumber。满足条件的话就执行由THEN 关键字开始的那一块代码<br /><br />。假如你的RNumber 等于495，那么要循环一遍数据库花的时间可就长了。<br /><br />虽然500这个数字看起来大了些，但相比更为稳固的企业解决方案这还是个<br /><br />小型数据库了，后者通常在一个数据库内就包含了成千上万条记录。这时候<br /><br />不就死定了？<br />　　采用SQL，你就可以很快地找出准确的记录并且打开一个只包含该记录<br /><br />的recordset，如下所示：<br />Randomize<br />RNumber = Int(Rnd*499) + 1<br />　<br />SQL = &amp;quot;SELECT * FROM Customers WHERE ID = &amp;quot; &amp;amp; RNumber<br />　<br />set objRec = ObjConn.Execute(SQL)<br />Response.WriteRNumber &amp;amp; &amp;quot; = &amp;quot; &amp;amp; objRec(&amp;quot;ID&amp;quot;) &amp;amp; &amp;quot; &amp;quot; &amp;amp; <br /><br />objRec(&amp;quot;c_email&amp;quot;)<br />　<br />　　不必写出RNumber 和ID，你只需要检查匹配情况即可。只要你对以上代<br /><br />码的工作满意，你自可按需操作“随机”记录。Recordset没有包含其他内<br /><br />容，因此你很快就能找到你需要的记录这样就大大降低了处理时间。<br />再谈随机数<br />　　现在你下定决心要榨干Random 函数的最后一滴油，那么你可能会一次<br /><br />取出多条随机记录或者想采用一定随机范围内的记录。把上面的标准Random <br /><br />示例扩展一下就可以用SQL应对上面两种情况了。<br />　　为了取出几条随机选择的记录并存放在同一recordset内，你可以存储<br /><br />三个随机数，然后查询数据库获得匹配这些数字的记录：<br />SQL = &amp;quot;SELECT * FROM Customers WHERE ID = &amp;quot; &amp;amp; RNumber &amp;amp; &amp;quot; OR <br /><br />ID = &amp;quot; &amp;amp; RNumber2 &amp;amp; &amp;quot; OR ID = &amp;quot; &amp;amp; RNumber3<br />　<br />　　假如你想选出10条记录（也许是每次页面装载时的10条链接的列表），<br /><br />你可以用BETWEEN 或者数学等式选出第一条记录和适当数量的递增记录。这<br /><br />一操作可以通过好几种方式来完成，但是 SELECT 语句只显示一种可能（这<br /><br />里的ID 是自动生成的号码）：<br />SQL = &amp;quot;SELECT * FROM Customers WHERE ID BETWEEN &amp;quot; &amp;amp; RNumber &amp;amp; <br /><br />&amp;quot; AND &amp;quot; &amp;amp; RNumber &amp;amp; &amp;quot;+ 9&amp;quot;<br /><br />　　注意：以上代码的执行目的不是检查数据库内是否有9条并发记录。<br /><br />　<br />随机读取若干条记录，测试过<br />Access语法：SELECT top 10 * From 表名 ORDER BY Rnd(id)<br />Sql server:select top n * from 表名 order by newid()<br />mysqlelect * From 表名 Order By rand() Limit n<br />Access左连接语法(最近开发要用左连接,Access帮助什么都没有,网上没有<br /><br />Access的SQL说明,只有自己测试, 现在记下以备后查)<br />语法elect table1.fd1,table1,fd2,table2.fd2 From table1 left <br /><br />join table2 on table1.fd1,table2.fd1 where ...<br />使用SQL语句 用...代替过长的字符串显示<br />语法：<br />SQL数据库：select case when len(field)&amp;gt;10 then left(field,10)<br /><br />+'...' else field end as news_name,news_id from tablename<br />Access数据库：SELECT iif(len(field)&amp;gt;2,left(field,2)<br /><br />+'...',field) FROM tablename;<br />　<br />Conn.Execute说明<br />Execute方法<br />　　该方法用于执行SQL语句。根据SQL语句执行后是否返回记录集，该方法<br /><br />的使用格式分为以下两种：<br />　　　　1．执行SQL查询语句时，将返回查询得到的记录集。用法为：<br />　　　　Set 对象变量名=连接对象.Execute(&amp;quot;SQL 查询语言&amp;quot;)<br />　　　Execute方法调用后，会自动创建记录集对象，并将查询结果存储在<br /><br />该记录对象中，通过Set方法，将记录集赋给指定的对象保存，以后对象变<br /><br />量就代表了该记录集对象。<br /><br />　　　　2．执行SQL的操作性语言时，没有记录集的返回。此时用法为：<br />　　　　连接对象.Execute &amp;quot;SQL 操作性语句&amp;quot; [, RecordAffected][, <br /><br />Option]<br />　　　　　　·RecordAffected 为可选项，此出可放置一个变量，SQL语<br /><br />句执行后，所生效的记录数会自动保存到该变量中。通过访问该变量，就可<br /><br />知道SQL语句队多少条记录进行了操作。<br />　　　　　　·Option 可选项，该参数的取值通常为adCMDText，它用于<br /><br />告诉ADO，应该将Execute方法之后的第一个字符解释为命令文本。通过指定<br /><br />该参数，可使执行更高效。<br /><br />·BeginTrans、RollbackTrans、CommitTrans方法<br />　　这三个方法是连接对象提供的用于事务处理的方法。BeginTrans用于开<br /><br />始一个事物；RollbackTrans用于回滚事务；CommitTrans用于提交所有的<br /><br />事务处理结果，即确认事务的处理。<br />　　事务处理可以将一组操作视为一个整体，只有全部语句都成功执行后，<br /><br />事务处理才算成功；若其中有一个语句执行失败，则整个处理就算失败，并<br /><br />恢复到处里前的状态。<br />　　BeginTrans和CommitTrans用于标记事务的开始和结束，在这两个之间<br /><br />的语句，就是作为事务处理的语句。判断事务处理是否成功，可通过连接对<br /><br />象的Error集合来实现，若Error集合的成员个数不为0，则说明有错误发生<br /><br />，事务处理失败。Error集合中的每一个Error对象，代表一个错误信息。</div><div style="FONT-SIZE: 12px; LINE-HEIGHT: 160%"><div style="FONT-SIZE: 12px; LINE-HEIGHT: 160%">以下是MS SQL<br /><br />SQL分类：<br />DDL—数据定义语言(CREATE，ALTER，DROP，DECLARE)<br />DML—数据操纵语言(SELECT，DELETE，UPDATE，INSERT)<br />DCL—数据控制语言(GRANT，REVOKE，COMMIT，ROLLBACK)<br /><br />首先,简要介绍基础语句：<br />1、说明：创建数据库<br />CREATE DATABASE database-name<br />2、说明：删除数据库<br />drop database dbname<br />3、说明：备份sql server<br />--- 创建 备份数据的 device<br />USE master<br />EXEC sp_addumpdevice 'disk', 'testBack', <br /><br />'c:\mssql7backup\MyNwind_1.dat'<br />--- 开始 备份<br />BACKUP DATABASE pubs TO testBack<br />4、说明：创建新表<br />create table tabname(col1 type1 [not null] [primary key],col2 <br /><br />type2 [not null],..)<br />根据已有的表创建新表：<br />A：create table tab_new like tab_old (使用旧表创建新表)<br />B：create table tab_new as select col1,col2… from tab_old <br /><br />definition only<br />5、说明：删除新表drop table tabname<br />6、说明：增加一个列<br />Alter table tabname add column col type<br />注：列增加后将不能删除。DB2中列加上后数据类型也不能改变，唯一能改<br /><br />变的是增加varchar类型的长度。<br />7、说明：添加主键： Alter table tabname add primary key(col)<br />说明：删除主键： Alter table tabname drop primary key(col)<br />8、说明：创建索引：create [unique] index idxname on tabname(col<br /><br />….)<br />删除索引：drop index idxname<br />注：索引是不可更改的，想更改必须删除重新建。<br />9、说明：创建视图：create view viewname as select statement<br />删除视图：drop view viewname<br />10、说明：几个简单的基本的sql语句<br />选择：select * from table1 where 范围<br />插入：insert into table1(field1,field2) values(value1,value2)<br />删除：delete from table1 where 范围<br />更新：update table1 set field1=value1 where 范围<br />查找：select * from table1 where field1 like ’%value1%’ --<br /><br />-like的语法很精妙，查资料!<br />排序：select * from table1 order by field1,field2 [desc]<br />总数：select count * as totalcount from table1<br />求和：select sum(field1) as sumvalue from table1<br />平均：select avg(field1) as avgvalue from table1<br />最大：select max(field1) as maxvalue from table1<br />最小：select min(field1) as minvalue from table1<br />11、说明：几个高级查询运算词<br />A： UNION 运算符<br />UNION 运算符通过组合其他两个结果表（例如 TABLE1 和 TABLE2）并消去<br /><br />表中任何重复行而派生出一个结果表。当 ALL 随 UNION 一起使用时（即 <br /><br />UNION ALL），不消除重复行。两种情况下，派生表的每一行不是来自 <br /><br />TABLE1 就是来自 TABLE2。<br />B： EXCEPT 运算符<br />EXCEPT 运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所<br /><br />有重复行而派生出一个结果表。当 ALL 随 EXCEPT 一起使用时 (EXCEPT <br /><br />ALL)，不消除重复行。<br />C： INTERSECT 运算符<br />INTERSECT 运算符通过只包括 TABLE1 和 TABLE2 中都有的行并消除所有<br /><br />重复行而派生出一个结果表。当 ALL 随 INTERSECT 一起使用时 <br /><br />(INTERSECT ALL)，不消除重复行。<br />注：使用运算词的几个查询结果行必须是一致的。<br />12、说明：使用外连接<br />A、left outer join：<br />左外连接（左连接）：结果集几包括连接表的匹配行，也包括左连接表的所<br /><br />有行。<br />SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN <br /><br />b ON a.a = b.c<br />B：right outer join:<br />右外连接(右连接)：结果集既包括连接表的匹配连接行，也包括右连接表的<br /><br />所有行。<br />C：full outer join：<br />全外连接：不仅包括符号连接表的匹配行，还包括两个连接表中的所有记录<br /><br />。<br /><br />其次，大家来看一些不错的sql语句<br />1、说明：复制表(只复制结构,源表名：a 新表名：b) (Access可用)<br />法一：select * into b from a where 1&amp;lt;&amp;gt;1<br />法二：select top 0 * into b from a<br /><br />2、说明：拷贝表(拷贝数据,源表名：a 目标表名：b) (Access可用)<br />insert into b(a, b, c) select d,e,f from b;<br /><br />3、说明：跨数据库之间表的拷贝(具体数据使用绝对路径) (Access可用)<br />insert into b(a, b, c) select d,e,f from b in ‘具体数据库’ <br /><br />where 条件<br />例子：..from b in '&amp;quot;&amp;amp;Server.MapPath(&amp;quot;.&amp;quot;)&amp;amp;&amp;quot;\data.mdb&amp;quot; &amp;amp;&amp;quot;' <br /><br />where..<br /><br />4、说明：子查询(表名1：a 表名2：b)<br />select a,b,c from a where a IN (select d from b ) 或者: <br /><br />select a,b,c from a where a IN (1,2,3)<br /><br />5、说明：显示文章、提交人和最后回复时间<br />select a.title,a.username,b.adddate from table a,(select <br /><br />max(adddate) adddate from table where table.title=a.title) b<br /><br />6、说明：外连接查询(表名1：a 表名2：b)<br />select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON <br /><br />a.a = b.c<br /><br />7、说明：在线视图查询(表名1：a )<br />select * from (SELECT a,b,c FROM a) T where t.a &amp;gt; 1;<br /><br />8、说明：between的用法,between限制查询数据范围时包括了边界值,not <br /><br />between不包括<br />select * from table1 where time between time1 and time2<br />select a,b,c, from table1 where a not between 数值1 and 数值2<br /><br />9、说明：in 的使用方法<br />select * from table1 where a [not] in (‘值1’,’值2’,’值4’,<br /><br />’值6’)<br /><br />10、说明：两张关联表，删除主表中已经在副表中没有的信息<br />delete from table1 where not exists ( select * from table2 <br /><br />where table1.field1=table2.field1 )<br /><br />11、说明：四表联查问题：<br />select * from a left inner join b on a.a=b.b right inner join <br /><br />c on a.a=c.c inner join d on a.a=d.d where .....<br /><br />12、说明：日程安排提前五分钟提醒<br />SQL: select * from 日程安排 where datediff('minute',f开始时<br /><br />间,getdate())&amp;gt;5<br /><br />13、说明：一条sql 语句搞定数据库分页<br />select top 10 b.* from (select top 20 主键字段,排序字段 from 表<br /><br />名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字<br /><br />段 order by a.排序字段<br /><br />14、说明：前10条记录<br />select top 10 * form table1 where 范围<br /><br />15、说明：选择在每一组b值相同的数据中对应的a最大的记录的所有信息(<br /><br />类似这样的用法可以用于论坛每月排行榜,每月热销产品分析,按科目成绩排<br /><br />名,等等.)<br />select a,b,c from tablename ta where a=(select max(a) from <br /><br />tablename tb where tb.b=ta.b)<br /><br />16、说明：包括所有在 TableA 中但不在 TableB和TableC 中的行并消除<br /><br />所有重复行而派生出一个结果表<br />(select a from tableA ) except (select a from tableB) except <br /><br />(select a from tableC)<br /><br />17、说明：随机取出10条数据<br />select top 10 * from tablename order by newid()<br /><br />18、说明：随机选择记录<br />select newid()<br /><br />19、说明：删除重复记录<br />Delete from tablename where id not in (select max(id) from <br /><br />tablename group by col1,col2,...)<br /><br />20、说明：列出数据库里所有的表名<br />select name from sysobjects where type='U'<br /><br />21、说明：列出表里的所有的<br />select name from syscolumns where id=object_id('TableName')<br /><br />22、说明：列示type、vender、pcs字段，以type字段排列，case可以方便<br /><br />地实现多重选择，类似select 中的case。<br />select type,sum(case vender when 'A' then pcs else 0 <br /><br />end),sum(case vender when 'C' then pcs else 0 end),sum(case <br /><br />vender when 'B' then pcs else 0 end) FROM tablename group by <br /><br />type<br />显示结果：<br />type vender pcs<br />电脑 A 1<br />电脑 A 1<br />光盘 B 2<br />光盘 A 2<br />手机 B 3<br />手机 C 3<br /><br />23、说明：初始化表table1<br />TRUNCATE TABLE table1<br /><br />24、说明：选择从10到15的记录<br />select top 5 * from (select top 15 * from table order by id <br /><br />asc) table_别名 order by id desc<br />　　<br />随机选择数据库记录的方法（使用Randomize函数，通过SQL语句实现）<br />　　对存储在数据库中的数据来说，随机数特性能给出上面的效果，但它们<br /><br />可能太慢了些。你不能要求ASP“找个随机数”然后打印出来。实际上常见<br /><br />的解决方案是建立如下所示的循环：<br />Randomize<br />RNumber = Int(Rnd*499) +1<br />　<br />While Not objRec.EOF<br />If objRec(&amp;quot;ID&amp;quot;) = RNumber THEN<br />... 这里是执行脚本 ...<br />end if<br />objRec.MoveNext<br />Wend<br />　<br />　　这很容易理解。首先，你取出1到500范围之内的一个随机数（假设500<br /><br />就是数据库内记录的总数）。然后，你遍历每一记录来测试ID 的值、检查<br /><br />其是否匹配RNumber。满足条件的话就执行由THEN 关键字开始的那一块代码<br /><br />。假如你的RNumber 等于495，那么要循环一遍数据库花的时间可就长了。<br /><br />虽然500这个数字看起来大了些，但相比更为稳固的企业解决方案这还是个<br /><br />小型数据库了，后者通常在一个数据库内就包含了成千上万条记录。这时候<br /><br />不就死定了？<br />　　采用SQL，你就可以很快地找出准确的记录并且打开一个只包含该记录<br /><br />的recordset，如下所示：<br />Randomize<br />RNumber = Int(Rnd*499) + 1<br />　<br />SQL = &amp;quot;SELECT * FROM Customers WHERE ID = &amp;quot; &amp;amp; RNumber<br />　<br />set objRec = ObjConn.Execute(SQL)<br />Response.WriteRNumber &amp;amp; &amp;quot; = &amp;quot; &amp;amp; objRec(&amp;quot;ID&amp;quot;) &amp;amp; &amp;quot; &amp;quot; &amp;amp; <br /><br />objRec(&amp;quot;c_email&amp;quot;)<br />　<br />　　不必写出RNumber 和ID，你只需要检查匹配情况即可。只要你对以上代<br /><br />码的工作满意，你自可按需操作“随机”记录。Recordset没有包含其他内<br /><br />容，因此你很快就能找到你需要的记录这样就大大降低了处理时间。<br />再谈随机数<br />　　现在你下定决心要榨干Random 函数的最后一滴油，那么你可能会一次<br /><br />取出多条随机记录或者想采用一定随机范围内的记录。把上面的标准Random <br /><br />示例扩展一下就可以用SQL应对上面两种情况了。<br />　　为了取出几条随机选择的记录并存放在同一recordset内，你可以存储<br /><br />三个随机数，然后查询数据库获得匹配这些数字的记录：<br />SQL = &amp;quot;SELECT * FROM Customers WHERE ID = &amp;quot; &amp;amp; RNumber &amp;amp; &amp;quot; OR <br /><br />ID = &amp;quot; &amp;amp; RNumber2 &amp;amp; &amp;quot; OR ID = &amp;quot; &amp;amp; RNumber3<br />　<br />　　假如你想选出10条记录（也许是每次页面装载时的10条链接的列表），<br /><br />你可以用BETWEEN 或者数学等式选出第一条记录和适当数量的递增记录。这<br /><br />一操作可以通过好几种方式来完成，但是 SELECT 语句只显示一种可能（这<br /><br />里的ID 是自动生成的号码）：<br />SQL = &amp;quot;SELECT * FROM Customers WHERE ID BETWEEN &amp;quot; &amp;amp; RNumber &amp;amp; <br /><br />&amp;quot; AND &amp;quot; &amp;amp; RNumber &amp;amp; &amp;quot;+ 9&amp;quot;<br /><br />　　注意：以上代码的执行目的不是检查数据库内是否有9条并发记录。<br /><br />　<br />随机读取若干条记录，测试过<br />Access语法：SELECT top 10 * From 表名 ORDER BY Rnd(id)<br />Sql server:select top n * from 表名 order by newid()<br />mysqlelect * From 表名 Order By rand() Limit n<br />Access左连接语法(最近开发要用左连接,Access帮助什么都没有,网上没有<br /><br />Access的SQL说明,只有自己测试, 现在记下以备后查)<br />语法elect table1.fd1,table1,fd2,table2.fd2 From table1 left <br /><br />join table2 on table1.fd1,table2.fd1 where ...<br />使用SQL语句 用...代替过长的字符串显示<br />语法：<br />SQL数据库：select case when len(field)&amp;gt;10 then left(field,10)<br /><br />+'...' else field end as news_name,news_id from tablename<br />Access数据库：SELECT iif(len(field)&amp;gt;2,left(field,2)<br /><br />+'...',field) FROM tablename;<br />　<br />Conn.Execute说明<br />Execute方法<br />　　该方法用于执行SQL语句。根据SQL语句执行后是否返回记录集，该方法<br /><br />的使用格式分为以下两种：<br />　　　　1．执行SQL查询语句时，将返回查询得到的记录集。用法为：<br />　　　　Set 对象变量名=连接对象.Execute(&amp;quot;SQL 查询语言&amp;quot;)<br />　　　Execute方法调用后，会自动创建记录集对象，并将查询结果存储在<br /><br />该记录对象中，通过Set方法，将记录集赋给指定的对象保存，以后对象变<br /><br />量就代表了该记录集对象。<br /><br />　　　　2．执行SQL的操作性语言时，没有记录集的返回。此时用法为：<br />　　　　连接对象.Execute &amp;quot;SQL 操作性语句&amp;quot; [, RecordAffected][, <br /><br />Option]<br />　　　　　　·RecordAffected 为可选项，此出可放置一个变量，SQL语<br /><br />句执行后，所生效的记录数会自动保存到该变量中。通过访问该变量，就可<br /><br />知道SQL语句队多少条记录进行了操作。<br />　　　　　　·Option 可选项，该参数的取值通常为adCMDText，它用于<br /><br />告诉ADO，应该将Execute方法之后的第一个字符解释为命令文本。通过指定<br /><br />该参数，可使执行更高效。<br /><br />·BeginTrans、RollbackTrans、CommitTrans方法<br />　　这三个方法是连接对象提供的用于事务处理的方法。BeginTrans用于开<br /><br />始一个事物；RollbackTrans用于回滚事务；CommitTrans用于提交所有的<br /><br />事务处理结果，即确认事务的处理。<br />　　事务处理可以将一组操作视为一个整体，只有全部语句都成功执行后，<br /><br />事务处理才算成功；若其中有一个语句执行失败，则整个处理就算失败，并<br /><br />恢复到处里前的状态。<br />　　BeginTrans和CommitTrans用于标记事务的开始和结束，在这两个之间<br /><br />的语句，就是作为事务处理的语句。判断事务处理是否成功，可通过连接对<br /><br />象的Error集合来实现，若Error集合的成员个数不为0，则说明有错误发生<br /><br />，事务处理失败。Error集合中的每一个Error对象，代表一个错误信息。</div><div style="FONT-SIZE: 12px; LINE-HEIGHT: 160%"><span class="bold">Access基本操作</span><br /><br /><div style="FONT-SIZE: 12px; LINE-HEIGHT: 160%">Access基本操作<br /><br />一、数据库的基本概念和设计方法<br /><br />所谓数据库实际上是关于某一特定主题或目标的信息集合。它把大量数据按一定的结构进行存储，集中管理和统一使用这些数据，实现数据共享。在Access中，数据库不仅包含用于存放加工过的信息的表，还包含以表中所存放的信息为操作对象的查询、窗体、报表、页等数据库对象。<br /><br />一般地，数据库的设计应遵循以下几个步骤：<br /><br />1、确定创建数据库所要完成的目的。<br /><br />2、确定创建数据库中所需要的表。<br /><br />3、确定表中所需要的字段。<br /><br />4、明确有唯一值的主关键字段。<br /><br />5、确定表之间的关系。<br /><br />6、优化设计。<br /><br />7、输入数据并创建其他数据库对象。<br />二、创建数据库<br /><br />在Access中既可以使用人工的方法按照自己的要求来建立数据库，也可以使用软件为用户提供的各种数据库向导，前者更为自由而后者则显然要方便一些。<br /><br />A、自行创建数据库<br /><br />使用人工的方法按照自己的要求来建立数据库，首先应创建一个空数据库，然后再将对象加入到数据库中。创建空数据库的步骤为：<br /><br />1、在启动Access后的Microsoft Access对话框中选择“空Access数据库”选项。<br /><br />2、在弹出的“文件新建数据库”对话框中选择数据库存放的位置、输入数据库的名称并单击“创建”按钮，进入Access数据库窗口，即完成了空数据库的创建。<br /><br />B、使用数据库向导创建数据库<br /><br />Access为用户提供了多种数据库向导，使用数据库向导可以方便地完成数据库的创建工作。具体步骤如下：<br /><br />1、在启动Access后的Microsoft Access对话框中选择“Access数据库向导、数据页和项目”选项。<br /><br />2、在弹出“新建”对话框后单击“数据库”选项卡，选择一种数据库。<br /><br />3、在弹出的“文件新建数据库”对话框中，选择数据库存放的位置，输入数据库的名称并单击“创建”按钮。<br /><br />4、依次在数据库向导窗口中为数据库中的各个表选择字段、选择显示样式、确定打印报表所用的样式、输入所建数据库的标题、确定是否在所有报表上加一幅图片，最后启动该数据库，至此就完成了使用向导创建数据库的工作。<br />三、表的基本概念<br /><br />表是数据库中存储数据的最基本的对象，常称为“基础表”，是构成数据库的一个重要组成部分。表由若干记录组成，每一行称为一个记录，对应着一个真实的对象；每一列称为一个字段，对应着对象的一个属性信息。每个表的关键字（关键字可以为一个字段或多个字段）使表中的记录唯一。在表内还可以定义索引，当表内存放大量数据时可以加速数据的查找。<br /><br />Access中的所有数据都存放在数据表中。表是一个数据库系统的基础，只有建立表后，才可以建立查询、窗体和报表等其他项目，逐步完善数据库。<br />四、创建表<br /><br />Access提供了三种创建新表的方法：<br /><br />1、使用数据库向导，在一个操作中创建整个数据库所需的全部表、窗体及报表。<br /><br />2、使用表向导来选择表的字段，这些字段可以从其他已定义好的表中选择。<br /><br />3、使用设计视图创建表。<br /><br />不管使用哪一种方法来创建表，随时都可以使用表设计试图来进一步自定义表。由于第一种方法已包含在使用数据库向导创建数据库的内容之中，这里着重说明后两种方法的实现。<br /><br />A、利用表向导创建表<br /><br />利用表向导创建新表，一般情况下能满足数据库开发者的要求，至于多余的字段可以用其他的方法将其删除。利用表向导创建表的步骤如下：<br /><br />1、在数据库窗口中，单击“对象”列表中的“表”，双击“使用向导创建表”。<br /><br />2、在弹出的“表向导”对话框中单击“表向导”并单击“确定”按钮。<br /><br />3、在弹出的对话框中选择要使用的表并添加所需要的字段（可以重命名）后单击“下一步”按钮。<br /><br />4、为表指定名称并确认主键，单击“下一步”按钮。<br /><br />5、选择创建完表后的动作，然后单击“完成”按钮。<br /><br />B、利用设计视图创建表<br /><br />使用设计器建立表的方法有两种：一种方法是直接使用设计器来创建新表，另一种方法是使用表向导来构造一个结构相近的表，然后在设计器中打开修改。下面介绍使用设计器来创建新表的步骤：<br /><br />1、在数据库窗口中，单击对象列表中的“表”，双击“使用设计器创建表”的按钮，进入“新建表”对话框。<br /><br />2、在各栏中输入字段名称、选择数据类型，然后设置字段属性。<br /><br />3、接着选中要设置为关键字的字段，单击“编辑”菜单中“主键”命令，设置“主关键字”，此时在所选字段左边行选定器上出现钥匙标记。<br /><br />4、保存所设计的表。<br />五、关于主关键字与建立表间关系<br /><br />A、设置主关键字<br /><br />为了提高Access在查询、窗体和报表操作中的快速查找能力和组合保存在各个不同表中信息的性能，必须为建立的表指定一个主关键字。主关键字可以包含一个或多个字段，以保证每条记录都有唯一的值。设定主关键字的目的就在于保证表中的所有记录都能够被唯一识别。如果表中没有可以用作唯一识别表中记录的字段，则可以使用多个字段来组合成主关键字。其设置步骤如下：<br /><br />1、在表设计器中，单击字段名称左边的字段选择按钮，选择要作为主关键字的字段。单击字段选择按钮的同时按住Ctrl键可以同时选择多个字段。<br /><br />2、单击“编辑”菜单中的“主键”命令，则在该字段的左边显示钥匙标记。<br /><br />B、建立表间关系<br /><br />在表中定义主关键字除了可以保证每条记录可以被唯一识别外，更重要的作用在于多个表间的连接。当数据库中包含多个表时，需要通过主关键字的连接来建立表间的关系，使各表协同工作。<br /><br />要在两个表间建立关系，必须在这两个表中拥有相同数据类型的字段。其设置步骤如下：<br /><br />1、打开表所在的数据库窗口。<br /><br />2、单击“工具”菜单中的“关系”命令，弹出“显示表”对话框。<br /><br />3、选择要建立关系的表，然后单击“添加”按钮，依次添加完所需要的表后，单击“关闭”按钮。<br /><br />4、在关系对话框中选择其中一表中的主关键字，拖曳到另一表中相同的主关键字，释放鼠表键后，弹出“编辑关系”对话框。<br /><br />5、若在“编辑关系”对话框中选中“实施参照完整性”和“级联更新相关字段”复选框，则使在更新主表中记录的同时更新关系表中的相关记录。<br /><br />6、若在“编辑关系”对话框中选中“实施参照完整性”和“级联删除相关字段”复选框，则使在删除主表中记录的同时删除关系表中的相关记录。<br /><br />7、接着单击“联接类型”按钮，弹出“联接属性”对话框，在此选择联接的方式。<br /><br />8、在“编辑关系”对话框中单击“创建”按钮，即在创建关系的表之间有一条线将其连接起来，表示已创建好表之间的关系。<br /><br />9、关闭关系对话框，按需要选择是否保存关系的设定。<br /><br />编辑或修改关联性的操作是直接用鼠标在这一条线上双击，然后在弹出的“编辑关系”对话框中进行修改。删除关联性的操作是先用鼠标在这一条线上单击，然后再按Delete键删除。<br />六、记录的基本操作<br /><br />Access只允许每次操作一个记录，正在操作的记录在行选定器上显示一个“三角图标”用于标记当前记录。当改变当前记录的数据但又没有保存时，行选定器上显示一个“笔型图标”。<br /><br />记录的基本操作包括添加记录、修改记录和删除记录。<br /><br />A、添加记录<br /><br />打开表的数据视图画面时，表的最末端有一条空白的记录，在记录的行选定器上显示一个星花图标，标示可以从这里开始增加新的记录。单击“插入”菜单中的“新记录”或直接选定该行即可添加记录。输入完数据后，移到另一个记录时会自动保存该记录。<br /><br />B、修改记录<br /><br />可用Tab键或直接用鼠标移到要修改的字段进行修改。<br /><br />C、删除记录<br /><br />选择一条或多条记录后按Delete键删除所有选中的记录。<br />七、总结<br /><br />读过本文之后，相信您应该可以轻松地创建一个小型的数据库了，然而相比于Access强大而繁多的功能，这篇文章涉及的仅仅是最最简单和基本的一些操作，关于查询、窗体、报表、页以及宏与模块的内容将比表的基础操作更为复杂，有兴趣的读者可以查询其它的资料，以便顺利地设计和制作出高质量的应用系统。</div></div></div></div>]]></description> 
<guid isPermaLink="false">6076265@http://http88.bokee.com/</guid> 
<dc:subject>html&amp;web</dc:subject> 
<dc:date>2007-01-29T16:36:35Z</dc:date> 
</item> 
<item> 
<title><![CDATA[Turbo C头文件 ]]></title> 
<link>http://http88.bokee.com/6074441.html</link> 
<description><![CDATA[<p><br /><br />&amp;nbsp;ALLOC.H&amp;nbsp;说明内存管理函数(分配、释放等)。&amp;nbsp;<br /><br />&amp;nbsp;ASSERT.H&amp;nbsp;定义&amp;nbsp;assert调试宏。&amp;nbsp;<br /><br />&amp;nbsp;BIOS.H&amp;nbsp;说明调用IBM—PC&amp;nbsp;ROM&amp;nbsp;BIOS子程序的各个函数。&amp;nbsp;<br /><br />&amp;nbsp;CONIO.H&amp;nbsp;说明调用DOS控制台I/O子程序的各个函数。&amp;nbsp;<br /><br />&amp;nbsp;CTYPE.H&amp;nbsp;包含有关字符分类及转换的名类信息(如&amp;nbsp;isalpha和toascii等)。&amp;nbsp;<br /><br />&amp;nbsp;DIR.H&amp;nbsp;包含有关目录和路径的结构、宏定义和函数。&amp;nbsp;<br /><br />&amp;nbsp;DOS.H&amp;nbsp;定义和说明MSDOS和8086调用的一些常量和函数。&amp;nbsp;<br /><br />&amp;nbsp;ERRON.H&amp;nbsp;定义错误代码的助记符。&amp;nbsp;<br /><br />&amp;nbsp;FCNTL.H&amp;nbsp;定义在与open库子程序连接时的符号常量。&amp;nbsp;<br /><br />&amp;nbsp;FLOAT.H&amp;nbsp;包含有关浮点运算的一些参数和函数。&amp;nbsp;<br /><br />&amp;nbsp;GRAPHICS.H&amp;nbsp;说明有关图形功能的各个函数，图形错误代码的常量定义，正对不同驱动程序的各种颜色值，及函数用到的一些特殊结构。&amp;nbsp;<br /><br />&amp;nbsp;IO.H&amp;nbsp;包含低级I/O子程序的结构和说明。&amp;nbsp;<br /><br />&amp;nbsp;LIMIT.H&amp;nbsp;包含各环境参数、编译时间限制、数的范围等信息。&amp;nbsp;<br /><br />&amp;nbsp;MATH.H&amp;nbsp;说明数学运算函数，还定了&amp;nbsp;HUGE&amp;nbsp;VAL&amp;nbsp;宏，&amp;nbsp;说明了matherr和matherr子程序用到的特殊结构。&amp;nbsp;<br /><br />&amp;nbsp;MEM.H&amp;nbsp;说明一些内存操作函数(其中大多数也在STRING.H中说明)。&amp;nbsp;<br /><br />&amp;nbsp;PROCESS.H&amp;nbsp;说明进程管理的各个函数，spawn…和EXEC&amp;nbsp;…函数的结构说明。&amp;nbsp;<br /><br />&amp;nbsp;SETJMP.H&amp;nbsp;定义longjmp和setjmp函数用到的jmp&amp;nbsp;buf类型，说明这两个函数。&amp;nbsp;<br /><br />&amp;nbsp;SHARE.H&amp;nbsp;定义文件共享函数的参数。&amp;nbsp;<br /><br />&amp;nbsp;SIGNAL.H&amp;nbsp;定义SIG[ZZ(Z]&amp;nbsp;[ZZ)]IGN和SIG[ZZ(Z]&amp;nbsp;[ZZ)]DFL常量，说明rajse和signal两个函数。&amp;nbsp;<br /><br />&amp;nbsp;STDARG.H&amp;nbsp;定义读函数参数表的宏。(如vprintf,vscarf函数)。&amp;nbsp;<br /><br />&amp;nbsp;STDDEF.H&amp;nbsp;定义一些公共数据类型和宏。&amp;nbsp;<br /><br />&amp;nbsp;STDIO.H&amp;nbsp;定义Kernighan和Ritchie在Unix&amp;nbsp;System&amp;nbsp;V&amp;nbsp;中定义的标准和扩展的类型和宏。还定义标准I/O&amp;nbsp;预定义流：stdin,stdout和stderr，说明&amp;nbsp;I/O流子程序。&amp;nbsp;<br /><br />&amp;nbsp;STDLIB.H&amp;nbsp;说明一些常用的子程序：转换子程序、搜索/&amp;nbsp;排序子程序等。&amp;nbsp;<br /><br />&amp;nbsp;STRING.H&amp;nbsp;说明一些串操作和内存操作函数。&amp;nbsp;<br /><br />&amp;nbsp;SYS\STAT.H&amp;nbsp;定义在打开和创建文件时用到的一些符号常量。&amp;nbsp;<br /><br />&amp;nbsp;SYS\TYPES.H&amp;nbsp;说明ftime函数和timeb结构。 </p><p>&amp;nbsp;SYS\TIME.H&amp;nbsp;定义时间的类型time[ZZ(Z]&amp;nbsp;[ZZ)]t。 <br /><br />TIME.H&amp;nbsp;定义时间转换子程序asctime、localtime和gmtime的结构，ctime、&amp;nbsp;difftime、&amp;nbsp;gmtime、&amp;nbsp;localtime和stime用到的类型，并提供这些函数的原型。&amp;nbsp;<br /><br />&amp;nbsp;VALUE.H&amp;nbsp;定义一些重要常量，包括依赖于机器硬件的和为与Unix&amp;nbsp;System&amp;nbsp;V相兼容而说明的一些常量，包括浮点和双精度值的范围。</p>]]></description> 
<guid isPermaLink="false">6074441@http://http88.bokee.com/</guid> 
<dc:subject>项目开发经验</dc:subject> 
<dc:date>2007-01-28T21:56:08Z</dc:date> 
</item> 
<item> 
<title><![CDATA[进程与线程]]></title> 
<link>http://http88.bokee.com/6073954.html</link> 
<description><![CDATA[说法一：进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. <br /><br />线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源. <br /><br />一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行 <br /><br /><br /><br />说法二：进程和线程都是由操作系统所体会的程序运行的基本单元，系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于： <br /><br />简而言之,一个程序至少有一个进程,一个进程至少有一个线程. <br /><br />线程的划分尺度小于进程，使得多线程程序的并发性高。 <br /><br />另外，进程在执行过程中拥有独立的内存单元，而多个线程共享内存，从而极大地提高了程序的运行效率。 <br /><br />线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行，必须依存在应用程序中，由应用程序提供多个线程执行控制。 <br /><br />从逻辑角度来看，多线程的意义在于一个应用程序中，有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用，来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。 <br /><br /><br /><br />说法三：多线程共存于应用程序中是现代操作系统中的基本特征和重要标志。用过UNIX操作系统的读者知道进程，在UNIX操作系统中，每个应用程序的执行都在操作系统内核中登记一个进程标志，操作系统根据分配的标志对应用程序的执行进行调度和系统资源分配，但进程和线程有什么区别呢？ <br /><br />进程和线程都是由操作系统所体会的程序运行的基本单元，系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于： <br /><br />线程的划分尺度小于进程，使得多线程程序的并发性搞。 <br /><br />另外，进程在执行过程中拥有独立的内存单元，而多个线程共享内存，从而极大地提高了程序的运行效率。 <br /><br />线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行，必须依存在应用程序中，由应用程序提供多个线程执行控制。 <br /><br />从逻辑角度来看，多线程的意义在于一个应用程序中，有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用，来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。 <br /><br />进程（Process）是最初定义在Unix等多用户、多任务操作系统环境下用于表示应用程序在内存环境中基本执行单元的概念。以Unix操作系统为例，进程是Unix操作系统环境中的基本成分、是系统资源分配的基本单位。Unix操作系统中完成的几乎所有用户管理和资源分配等工作都是通过操作系统对应用程序进程的控制来实现的。 <br /><br />C、C++、Java等语言编写的源程序经相应的编译器编译成可执行文件后，提交给计算机处理器运行。这时，处在可执行状态中的应用程序称为进程。从用户角度来看，进程是应用程序的一个执行过程。从操作系统核心角度来看，进程代表的是操作系统分配的内存、CPU时间片等资源的基本单位，是为正在运行的程序提供的运行环境。进程与应用程序的区别在于应用程序作为一个静态文件存储在计算机系统的硬盘等存储空间中，而进程则是处于动态条件下由操作系统维护的系统资源管理实体。多任务环境下应用程序进程的主要特点包括： <br /><br />●进程在执行过程中有内存单元的初始入口点，并且进程存活过程中始终拥有独立的内存地址空间； <br /><br />●进程的生存期状态包括创建、就绪、运行、阻塞和死亡等类型； <br /><br />●从应用程序进程在执行过程中向CPU发出的运行指令形式不同，可以将进程的状态分为用户态和核心态。处于用户态下的进程执行的是应用程序指令、处于核心态下的应用程序进程执行的是操作系统指令。 <br /><br />在Unix操作系统启动过程中，系统自动创建swapper、init等系统进程，用于管理内存资源以及对用户进程进行调度等。在Unix环境下无论是由操作系统创建的进程还要由应用程序执行创建的进程，均拥有唯一的进程标识（PID）。 <br /><br />说法四：应用程序在执行过程中存在一个内存空间的初始入口点地址、一个程序执行过程中的代码执行序列以及用于标识进程结束的内存出口点地址，在进程执行过程中的每一时间点均有唯一的处理器指令与内存单元地址相对应。 <br /><br />Java语言中定义的线程（Thread）同样包括一个内存入口点地址、一个出口点地址以及能够顺序执行的代码序列。但是进程与线程的重要区别在于线程不能够单独执行，它必须运行在处于活动状态的应用程序进程中，因此可以定义线程是程序内部的具有并发性的顺序代码流。 <br /><br />Unix操作系统和Microsoft Windows操作系统支持多用户、多进程的并发执行，而Java语言支持应用程序进程内部的多个执行线程的并发执行。多线程的意义在于一个应用程序的多个逻辑单元可以并发地执行。但是多线程并不意味着多个用户进程在执行，操作系统也不把每个线程作为独立的进程来分配独立的系统资源。进程可以创建其子进程，子进程与父进程拥有不同的可执行代码和数据内存空间。而在用于代表应用程序的进程中多个线程共享数据内存空间，但保持每个线程拥有独立的执行堆栈和程序执行上下文（Context）。 <br /><br />基于上述区别，线程也可以称为轻型进程 (Light Weight Process，LWP)。不同线程间允许任务协作和数据交换，使得在计算机系统资源消耗等方面非常廉价。 <br /><br />线程需要操作系统的支持，不是所有类型的计算机都支持多线程应用程序。Java程序设计语言将线程支持与语言运行环境结合在一起，提供了多任务并发执行的能力。这就好比一个人在处理家务的过程中，将衣服放到洗衣机中自动洗涤后将大米放在电饭锅里，然后开始做菜。等菜做好了，饭熟了同时衣服也洗好了。 <br /><br />需要注意的是：在应用程序中使用多线程不会增加 CPU 的数据处理能力。只有在多CPU 的计算机或者在网络计算体系结构下，将Java程序划分为多个并发执行线程后，同时启动多个线程运行，使不同的线程运行在基于不同处理器的Java虚拟机中，才能提高应用程序的执行效率。]]></description> 
<guid isPermaLink="false">6073954@http://http88.bokee.com/</guid> 
<dc:subject>项目开发经验</dc:subject> 
<dc:date>2007-01-28T18:05:54Z</dc:date> 
</item> 
<item> 
<title><![CDATA[堆和栈]]></title> 
<link>http://http88.bokee.com/6073816.html</link> 
<description><![CDATA[<p><strong>链表与数组的区别<br /></strong>A 从逻辑结构来看<br />A-1. 数组必须事先定义固定的长度（元素个数），不能适应数据动态地增减的情况。当&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 数据增加时，可能超出原先定义的元素个数；当数据减少时，造成内存浪费。</p><p>A-2. 链表动态地进行存储分配，可以适应数据动态地增减的情况，且可以方便地插入、&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 删除数据项。（数组中插入、删除数据项时，需要移动其它数据项）</p><p><br />B 从内存存储来看<br />B-1. (静态)数组从栈中分配空间, 对于程序员方便快速,但是自由度小<br />B-2. 链表从堆中分配空间, 自由度大但是申请管理比较麻烦.<br /><br /></p><p class="MsoNormal" style="BACKGROUND: white; MARGIN: 0cm 0cm 0pt 147pt; TEXT-INDENT: 21pt; TEXT-ALIGN: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-outline-level: 3" align="left"><b><span style="FONT-SIZE: 13.5pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">堆和栈的区别</span></b><b><span lang="EN-US" style="FONT-SIZE: 13.5pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span></b><p /><p /><p class="MsoNormal" style="BACKGROUND: white; MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">solost </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">于</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt"> 2004</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">年</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt"> 10</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">月</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">09</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">日</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">发表</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span><p /><p /><p class="MsoNormal" style="BACKGROUND: white; MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto" align="left"><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">一、预备知识</span></b><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">—</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">程序的内存分配</span></b><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span></b><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">一个由</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">c/C++</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">编译的程序占用的内存分为以下几个部分</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">1</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">、<b style="mso-bidi-font-weight: normal">栈区（</b></span><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">stack</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">）</span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">—&amp;nbsp; &amp;nbsp;</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">由<b style="mso-bidi-font-weight: normal">编译器</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">(Compiler)</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">自动分配释放</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">，存放<b style="mso-bidi-font-weight: normal">函数的参数值</b>，<b style="mso-bidi-font-weight: normal">局部变量</b>的值等。其操作方式类似于数据结构中的栈。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">2</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">、<b style="mso-bidi-font-weight: normal">堆区（</b></span><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">heap</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">）</span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt"> —&amp;nbsp; &amp;nbsp;</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">一般由<b style="mso-bidi-font-weight: normal">程序员</b>分配释放，</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">若程序员不释放，程序结束时可能由</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">OS</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">回收</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">。注意它与数据结构中的堆是两回事，分配方式倒是类似于链表，呵呵。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">3</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">、<b style="mso-bidi-font-weight: normal">全局区（静态区）</b>（</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">static</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">）</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">—</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">，<b style="mso-bidi-font-weight: normal">全局变量和静态变量的</b>存储是放在一块的，<b style="mso-bidi-font-weight: normal">初始化</b>的全局变量和静态变量在一块区域，</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt"> </span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">未初始化</span></b><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">的全局变量和未初始化的静态变量在相邻的另一块区域。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt"> - </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">程序结束后有系统释放</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt"> </span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">4</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">、<b style="mso-bidi-font-weight: normal">文字常量区</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">&amp;nbsp;&amp;nbsp;— </span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">常量字符串</span></b><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">就是放在这里的。</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">程序结束后由<b style="mso-bidi-font-weight: normal">系统释放</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">5</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">、<b style="mso-bidi-font-weight: normal">程序代码区</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">— </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt; mso-bidi-font-size: 12.0pt">存放<b style="mso-bidi-font-weight: normal">函数体的二进制代码</b>。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br style="mso-special-character: line-break" /><br style="mso-special-character: line-break" /></span><p /><p /><p class="MsoNormal" style="BACKGROUND: white; MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto" align="left"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">二、例子程序</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">这是一个前辈写的，非常详细</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />//main.cpp <br />int a = 0; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">全局初始化区</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />char *p1; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">全局未初始化区</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />main() <br />{ <br />int b; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">栈</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />char s[] = &amp;quot;abc&amp;quot;; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">栈</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />char *p2; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">栈</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />char *p3 = &amp;quot;123456&amp;quot;; 123456\0</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">在常量区，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">p3</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">在栈上。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />static int c =0</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">；</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">全局（静态）初始化区</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />p1 = (char *)malloc(10); <br />p2 = (char *)malloc(20); <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">分配得来得</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">10</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">和</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">20</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">字节的区域就在堆区。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />strcpy(p1, &amp;quot;123456&amp;quot;); 123456\0</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">放在常量区，编译器<b style="mso-bidi-font-weight: normal">可能</b>会将它与</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">p3</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">所指向的</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">&amp;quot;123456&amp;quot;</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">优化成一个地方。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />} <br /><br /><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">二、堆和栈的理论知识</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /><b style="mso-bidi-font-weight: normal">2.1</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">申请方式</span></b><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">stack: <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">由系统自动分配。</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">例如，声明在函数中一个局部变量</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> int b; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">系统自动在栈中为</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">b</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">开辟空间</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />heap: <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">需要程序员自己申请，并指明大小，在</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">c</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">中</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">malloc</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">函数</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">如</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">p1 = (char *)malloc(10); <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">在</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">中用</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">new</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">运算符</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">如</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">p2 = (char *)malloc(10); <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">但是注意</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">p1</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">、</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">p2</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">本身是在栈中的。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /><br /><br /><b style="mso-bidi-font-weight: normal">2.2 </b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">申请后系统的响应</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">栈：只要栈的剩余空间大于所申请空间，系统将为程序提供内存，否则将报异常提示栈溢出。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">堆：首先应该知道操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大小，这样，代码中的</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">delete</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">语句才能正确的释放本内存空间。另外，由于找到的堆结点的大小不一定正好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表中。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /><br /><b style="mso-bidi-font-weight: normal">2.3</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">申请大小的限制</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">栈：在</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">Windows</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">下</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">, </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">栈是<b style="mso-bidi-font-weight: normal">向低地址扩展</b>的数据结构，是一块<b style="mso-bidi-font-weight: normal">连续</b>的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是<b style="mso-bidi-font-weight: normal">系统预先规定好</b>的，在</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">WINDOWS</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">下，栈的大小是</span><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">2M</span></b><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">（也有的说是</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">1M</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">，总之是一个编译时就确定的常数），如果申请的空间超过栈的剩余空间时，将提示</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">overflow</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">。因此，能从栈获得的空间较小。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">堆：堆是<b style="mso-bidi-font-weight: normal">向高地址扩展</b>的数据结构，是<b style="mso-bidi-font-weight: normal">不连续</b>的内存区域。这是由于系统是用链表来存储的空闲内存地址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中<b style="mso-bidi-font-weight: normal">有效的虚拟内存</b>。由此可见，堆获得的空间比较灵活，也比较大。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /><br /><br /><b style="mso-bidi-font-weight: normal">2.4</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">申请效率的比较：</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">栈由系统自动分配，速度较快。但程序员是<b style="mso-bidi-font-weight: normal">无法控制</b>的。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">堆是由</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">new</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">分配的内存，一般速度比较慢，而且<b style="mso-bidi-font-weight: normal">容易产生内存碎片</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">,</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">不过用起来最<b style="mso-bidi-font-weight: normal">方便</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">. <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">另外，在</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">WINDOWS</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">下，最好的方式是用</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">VirtualAlloc</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">分配内存，他不是在堆，也不是在栈是直接在进程的地址空间中保留一快内存，虽然用起来最不方便。但是速度快，也最灵活。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /><br /><b style="mso-bidi-font-weight: normal">2.5</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">堆和栈中的存储内容</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">栈：</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">在函数调用时，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">(1) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">第一个进栈的是主函数中后的下一条指令（函数调用语句的下一条可执行语句）的<b style="mso-bidi-font-weight: normal">地址</b>，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">(2) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">然后是函数的各个参数，在大多数的</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">C</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">编译器中，<b style="mso-bidi-font-weight: normal">参数是由右往左入栈</b>的，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">(3) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">然后是函数中的局部变量。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><span style="mso-tab-count: 1"> </span></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">注意</span></b><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">: </span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">静态变量是不入栈的。</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">当本次函数调用结束后，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">(1) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">局部变量先出栈，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">(2) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">然后是参数，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">(3) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">最后栈顶指针指向最开始存的地址，也就是主函数中的下一条指令，<b style="mso-bidi-font-weight: normal">程序由该点继续运行</b>。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">堆：一般是在堆的头部用一个字节存放堆的大小。<b style="mso-bidi-font-weight: normal">堆中的具体内容有程序员安排。</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /><br /><b style="mso-bidi-font-weight: normal">2.6</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">存取效率的比较</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br />char s1[] = &amp;quot;aaaaaaaaaaaaaaa&amp;quot;; <br />char *s2 = &amp;quot;bbbbbbbbbbbbbbbbb&amp;quot;; <br />aaaaaaaaaaa</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">是在<b style="mso-bidi-font-weight: normal">运行时刻</b>赋值的；</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">而</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">bbbbbbbbbbb</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">是在<b style="mso-bidi-font-weight: normal">编译时</b>就确定的；</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">但是，在以后的存取中，在栈上的数组比指针所指向的字符串</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">(</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">例如堆</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">)</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">快。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">比如：</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />#include <br />void main() <br />{ <br />char a = 1; <br />char c[] = &amp;quot;1234567890&amp;quot;; <br />char *p =&amp;quot;1234567890&amp;quot;; <br />a = c[1]; <br />a = p[1]; <br />return; <br />} <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">对应的汇编代码</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br />10: a = c[1]; <br />00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] <br />0040106A 88 4D FC mov byte ptr [ebp-4],cl <br />11: a = p[1]; <br />0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] <br />00401070 8A 42 01 mov al,byte ptr [edx+1] <br />00401073 88 45 FC mov byte ptr [ebp-4],al <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">第一种在读取时直接就把字符串中的元素读到寄存器</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">cl</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">中，而第二种则要先把指针值读到</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">edx</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">中，在根据</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">edx</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">读取字符，显然慢了。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /><br /><br /><b style="mso-bidi-font-weight: normal">2.7</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">小结：</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">堆和栈的区别可以用如下的比喻来看出：</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">使用栈就象我们去饭馆里吃饭，只管点菜（发出申请）、付钱、和吃（使用），吃饱了就走，不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作，他的好处是<b style="mso-bidi-font-weight: normal">快捷，但是自由度小。</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt">使用堆就象是自己动手做喜欢吃的菜肴，比较麻烦，但是比较符合自己的口味，而且自由度大。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 宋体; mso-font-kerning: 0pt"> </span><p /><p /><p><br /><strong>深度优先搜索与广度优先搜索算法有何区别呢？<br /></strong>　　通常深度优先搜索法不全部保留结点，扩展完的结点从数据库中弹出删去，这样，一般在数据库中存储的结点数就是深度值，因此它占用空间较少。所以，当搜索树的结点较多，用其它方法易产生内存溢出时，深度优先搜索不失为一种有效的求解方法。<br />　　广度优先搜索算法，一般需存储产生的所有结点，占用的存储空间要比深度优先搜索大得多，因此，程序设计中，必须考虑溢出和节省内存空间的问题。但广度优先搜索法一般无回溯操作，即入栈和出栈的操作，所以运行速度比深度优先搜索要快些<br /></p></p><p /></p><p /></p><p /></p><p />]]></description> 
<guid isPermaLink="false">6073816@http://http88.bokee.com/</guid> 
<dc:subject>项目开发经验</dc:subject> 
<dc:date>2007-01-28T16:48:00Z</dc:date> 
</item> 
<item> 
<title><![CDATA[三十分钟掌握STL]]></title> 
<link>http://http88.bokee.com/6069682.html</link> 
<description><![CDATA[<span id="ArticleContent1_ArticleContent1_lblContent"><h1 class="MsoNormal" style="TEXT-INDENT: 21pt"><span style="FONT-FAMILY: 宋体">译者：</span><span lang="EN-US">kary</span></h1><p class="MsoNormal" style="TEXT-INDENT: 21pt"><span lang="EN-US">contact:karymay@163.net</span></p><h2><span lang="EN-US">STL</span><span style="FONT-FAMILY: 黑体">概述</span></h2><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">的一个重要特点是数据结构和算法的分离。尽管这是个简单的概念，但这种分离确实使得</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">变得非常通用。例如，由于</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">的</span><span lang="EN-US">sort()</span><span style="FONT-FAMILY: 宋体">函数是完全通用的，你可以用它来操作几乎任何数据集合，包括链表，容器和数组。</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span style="FONT-FAMILY: 宋体">要点</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">算法作为模板函数提供。为了和其他组件相区别，在本书中</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">算法以后接一对圆括弧的方式表示，例如</span><span lang="EN-US">sort()</span><span style="FONT-FAMILY: 宋体">。</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">另一个重要特性是它不是面向对象的。为了具有足够通用性，</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">主要依赖于模板而不是封装，继承和虚函数（多态性）——</span><span lang="EN-US">OOP</span><span style="FONT-FAMILY: 宋体">的三个要素。你在</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">中找不到任何明显的类继承关系。这好像是一种倒退，但这正好是使得</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">的组件具有广泛通用性的底层特征。另外，由于</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">是基于模板，内联函数的使用使得生成的代码短小高效。</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span style="FONT-FAMILY: 宋体">提示</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span style="FONT-FAMILY: 宋体">确保在编译使用了</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">的程序中至少要使用</span><span lang="EN-US">-O</span><span style="FONT-FAMILY: 宋体">优化来保证内联扩展。</span></p><h3><span lang="EN-US" style="FONT-SIZE: 10.5pt">STL</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">组件</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">提供了大量的模板类和函数，可以在</span><span lang="EN-US">OOP</span><span style="FONT-FAMILY: 宋体">和常规编程中使用。所有的</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">的大约</span><span lang="EN-US">50</span><span style="FONT-FAMILY: 宋体">个算法都是完全通用的，而且不依赖于任何特定的数据类型。下面的小节说明了三个基本的</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">组件：</span></p><p class="MsoNormal" style="MARGIN-LEFT: 57.75pt; TEXT-INDENT: -36pt"><span lang="EN-US">1）<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><span style="FONT-FAMILY: 宋体">迭代器提供了访问容器中对象的方法。例如，可以使用一对迭代器指定</span><span lang="EN-US">list</span><span style="FONT-FAMILY: 宋体">或</span><span lang="EN-US">vector</span><span style="FONT-FAMILY: 宋体">中的一定范围的对象。迭代器就如同一个指针。事实上，</span><span lang="EN-US">C++</span><span style="FONT-FAMILY: 宋体">的指针也是一种迭代器。但是，迭代器也可以是那些定义了</span><span lang="EN-US">operator*()</span><span style="FONT-FAMILY: 宋体">以及其他类似于指针的操作符地方法的类对象。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 57.75pt; TEXT-INDENT: -36pt"><span lang="EN-US">2）<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><span style="FONT-FAMILY: 宋体">容器是一种数据结构，如</span><span lang="EN-US">list</span><span style="FONT-FAMILY: 宋体">，</span><span lang="EN-US">vector</span><span style="FONT-FAMILY: 宋体">，和</span><span lang="EN-US">deques </span><span style="FONT-FAMILY: 宋体">，以模板类的方法提供。为了访问容器中的数据，可以使用由容器类输出的迭代器。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 57.75pt; TEXT-INDENT: -36pt"><span lang="EN-US">3）<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><span style="FONT-FAMILY: 宋体">算法是用来操作容器中的数据的模板函数。例如，</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">用</span><span lang="EN-US">sort()</span><span style="FONT-FAMILY: 宋体">来对一个</span><span lang="EN-US">vector</span><span style="FONT-FAMILY: 宋体">中的数据进行排序，用</span><span lang="EN-US">find()</span><span style="FONT-FAMILY: 宋体">来搜索一个</span><span lang="EN-US">list</span><span style="FONT-FAMILY: 宋体">中的对象。函数本身与他们操作的数据的结构和类型无关，因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用。</span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">头文件</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span style="FONT-FAMILY: 宋体">为了避免和其他头文件冲突，</span><span lang="EN-US"> STL</span><span style="FONT-FAMILY: 宋体">的头文件不再使用常规的</span><span lang="EN-US">.h</span><span style="FONT-FAMILY: 宋体">扩展。为了包含标准的</span><span lang="EN-US">string</span><span style="FONT-FAMILY: 宋体">类，迭代器和算法，用下面的指示符：</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">#include &amp;lt;string&amp;gt;</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">#include &amp;lt;iterator&amp;gt;</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">#include &amp;lt;algorithm&amp;gt;</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">&amp;nbsp;<p /></span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span style="FONT-FAMILY: 宋体">如果你查看</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">的头文件，你可以看到象</span><span lang="EN-US">iterator.h</span><span style="FONT-FAMILY: 宋体">和</span><span lang="EN-US">stl_iterator.h</span><span style="FONT-FAMILY: 宋体">这样的头文件。由于这些名字在各种</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">实现之间都可能不同，你应该避免使用这些名字来引用这些头文件。为了确保可移植性，使用相应的没有</span><span lang="EN-US">.h</span><span style="FONT-FAMILY: 宋体">后缀的文件名。表</span><span lang="EN-US">1</span><span style="FONT-FAMILY: 宋体">列出了最常使用的各种容器类的头文件。该表并不完整，对于其他头文件，我将在本章和后面的两章中介绍。</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">&amp;nbsp;<p /></span></p><p class="MsoNormal" style="TEXT-ALIGN: center" align="center"><b><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">表</span></b><b><span lang="EN-US" style="FONT-SIZE: 10pt"> 1. STL</span></b><b><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">头文件和容器类</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"><p /></span></p><div align="center"><table cellspacing="3" cellpadding="0" border="1"><thead><tr><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><b><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">#include</span></b></tt><b><span lang="EN-US"><p /></span></b></p></td><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><b><span lang="EN-US">Container Class<p /></span></b></p></td></tr></thead><tbody><tr><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">&amp;lt;deque&amp;gt;</span></tt></p></td><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">deque</span></tt></p></td></tr><tr><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">&amp;lt;list&amp;gt;</span></tt></p></td><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">list</span></tt></p></td></tr><tr><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">&amp;lt;map&amp;gt;</span></tt></p></td><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">map, multimap</span></tt></p></td></tr><tr><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">&amp;lt;queue&amp;gt;</span></tt></p></td><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">queue, priority_queue</span></tt></p></td></tr><tr><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">&amp;lt;set&amp;gt;</span></tt></p></td><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">set, multiset</span></tt></p></td></tr><tr><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">&amp;lt;stack&amp;gt;</span></tt></p></td><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">stack</span></tt></p></td></tr><tr><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">&amp;lt;vector&amp;gt;</span></tt></p></td><td style="PADDING-RIGHT: 0.75pt; PADDING-LEFT: 0.75pt; PADDING-BOTTOM: 0.75pt; PADDING-TOP: 0.75pt" valign="top"><p style="TEXT-ALIGN: center" align="center"><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">vector, vector&amp;lt;bool&amp;gt;</span></tt></p></td></tr></tbody></table></div><p class="MsoNormal"><span lang="EN-US">&amp;nbsp;<p /></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">名字空间</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span style="FONT-FAMILY: 宋体">你的编译器可能不能识别名字空间。名字空间就好像一个信封，将标志符封装在另一个名字中。标志符只在名字空间中存在，因而避免了和其他标志符冲突。例如，可能有其他库和程序模块定义了</span><span lang="EN-US">sort()</span><span style="FONT-FAMILY: 宋体">函数，为了避免和</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">地</span><span lang="EN-US">sort()</span><span style="FONT-FAMILY: 宋体">算法冲突，</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">的</span><span lang="EN-US">sort()</span><span style="FONT-FAMILY: 宋体">以及其他标志符都封装在名字空间</span><span lang="EN-US">std</span><span style="FONT-FAMILY: 宋体">中。</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">的</span><span lang="EN-US">sort()</span><span style="FONT-FAMILY: 宋体">算法编译为</span><span lang="EN-US">std::sort()</span><span style="FONT-FAMILY: 宋体">，从而避免了名字冲突。</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span style="FONT-FAMILY: 宋体">尽管你的编译器可能没有实现名字空间，你仍然可以使用他们。为了使用</span><span lang="EN-US">STL</span><span style="FONT-FAMILY: 宋体">，可以将下面的指示符插入到你的源代码文件中，典型地是在所有的</span><span lang="EN-US">#include</span><span style="FONT-FAMILY: 宋体">指示符的后面：</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">&amp;nbsp;<p /></span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">using namespace std;</span></p><p class="MsoNormal" style="TEXT-INDENT: 21.75pt"><span lang="EN-US">&amp;nbsp;<p /></span></p><p class="MsoNormal"><span lang="EN-US">&amp;nbsp;<p /></span></p><p class="MsoNormal"><span lang="EN-US">&amp;nbsp;<p /></span></p><p class="MsoNormal"><span lang="EN-US">&amp;nbsp;<p /></span></p><h2><span style="FONT-FAMILY: 黑体">迭代器</span></h2><p style="TEXT-INDENT: 24pt"><span style="FONT-SIZE: 10.5pt">迭代器提供对一个容器中的对象的访问方法，并且定义了容器中对象的范围。迭代器就如同一个指针。事实上，<span lang="EN-US">C++的指针也是一种迭代器。但是，迭代器不仅仅是指针，因此你不能认为他们一定具有地址值。例如，一个数组索引，也可以认为是一种迭代器。<p /></span></span></p><p><span style="FONT-SIZE: 10.5pt">迭代器有各种不同的创建方法。程序可能把迭代器作为一个变量创建。一个<span lang="EN-US">STL 容器类可能为了使用一个特定类型的数据而创建一个迭代器。作为指针，必须能够使用*操作符类获取数据。你还可以使用其他数学操作符如++。典型的，++操作符用来递增迭代器，以访问容器中的下一个对象。如果迭代器到达了容器中的最后一个元素的后面，则迭代器变成past-the-end值。使用一个 past-the-end值得指针来访问对象是非法的，就好像使用NULL或为初始化的指针一样。<p /></span></span></p><p style="TEXT-INDENT: 21pt"><span style="FONT-SIZE: 10.5pt">提示<span lang="EN-US"><p /></span></span></p><p style="TEXT-INDENT: 21pt"><span lang="EN-US" style="FONT-SIZE: 10.5pt">STL不保证可以从另一个迭代器来抵达一个迭代器。例如，当对一个集合中的对象排序时，如果你在不同的结构中指定了两个迭代器，第二个迭代器无法从第一个迭代器抵达，此时程序注定要失败。这是STL灵活性的一个代价。<i>STL不保证检测毫无道理的错误。</i></span><span lang="EN-US" style="FONT-SIZE: 10pt"><p /></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">迭代器的类型</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">对于<span lang="EN-US">STL数据结构和算法，你可以使用五种迭代器。下面简要说明了这五种类型：<p /></span></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><i><span lang="EN-US" style="FONT-SIZE: 10pt">Input iterators</span></i><span lang="EN-US" style="FONT-SIZE: 10pt"> 提供对数据的只读访问。<p /></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><i><span lang="EN-US" style="FONT-SIZE: 10pt">Output iterators</span></i><span lang="EN-US" style="FONT-SIZE: 10pt"> 提供对数据的只写访问<p /></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><i><span lang="EN-US" style="FONT-SIZE: 10pt">Forward iterators</span></i><span lang="EN-US" style="FONT-SIZE: 10pt"> 提供读写操作，并能向前推进迭代器。<p /></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><i><span lang="EN-US" style="FONT-SIZE: 10pt">Bidirectional iterators</span></i><span style="FONT-SIZE: 10pt">提供读写操作，并能向前和向后操作。<span lang="EN-US"><p /></span></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><i><span lang="EN-US" style="FONT-SIZE: 10pt">Random access iterators</span></i><span style="FONT-SIZE: 10pt">提供读写操作，并能在数据中随机移动。<span lang="EN-US"><p /></span></span></p><p><span style="FONT-SIZE: 10pt">尽管各种不同的<span lang="EN-US">STL实现细节方面有所不同，还是可以将上面的迭代器想象为一种类继承关系。从这个意义上说，下面的迭代器继承自上面的迭代器。由于这种继承关系，你可以将一个Forward迭代器作为一个output或input迭代器使用。同样，如果一个算法要求是一个bidirectional 迭代器，那么只能使用该种类型和随机访问迭代器。 <p /></span></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">指针迭代器</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">正如下面的小程序显示的，一个指针也是一种迭代器。该程序同样显示了<span lang="EN-US">STL的一个主要特性——它不只是能够用于它自己的类类型，而且也能用于任何C或C++类型。<a href="http://www.csdn.net/editor/Editor.htm#Listing%201"><span style="FONT-SIZE: 12pt">Listing 1</span></a>, iterdemo.cpp, 显示了如何把指针作为迭代器用于STL的find()算法来搜索普通的数组。<p /></span></span></p><p class="MsoNormal"><b><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">表</span></b><b><span lang="EN-US" style="FONT-SIZE: 10pt"> 1. iterdemo.cpp</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><pre><span lang="EN-US">#include &amp;lt;iostream.h&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;algorithm&amp;gt;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">using namespace std;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">#define SIZE 100</span></pre><pre><span lang="EN-US">int iarray[SIZE];</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">int main()</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>iarray[20] = 50;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>int* ip = find(iarray, iarray + SIZE, 50);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>if (ip == iarray + SIZE)</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>cout &amp;lt;&amp;lt; &amp;quot;50 not found in array&amp;quot; &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>else</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>cout &amp;lt;&amp;lt; *ip &amp;lt;&amp;lt; &amp;quot; found in array&amp;quot; &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>return 0;</span></pre><pre><span lang="EN-US">}</span></pre><p><span style="FONT-SIZE: 10pt">在引用了<span lang="EN-US">I/O流库和STL算法头文件（注意没有.h后缀），该程序告诉编译器使用std名字空间。使用std名字空间的这行是可选的，因为可以删除该行对于这么一个小程序来说不会导致名字冲突。<p /></span></span></p><p><span style="FONT-SIZE: 10pt">程序中定义了尺寸为<span lang="EN-US">SIZE的全局数组。由于是全局变量，所以运行时数组自动初始化为零。下面的语句将在索引20位置处地元素设置为50,并使用find()算法来搜索值50: <p /></span></span></p><pre><span lang="EN-US">iarray[20] = 50;</span></pre><pre><span lang="EN-US">int* ip = find(iarray, iarray + SIZE, 50);</span></pre><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">find()函数接受三个参数。头两个定义了搜索的范围。由于C和C++数组等同于指针，表达式iarray指向数组的第一个元素。而第二个参数iarray + SIZE等同于</span></tt><i><span lang="EN-US" style="FONT-SIZE: 10pt">past-the-end</span></i><span lang="EN-US" style="FONT-SIZE: 10pt"> 值，也就是数组中最后一个元素的后面位置。第三个参数是待定位的值，也就是50。</span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">find()函数返回和前两个参数相同类型的迭代器，这儿是一个指向整数的指针ip。</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><p class="tiptitle"><b><span style="FONT-SIZE: 10pt">提示</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"><p /></span></p><p><span style="FONT-SIZE: 10pt">必须记住<span lang="EN-US">STL使用模板。因此，STL函数自动根据它们使用的数据类型来构造。<p /></span></span></p><p><span style="FONT-SIZE: 10pt">为了判断</span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">find()是否成功，例子中测试ip和</span></tt><span style="FONT-SIZE: 10pt"> <i><span lang="EN-US">past-the-end</span></i><span lang="EN-US"> 值是否相等： <p /></span></span></p><pre><span lang="EN-US">if (ip == iarray + SIZE) ...</span></pre><p><span style="FONT-SIZE: 10pt">如果表达式为真，则表示在搜索的范围内没有指定的值。否则就是指向一个合法对象的指针，这时可以用下面的语句显示：<span lang="EN-US">:<p /></span></span></p><pre><span lang="EN-US">cout &amp;lt;&amp;lt; *ip &amp;lt;&amp;lt; &amp;quot; found in array&amp;quot; &amp;lt;&amp;lt; endl;</span></pre><p><span style="FONT-SIZE: 10pt">测试函数返回值和<span lang="EN-US">NULL是否相等是不正确的。不要象下面这样使用：<p /></span></span></p><pre><span lang="EN-US">int* ip = find(iarray, iarray + SIZE, 50);</span></pre><pre><span lang="EN-US">if (ip != NULL) ...<span>&amp;nbsp; </span>// ??? incorrect</span></pre><p><span style="FONT-SIZE: 10pt">当使用<span lang="EN-US">STL函数时，只能测试ip是否和<i>past-the-end</i> 值是否相等。尽管在本例中ip是一个C++指针,其用法也必须符合STL迭代器的规则。<p /></span></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">容器迭代器</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">尽管<span lang="EN-US">C++指针也是迭代器，但用的更多的是容器迭代器。容器迭代器用法和iterdemo.cpp一样，但和将迭代器申明为指针变量不同的是，你可以使用容器类方法来获取迭代器对象。两个典型的容器类方法是begin()和end()。它们在大多数容器中表示整个容器范围。其他一些容器还使用rbegin()和rend()方法提供反向迭代器，以按反向顺序指定对象范围。<p /></span></span></p><p><span style="FONT-SIZE: 10pt">下面的程序创建了一个矢量容器（<span lang="EN-US">STL的和数组等价的对象），并使用迭代器在其中搜索。该程序和前一章中的程序相同。<p /></span></span></p><p class="MsoNormal"><b><span lang="EN-US" style="FONT-SIZE: 10pt">Listing 2. vectdemo.cpp</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><pre><span lang="EN-US">#include &amp;lt;iostream.h&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;algorithm&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;vector&amp;gt;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">using namespace std;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">vector&amp;lt;int&amp;gt; intVector(100);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">void main()</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>intVector[20] = 50;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>vector&amp;lt;int&amp;gt;::iterator intIter =</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>find(intVector.begin(), intVector.end(), 50);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>if (intIter != intVector.end())</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>cout &amp;lt;&amp;lt; &amp;quot;Vector contains value &amp;quot; &amp;lt;&amp;lt; *intIter &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>else</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>cout &amp;lt;&amp;lt; &amp;quot;Vector does not contain 50&amp;quot; &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><p><span style="FONT-SIZE: 10pt">注意用下面的方法显示搜索到的数据：<span lang="EN-US"><p /></span></span></p><pre><span lang="EN-US">cout &amp;lt;&amp;lt; &amp;quot;Vector contains value &amp;quot; &amp;lt;&amp;lt; *intIter &amp;lt;&amp;lt; endl;</span></pre><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">常量迭代器</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">和指针一样，你可以给一个迭代器赋值。例如，首先申明一个迭代器：<span lang="EN-US"><p /></span></span></p><pre><span lang="EN-US">vector&amp;lt;int&amp;gt;::iterator first;</span></pre><p><span style="FONT-SIZE: 10pt">该语句创建了一个<span lang="EN-US">vector&amp;lt;int&amp;gt;类的迭代器。下面的语句将该迭代器设置到intVector的第一个对象，并将它指向的对象值设置为123：:<p /></span></span></p><pre><span lang="EN-US">first = intVector.begin();</span></pre><pre><span lang="EN-US">*first = 123;</span></pre><p><span style="FONT-SIZE: 10pt">这种赋值对于大多数容器类都是允许的，除了只读变量。为了防止错误赋值，可以申明迭代器为：<span lang="EN-US"><p /></span></span></p><pre><span lang="EN-US">const vector&amp;lt;int&amp;gt;::iterator result;</span></pre><pre><span lang="EN-US">result = find(intVector.begin(), intVector.end(), value);</span></pre><pre><span lang="EN-US">if (result != intVector.end())</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>*result = 123;<span>&amp;nbsp; </span>// ???</span></pre><p class="warningtitle"><b><span style="FONT-SIZE: 10pt">警告</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"><p /></span></p><p><span style="FONT-SIZE: 10pt">另一种防止数据被改变得方法是将容器申明为<span lang="EN-US">const类型。<p /></span></span></p><p><span style="FONT-SIZE: 10pt">『呀！在<span lang="EN-US">VC中测试出错,正确的含义是result成为常量而不是它指向的对象不允许改变，如同int *const p;看来这作者自己也不懂』 <p /></span></span></p><h2><span style="FONT-FAMILY: 黑体">使用迭代器编程</span><span lang="EN-US"><p /></span></h2><p><span style="FONT-SIZE: 10pt">你已经见到了迭代器的一些例子，现在我们将关注每种特定的迭代器如何使用。由于使用迭代器需要关于<span lang="EN-US">STL容器类和算法的知识，在阅读了后面的两章后你可能需要重新复习一下本章内容。<p /></span></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">输入迭代器</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">输入迭代器是最普通的类型。输入迭代器至少能够使用<span lang="EN-US">==和!=测试是否相等；使用*来访问数据；使用++操作来递推迭代器到下一个元素或到达<i>past-the-end</i> 值。<p /></span></span></p><p><span style="FONT-SIZE: 10pt">为了理解迭代器和<span lang="EN-US">STL函数是如何使用它们的，现在来看一下find()模板函数的定义：<p /></span></span></p><pre><span lang="EN-US">template &amp;lt;class InputIterator, class T&amp;gt;</span></pre><pre><span lang="EN-US">InputIterator find(</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>InputIterator first, InputIterator last, const T&amp;amp; value) {</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>while (first != last &amp;amp;&amp;amp; *first != value) ++first;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>return first;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>}</span></pre><p><span lang="EN-US" style="FONT-SIZE: 10pt">&amp;nbsp;<p /></span></p><p class="normaltitle"><b><span style="FONT-SIZE: 10pt">注意</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"><p /></span></p><p><span style="FONT-SIZE: 10pt">在<span lang="EN-US">find()算法中，注意如果first和last指向不同的容器，该算法可能陷入死循环。<p /></span></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">输出迭代器</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">输出迭代器缺省只写，通常用于将数据从一个位置拷贝到另一个位置。由于输出迭代器无法读取对象，因此你不会在任何搜索和其他算法中使用它。要想读取一个拷贝的值，必须使用另一个输入迭代器（或它的继承迭代器）。<span lang="EN-US"><p /></span></span></p><p class="MsoNormal"><b><span lang="EN-US" style="FONT-SIZE: 10pt">Listing 3. outiter.cpp</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><pre><span lang="EN-US">#include &amp;lt;iostream.h&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;algorithm&amp;gt;<span>&amp;nbsp;&amp;nbsp; </span>// Need copy()</span></pre><pre><span lang="EN-US">#include &amp;lt;vector&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need vector</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">using namespace std;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">double darray[10] =</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>{1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9};</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">vector&amp;lt;double&amp;gt; vdouble(10);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">int main()</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>vector&amp;lt;double&amp;gt;::iterator outputIterator = vdouble.begin();</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>copy(darray, darray + 10, outputIterator);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>while (outputIterator != vdouble.end()) {</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>cout &amp;lt;&amp;lt; *outputIterator &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>outputIterator++;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>}</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>return 0;</span></pre><pre><span lang="EN-US">}</span></pre><p class="MsoNormal"><b><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">注意</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><p><span style="FONT-SIZE: 10pt">当使用<span lang="EN-US">copy()算法的时候，你必须确保目标容器有足够大的空间，或者容器本身是自动扩展的。<p /></span></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">前推迭代器</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">前推迭代器能够读写数据值，并能够向前推进到下一个值。但是没法递减。</span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">replace()算法显示了前推迭代器的使用方法。</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><pre><span lang="EN-US">template &amp;lt;class ForwardIterator, class T&amp;gt;</span></pre><pre><span lang="EN-US">void replace (ForwardIterator first,</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>ForwardIterator last,</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>const T&amp;amp; old_value,</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>const T&amp;amp; new_value);</span></pre><p><span style="FONT-SIZE: 10pt">使用</span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">replace()将[first,last]范围内的所有值为old_value的对象替换为new_value。</span></tt><i><span lang="EN-US" style="FONT-SIZE: 10pt">:</span></i><span lang="EN-US" style="FONT-SIZE: 10pt"><p /></span></p><pre><span lang="EN-US">replace(vdouble.begin(), vdouble.end(), 1.5, 3.14159);</span></pre><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">双向迭代器</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">双向迭代器要求能够增减。如<span lang="EN-US">reverse()算法要求两个双向迭代器作为参数:<p /></span></span></p><pre><span lang="EN-US">template &amp;lt;class BidirectionalIterator&amp;gt;</span></pre><pre><span lang="EN-US">void reverse (BidirectionalIterator first,</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>BidirectionalIterator last);</span></pre><p><span style="FONT-SIZE: 10pt">使用</span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">reverse()函数来对容器进行逆向排序</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt">:<p /></span></p><pre><span lang="EN-US">reverse(vdouble.begin(), vdouble.end());</span></pre><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">随机访问迭代器</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">随机访问迭代器能够以任意顺序访问数据，并能用于读写数据（不是<span lang="EN-US">const的C++指针也是随机访问迭代器）。STL的排序和搜索函数使用随机访问迭代器。随机访问迭代器可以使用关系操作符作比较。<p /></span></span></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">random_shuffle()</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"> 函数随机打乱原先的顺序。申明为：<p /></span></p><pre><span lang="EN-US">template &amp;lt;class RandomAccessIterator&amp;gt;</span></pre><pre><span lang="EN-US">void random_shuffle (RandomAccessIterator first,</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>RandomAccessIterator last);</span></pre><p><span style="FONT-SIZE: 10pt">使用方法：<span lang="EN-US"><p /></span></span></p><pre><span lang="EN-US">random_shuffle(vdouble.begin(), vdouble.end());</span></pre><h2><span style="FONT-FAMILY: 黑体">迭代器技术</span><span lang="EN-US"><p /></span></h2><p><span style="FONT-SIZE: 10pt">要学会使用迭代器和容器以及算法，需要学习下面的新技术。<span lang="EN-US"><p /></span></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">流和迭代器</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">本书的很多例子程序使用<span lang="EN-US">I/O流语句来读写数据。例如：<p /></span></span></p><pre><span lang="EN-US">int value;</span></pre><pre><span lang="EN-US">cout &amp;lt;&amp;lt; &amp;quot;Enter value: &amp;quot;;</span></pre><pre><span lang="EN-US">cin &amp;gt;&amp;gt; value;</span></pre><pre><span lang="EN-US">cout &amp;lt;&amp;lt; &amp;quot;You entered &amp;quot; &amp;lt;&amp;lt; value &amp;lt;&amp;lt; endl;</span></pre><p><span style="FONT-SIZE: 10pt">对于迭代器，有另一种方法使用流和标准函数。理解的要点是将输入<span lang="EN-US">/输出流作为容器看待。因此，任何接受迭代器参数的算法都可以和流一起工作。 <p /></span></span></p><p class="MsoNormal"><b><span lang="EN-US" style="FONT-SIZE: 10pt">Listing 4. outstrm.cpp</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><pre><span lang="EN-US">#include &amp;lt;iostream.h&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;stdlib.h&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need random(), srandom()</span></pre><pre><span lang="EN-US">#include &amp;lt;time.h&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need time()</span></pre><pre><span lang="EN-US">#include &amp;lt;algorithm&amp;gt;<span>&amp;nbsp;&amp;nbsp; </span>// Need sort(), copy()</span></pre><pre><span lang="EN-US">#include &amp;lt;vector&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need vector</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">using namespace std;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">void Display(vector&amp;lt;int&amp;gt;&amp;amp; v, const char* s);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">int main()</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>// Seed the random number generator</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>srandom( time(NULL) );</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>// Construct vector and fill with random integer values</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>vector&amp;lt;int&amp;gt; collection(10);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>for (int i = 0; i &amp;lt; 10; i++)</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>collection[i] = random() % 10000;;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>// Display, sort, and redisplay</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>Display(collection, &amp;quot;Before sorting&amp;quot;);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>sort(collection.begin(), collection.end());</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>Display(collection, &amp;quot;After sorting&amp;quot;);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>return 0;</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Display label s and contents of integer vector v</span></pre><pre><span lang="EN-US">void Display(vector&amp;lt;int&amp;gt;&amp;amp; v, const char* s)</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; endl &amp;lt;&amp;lt; s &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>copy(v.begin(), v.end(),</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>ostream_iterator&amp;lt;int&amp;gt;(cout, &amp;quot;\t&amp;quot;));</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">}</span></pre><p><span style="FONT-SIZE: 10pt">函数<span lang="EN-US">Display()显示了如何使用一个输出流迭代器。下面的语句将容器中的值传输到cout输出流对象中:<p /></span></span></p><pre><span lang="EN-US">copy(v.begin(), v.end(),</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>ostream_iterator&amp;lt;int&amp;gt;(cout, &amp;quot;\t&amp;quot;));</span></pre><p><span style="FONT-SIZE: 10pt">第三个参数实例化了<span lang="EN-US">ostream_iterator&amp;lt;int&amp;gt;类型，并将它作为copy()函数的输出目标迭代器对象。“\t”字符串是作为分隔符。运行结果：<p /></span></span></p><pre><span lang="EN-US">$ g++ outstrm.cpp</span></pre><pre><span lang="EN-US">$ ./a.out</span></pre><pre><span lang="EN-US">Before sorting</span></pre><pre><span lang="EN-US">677<span>&amp;nbsp;&amp;nbsp; </span>722<span>&amp;nbsp;&amp;nbsp; </span>686<span>&amp;nbsp;&amp;nbsp; </span>238<span>&amp;nbsp;&amp;nbsp; </span>964<span>&amp;nbsp;&amp;nbsp; </span>397<span>&amp;nbsp;&amp;nbsp; </span>251<span>&amp;nbsp;&amp;nbsp; </span>118<span>&amp;nbsp;&amp;nbsp; </span>11<span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>312</span></pre><pre><span lang="EN-US">After sorting</span></pre><pre><span lang="EN-US">11<span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>118<span>&amp;nbsp;&amp;nbsp; </span>238<span>&amp;nbsp;&amp;nbsp; </span>251<span>&amp;nbsp;&amp;nbsp; </span>312<span>&amp;nbsp;&amp;nbsp; </span>397<span>&amp;nbsp;&amp;nbsp; </span>677<span>&amp;nbsp;&amp;nbsp; </span>686<span>&amp;nbsp;&amp;nbsp; </span>722<span>&amp;nbsp;&amp;nbsp; </span>964</span></pre><p><span style="FONT-SIZE: 10pt">这是<span lang="EN-US">STL神奇的一面『确实神奇』。为定义输出流迭代器，STL提供了模板类</span></span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">ostream_iterator</span></tt><span style="FONT-SIZE: 10pt">。这个类的构造函数有两个参数：一个<span lang="EN-US">ostream对象和一个string值。因此可以象下面一样简单地创建一个迭代器对象：<p /></span></span></p><pre><span lang="EN-US">ostream_iterator&amp;lt;int&amp;gt;(cout, &amp;quot;\n&amp;quot;)</span></pre><p><span style="FONT-SIZE: 10pt">该迭代起可以和任何接受一个输出迭代器的函数一起使用。<span lang="EN-US"><p /></span></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">插入迭代器</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">插入迭代器用于将值插入到容器中。它们也叫做适配器，因为它们将容器适配或转化为一个迭代器，并用于<span lang="EN-US">copy()这样的算法中。例如，一个程序定义了一个链表和一个矢量容器:<p /></span></span></p><pre><span lang="EN-US">list&amp;lt;double&amp;gt; dList;</span></pre><pre><span lang="EN-US">vector&amp;lt;double&amp;gt; dVector;</span></pre><p><span style="FONT-SIZE: 10pt">通过使用<span lang="EN-US">front_inserter迭代器对象，可以只用单个copy()语句就完成将矢量中的对象插入到链表前端的操作：<p /></span></span></p><pre><span lang="EN-US">copy(dVector.begin(), dVector.end(), front_inserter(dList));</span></pre><p><span style="FONT-SIZE: 10pt">三种插入迭代器如下：<span lang="EN-US"><p /></span></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><i><span style="FONT-SIZE: 10pt">普通插入器</span></i><span style="FONT-SIZE: 10pt"> 将对象插入到容器任何对象的前面。<span lang="EN-US"><p /></span></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><i><span lang="EN-US" style="FONT-SIZE: 10pt">Front inserters</span></i><span lang="EN-US" style="FONT-SIZE: 10pt"> 将对象插入到数据集的前面——例如，链表表头。<p /></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><i><span lang="EN-US" style="FONT-SIZE: 10pt">Back inserters</span></i><span lang="EN-US" style="FONT-SIZE: 10pt"> 将对象插入到集合的尾部——例如，矢量的尾部，导致矢量容器扩展。<p /></span></p><p><span style="FONT-SIZE: 10pt">使用插入迭代器可能导致容器中的其他对象移动位置，因而使得现存的迭代器非法。例如，将一个对象插入到矢量容器将导致其他值移动位置以腾出空间。一般来说，插入到象链表这样的结构中更为有效，因为它们不会导致其他对象移动。<span lang="EN-US"><p /></span></span></p><p class="MsoNormal"><b><span lang="EN-US" style="FONT-SIZE: 10pt">Listing 5. insert.cpp</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><pre><span lang="EN-US">#include &amp;lt;iostream.h&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;algorithm&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;list&amp;gt;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">using namespace std;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">int iArray[5] = { 1, 2, 3, 4, 5 };</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">void Display(list&amp;lt;int&amp;gt;&amp;amp; v, const char* s);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">int main()</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>list&amp;lt;int&amp;gt; iList;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>// Copy iArray backwards into iList</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>copy(iArray, iArray + 5, front_inserter(iList));</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>Display(iList, &amp;quot;Before find and copy&amp;quot;);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>// Locate value 3 in iList</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>list&amp;lt;int&amp;gt;::iterator p =</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>find(iList.begin(), iList.end(), 3);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>// Copy first two iArray values to iList ahead of p</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>copy(iArray, iArray + 2, inserter(iList, p));</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>Display(iList, &amp;quot;After find and copy&amp;quot;);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>return 0;</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">void Display(list&amp;lt;int&amp;gt;&amp;amp; a, const char* s)</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; s &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>copy(a.begin(), a.end(),</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>ostream_iterator&amp;lt;int&amp;gt;(cout, &amp;quot; &amp;quot;));</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">}</span></pre><p><span style="FONT-SIZE: 10pt">运行结果如下：<span lang="EN-US"><p /></span></span></p><pre><span lang="EN-US">$ g++ insert.cpp</span></pre><pre><span lang="EN-US">$ ./a.out</span></pre><pre><span lang="EN-US">Before find and copy</span></pre><pre><span lang="EN-US">5 4 3 2 1</span></pre><pre><span lang="EN-US">After find and copy</span></pre><pre><span lang="EN-US">5 4 1 2 3 2 1</span></pre><p><span style="FONT-SIZE: 10pt">可以将<span lang="EN-US">front_inserter替换为back_inserter试试。<p /></span></span></p><p><span style="FONT-SIZE: 10pt">如果用<span lang="EN-US">find()去查找在列表中不存在的值，例如99。由于这时将p设置为<i>past-the-end</i> 值。最后的copy()函数将iArray的值附加到链表的后部。<p /></span></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">混合迭代器函数</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">在涉及到容器和算法的操作中，还有两个迭代器函数非常有用：<span lang="EN-US"><p /></span></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">advance()</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"> 按指定的数目增减迭代器。<p /></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">distance()</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"> 返回到达一个迭代器所需（递增）操作的数目。<p /></span></p><p><span style="FONT-SIZE: 10pt">例如：<span lang="EN-US"><p /></span></span></p><pre><span lang="EN-US">list&amp;lt;int&amp;gt; iList;</span></pre><pre><span lang="EN-US">list&amp;lt;int&amp;gt;::iterator p =</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>find(iList.begin(), iList.end(), 2);</span></pre><pre><span lang="EN-US">cout &amp;lt;&amp;lt; &amp;quot;before: p == &amp;quot; &amp;lt;&amp;lt; *p &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">advance(p, 2);<span>&amp;nbsp; </span>// same as p = p + 2;</span></pre><pre><span lang="EN-US">cout &amp;lt;&amp;lt; &amp;quot;after : p == &amp;quot; &amp;lt;&amp;lt; *p &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">int k = 0;</span></pre><pre><span lang="EN-US">distance(p, iList.end(), k);</span></pre><pre><span lang="EN-US">cout &amp;lt;&amp;lt; &amp;quot;k == &amp;quot; &amp;lt;&amp;lt; k &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">advance()函数接受两个参数。第二个参数是向前推进的数目。对于前推迭代器，该值必须为正，而对于双向迭代器和随机访问迭代器，该值可以为负。</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"><p /></span></p><pre>使用 <tt><span lang="EN-US"><font face="新宋体">distance()函数来返回到达另一个迭代器所需要的步骤。</font></span></tt></pre><p class="warningtitle"><b><span style="FONT-SIZE: 10pt">注意</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"><p /></span></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">distance()函数是迭代的，也就是说，它递增第三个参数。因此，你必须初始化该参数。未初始化该参数几乎注定要失败。</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"><p /></span></p><h2><span style="FONT-FAMILY: 黑体">函数和函数对象</span><span lang="EN-US"><p /></span></h2><p><span lang="EN-US" style="FONT-SIZE: 10pt">STL中，函数被称为算法，也就是说它们和标准C库函数相比，它们更为通用。STL算法通过重载operator()函数实现为模板类或模板函数。这些类用于创建函数对象，对容器中的数据进行各种各样的操作。下面的几节解释如何使用函数和函数对象。<p /></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">函数和断言</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">经常需要对容器中的数据进行用户自定义的操作。例如，你可能希望遍历一个容器中所有对象的<span lang="EN-US">STL算法能够回调自己的函数。例如<p /></span></span></p><pre><span lang="EN-US">#include &amp;lt;iostream.h&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;stdlib.h&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need random(), srandom()</span></pre><pre><span lang="EN-US">#include &amp;lt;time.h&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need time()</span></pre><pre><span lang="EN-US">#include &amp;lt;vector&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need vector</span></pre><pre><span lang="EN-US">#include &amp;lt;algorithm&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need for_each()</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">#define VSIZE 24<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Size of vector</span></pre><pre><span lang="EN-US">vector&amp;lt;long&amp;gt; v(VSIZE);<span>&amp;nbsp; </span>// Vector object</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Function prototypes</span></pre><pre><span lang="EN-US">void initialize(long &amp;amp;ri);</span></pre><pre><span lang="EN-US">void show(const long &amp;amp;ri);</span></pre><pre><span lang="EN-US">bool isMinus(const long &amp;amp;ri);<span>&amp;nbsp; </span>// Predicate function</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">int main()</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>srandom( time(NULL) );<span>&amp;nbsp; </span>// Seed random generator</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>for_each(v.begin(), v.end(), initialize);//调用普通函数</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; &amp;quot;Vector of signed long integers&amp;quot; &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>for_each(v.begin(), v.end(), show);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>// Use predicate function to count negative values</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>//</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>int count = 0;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>vector&amp;lt;long&amp;gt;::iterator p;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>p = find_if(v.begin(), v.end(), isMinus);//调用断言函数</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>while (p != v.end()) {</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>count++;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>p = find_if(p + 1, v.end(), isMinus);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>}</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; &amp;quot;Number of values: &amp;quot; &amp;lt;&amp;lt; VSIZE &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; &amp;quot;Negative values : &amp;quot; &amp;lt;&amp;lt; count &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>return 0;</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Set ri to a signed integer value</span></pre><pre><span lang="EN-US">void initialize(long &amp;amp;ri)</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>ri = ( random() - (RAND_MAX / 2) );</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>//<span>&amp;nbsp; </span>ri = random();</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Display value of ri</span></pre><pre><span lang="EN-US">void show(const long &amp;amp;ri)</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; ri &amp;lt;&amp;lt; &amp;quot;<span>&amp;nbsp; </span>&amp;quot;;</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Returns true if ri is less than 0</span></pre><pre><span lang="EN-US">bool isMinus(const long &amp;amp;ri)</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>return (ri &amp;lt; 0);</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><p><span style="FONT-SIZE: 10pt">所谓断言函数，就是返回<span lang="EN-US">bool值的函数。<p /></span></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">函数对象</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">除了给<span lang="EN-US">STL算法传递一个回调函数，你还可能需要传递一个类对象以便执行更复杂的操作。这样的一个对象就叫做函数对象。实际上函数对象就是一个类，但它和回调函数一样可以被回调。例如，在函数对象每次被for_each()或find_if()函数调用时可以保留统计信息。函数对象是通过重载operator()()实现的。如果 TanyClass定义了opeator()(),那么就可以这么使用：<p /></span></span></p><pre><span lang="EN-US">TAnyClass object;<span>&amp;nbsp; </span>// Construct object</span></pre><pre><span lang="EN-US">object();<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Calls TAnyClass::operator()() function</span></pre><pre><span lang="EN-US">for_each(v.begin(), v.end(), object);</span></pre><p><span lang="EN-US" style="FONT-SIZE: 10pt">STL定义了几个函数对象。由于它们是模板，所以能够用于任何类型，包括C/C++固有的数据类型，如long。有些函数对象从名字中就可以看出它的用途，如plus()和multiplies()。类似的greater()和less-equal()用于比较两个值。 <p /></span></p><p class="normaltitle"><b><span style="FONT-SIZE: 9pt">注意</span></b><span lang="EN-US" style="FONT-SIZE: 9pt"><p /></span></p><p><span style="FONT-SIZE: 10pt">有些版本的<span lang="EN-US">ANSI C++定义了times()函数对象，而GNU C++把它命名为multiplies()。使用时必须包含头文件</span></span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">&amp;lt;functional&amp;gt;。</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"><p /></span></p><p><span style="FONT-SIZE: 10pt">一个有用的函数对象的应用是</span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">accumulate()</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"> 算法。该函数计算容器中所有值的总和。记住这样的值不一定是简单的类型，通过重载operator+()，也可以是类对象。<p /></span></p><p class="MsoNormal"><b><span lang="EN-US" style="FONT-SIZE: 10pt">Listing 8. accum.cpp</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"> <span>&amp;nbsp;</span><p /></span></p><pre><span lang="EN-US">#include &amp;lt;iostream.h&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;numeric&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need accumulate()</span></pre><pre><span lang="EN-US">#include &amp;lt;vector&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need vector</span></pre><pre><span lang="EN-US">#include &amp;lt;functional&amp;gt;<span>&amp;nbsp;&amp;nbsp; </span>// Need multiplies() (or times())</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">#define MAX 10</span></pre><pre><span lang="EN-US">vector&amp;lt;long&amp;gt; v(MAX);<span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Vector object</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">int main()</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>// Fill vector using conventional loop</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>//</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>for (int i = 0; i &amp;lt; MAX; i++)</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>v[i] = i + 1;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>// Accumulate the sum of contained values</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>//</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>long sum =</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>accumulate(v.begin(), v.end(), 0);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; &amp;quot;Sum of values == &amp;quot; &amp;lt;&amp;lt; sum &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>// Accumulate the product of contained values</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>//</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>long product =</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>accumulate(v.begin(), v.end(), 1, multiplies&amp;lt;long&amp;gt;());//注意这行</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; &amp;quot;Product of values == &amp;quot; &amp;lt;&amp;lt; product &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>return 0;</span></pre><pre><span lang="EN-US">}</span></pre><p><span style="FONT-SIZE: 10pt">编译输出如下：<span lang="EN-US"><p /></span></span></p><pre><span lang="EN-US">$ g++ accum.cpp</span></pre><pre><span lang="EN-US">$ ./a.out</span></pre><pre><span lang="EN-US">Sum of values == 55</span></pre><pre><span lang="EN-US">Product of values == 3628800</span></pre><p><tt><span style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">『注意使用了函数对象的<span lang="EN-US">accumulate()的用法。accumulate() 在内部将每个容器中的对象和第三个参数作为multiplies函数对象的参数,multiplies(1,v)计算乘积。VC中的这些模板的源代码如下：<p /></span></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// TEMPLATE FUNCTION accumulate<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">template&amp;lt;class _II, class _Ty&amp;gt; inline<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>_Ty accumulate(_II _F, _II _L, _Ty _V)<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>{for (; _F != _L; ++_F)<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>_V = _V + *_F;<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>return (_V); }<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// TEMPLATE FUNCTION accumulate WITH BINOP<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">template&amp;lt;class _II, class _Ty, class _Bop&amp;gt; inline<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>_Ty accumulate(_II _F, _II _L, _Ty _V, _Bop _B)<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>{for (; _F != _L; ++_F)<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>_V = _B(_V, *_F);<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>return (_V); }<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// TEMPLATE STRUCT binary_function<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">template&amp;lt;class _A1, class _A2, class _R&amp;gt;<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>struct binary_function {<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>typedef _A1 first_argument_type;<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>typedef _A2 second_argument_type;<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>typedef _R result_type;<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>};<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// TEMPLATE STRUCT multiplies<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">template&amp;lt;class _Ty&amp;gt;<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>struct multiplies : binary_function&amp;lt;_Ty, _Ty, _Ty&amp;gt; {<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>_Ty operator()(const _Ty&amp;amp; _X, const _Ty&amp;amp; _Y) const<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>{return (_X * _Y); }<p /></span></tt></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>};<p /></span></tt></p><p><tt><span style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">引言：如果你想深入了解<span lang="EN-US">STL到底是怎么实现的，最好的办法是写个简单的程序，将程序中涉及到的模板源码给copy下来，稍作整理，就能看懂了。所以没有必要去买什么《STL源码剖析》之类的书籍，那些书可能反而浪费时间。』</span></span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"><p /></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">发生器函数对象</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">有一类有用的函数对象是“发生器”<span lang="EN-US">(generator)。这类函数有自己的内存，也就是说它能够从先前的调用中记住一个值。例如随机数发生器函数。<p /></span></span></p><p><span style="FONT-SIZE: 10pt">普通的<span lang="EN-US">C程序员使用静态或全局变量 “记忆”上次调用的结果。但这样做的缺点是该函数无法和它的数据相分离『还有个缺点是要用TLS才能线程安全』。显然，使用类来封装一块：“内存”更安全可靠。先看一下例子：<p /></span></span></p><p class="MsoNormal"><b><span lang="EN-US" style="FONT-SIZE: 10pt">Listing 9. randfunc.cpp</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><pre><span lang="EN-US">#include &amp;lt;iostream.h&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;stdlib.h&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need random(), srandom()</span></pre><pre><span lang="EN-US">#include &amp;lt;time.h&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need time()</span></pre><pre><span lang="EN-US">#include &amp;lt;algorithm&amp;gt;<span>&amp;nbsp;&amp;nbsp; </span>// Need random_shuffle()</span></pre><pre><span lang="EN-US">#include &amp;lt;vector&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need vector</span></pre><pre><span lang="EN-US">#include &amp;lt;functional&amp;gt;<span>&amp;nbsp; </span>// Need ptr_fun()</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">using namespace std;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Data to randomize</span></pre><pre><span lang="EN-US">int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};</span></pre><pre><span lang="EN-US">vector&amp;lt;int&amp;gt; v(iarray, iarray + 10);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Function prototypes</span></pre><pre><span lang="EN-US">void Display(vector&amp;lt;int&amp;gt;&amp;amp; vr, const char *s);</span></pre><pre><span lang="EN-US">unsigned int RandInt(const unsigned int n);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">int main()</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>srandom( time(NULL) );<span>&amp;nbsp; </span>// Seed random generator</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>Display(v, &amp;quot;Before shuffle:&amp;quot;);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>pointer_to_unary_function&amp;lt;unsigned int, unsigned int&amp;gt;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>ptr_RandInt = ptr_fun(RandInt);<span>&amp;nbsp; </span>// Pointer to RandInt()//注意这行</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>random_shuffle(v.begin(), v.end(), ptr_RandInt);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>Display(v, &amp;quot;After shuffle:&amp;quot;);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>return 0;</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Display contents of vector vr</span></pre><pre><span lang="EN-US">void Display(vector&amp;lt;int&amp;gt;&amp;amp; vr, const char *s)</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; endl &amp;lt;&amp;lt; s &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>copy(vr.begin(), vr.end(), ostream_iterator&amp;lt;int&amp;gt;(cout, &amp;quot; &amp;quot;));</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Return next random value in sequence modulo n</span></pre><pre><span lang="EN-US">unsigned int RandInt(const unsigned int n)</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>return random() % n;</span></pre><pre><span lang="EN-US">}</span></pre><p><span style="FONT-SIZE: 10pt">编译运行结果如下：<span lang="EN-US"><p /></span></span></p><pre><span lang="EN-US">$ g++ randfunc.cpp</span></pre><pre><span lang="EN-US">$ ./a.out</span></pre><pre><span lang="EN-US">Before shuffle:</span></pre><pre><span lang="EN-US">1 2 3 4 5 6 7 8 9 10</span></pre><pre><span lang="EN-US">After shuffle:</span></pre><pre><span lang="EN-US">6 7 2 8 3 5 10 1 9 4</span></pre><p><span style="FONT-SIZE: 10pt">首先用下面的语句申明一个对象：<span lang="EN-US"><p /></span></span></p><pre><span lang="EN-US">pointer_to_unary_function&amp;lt;unsigned int, unsigned int&amp;gt;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>ptr_RandInt = ptr_fun(RandInt);</span></pre><p><span style="FONT-SIZE: 10pt">这儿使用<span lang="EN-US">STL的单目函数模板定义了一个变量ptr_RandInt，并将地址初始化到我们的函数RandInt()。单目函数接受一个参数，并返回一个值。现在random_shuffle()可以如下调用：<p /></span></span></p><pre><span lang="EN-US">random_shuffle(v.begin(), v.end(), ptr_RandInt);</span></pre><pre>在本例子中，发生器只是简单的调用<span lang="EN-US">rand()函数。</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><p class="MsoNormal"><b><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">关于常量引用的一点小麻烦（不翻译了，</span></b><b><span lang="EN-US" style="FONT-SIZE: 10pt">VC</span></b><b><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">下将例子中的</span></b><b><span lang="EN-US" style="FONT-SIZE: 10pt">const</span></b><b><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">去掉）</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">发生器函数类对象</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">下面的例子说明发生器函数类对象的使用。<span lang="EN-US"><p /></span></span></p><p class="MsoNormal"><b><span lang="EN-US" style="FONT-SIZE: 10pt">Listing 10. fiborand.cpp</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><pre><span lang="EN-US">#include &amp;lt;iostream.h&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;algorithm&amp;gt;<span>&amp;nbsp;&amp;nbsp; </span>// Need random_shuffle()</span></pre><pre><span lang="EN-US">#include &amp;lt;vector&amp;gt;<span>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>// Need vector</span></pre><pre><span lang="EN-US">#include &amp;lt;functional&amp;gt;<span>&amp;nbsp; </span>// Need unary_function</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">using namespace std;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Data to randomize</span></pre><pre><span lang="EN-US">int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};</span></pre><pre><span lang="EN-US">vector&amp;lt;int&amp;gt; v(iarray, iarray + 10);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Function prototype</span></pre><pre><span lang="EN-US">void Display(vector&amp;lt;int&amp;gt;&amp;amp; vr, const char *s);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// The FiboRand template function-object class</span></pre><pre><span lang="EN-US">template &amp;lt;class Arg&amp;gt;</span></pre><pre><span lang="EN-US">class FiboRand : public unary_function&amp;lt;Arg, Arg&amp;gt; {</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>int i, j;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>Arg sequence[18];</span></pre><pre><span lang="EN-US">public:</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>FiboRand();</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>Arg operator()(const Arg&amp;amp; arg);</span></pre><pre><span lang="EN-US">};</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">void main()</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>FiboRand&amp;lt;int&amp;gt; fibogen;<span>&amp;nbsp; </span>// Construct generator object</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; &amp;quot;Fibonacci random number generator&amp;quot; &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; &amp;quot;using random_shuffle and a function object&amp;quot; &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>Display(v, &amp;quot;Before shuffle:&amp;quot;);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>random_shuffle(v.begin(), v.end(), fibogen);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>Display(v, &amp;quot;After shuffle:&amp;quot;);</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Display contents of vector vr</span></pre><pre><span lang="EN-US">void Display(vector&amp;lt;int&amp;gt;&amp;amp; vr, const char *s)</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; endl &amp;lt;&amp;lt; s &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>copy(vr.begin(), vr.end(),</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>ostream_iterator&amp;lt;int&amp;gt;(cout, &amp;quot; &amp;quot;));</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// FiboRand class constructor</span></pre><pre><span lang="EN-US">template&amp;lt;class Arg&amp;gt;</span></pre><pre><span lang="EN-US">FiboRand&amp;lt;Arg&amp;gt;::FiboRand()</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>sequence[17] = 1;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>sequence[16] = 2;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>for (int n = 15; n &amp;gt; 0; n</span><span lang="EN-US" style="FONT-FAMILY: 'Courier New'">—</span><span lang="EN-US">)</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>sequence[n] = sequence[n + 1] + sequence[n + 2];</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>i = 17;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>j = 5;</span></pre><pre><span lang="EN-US">}</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// FiboRand class function operator</span></pre><pre><span lang="EN-US">template&amp;lt;class Arg&amp;gt;</span></pre><pre><span lang="EN-US">Arg FiboRand&amp;lt;Arg&amp;gt;::operator()(const Arg&amp;amp; arg)</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>Arg k = sequence[i] + sequence[j];</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>sequence[i] = k;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>i--;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>j--;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>if (i == 0) i = 17;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>if (j == 0) j = 17;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>return k % arg;</span></pre><pre><span lang="EN-US">}</span></pre><p><span style="FONT-SIZE: 10pt">编译运行输出如下<span lang="EN-US">:<p /></span></span></p><pre><span lang="EN-US">$ g++ fiborand.cpp</span></pre><pre><span lang="EN-US">$ ./a.out</span></pre><pre><span lang="EN-US">Fibonacci random number generator</span></pre><pre><span lang="EN-US">using random_shuffle and a function object</span></pre><pre><span lang="EN-US">Before shuffle:</span></pre><pre><span lang="EN-US">1 2 3 4 5 6 7 8 9 10</span></pre><pre><span lang="EN-US">After shuffle:</span></pre><pre><span lang="EN-US">6 8 5 4 3 7 10 1 9</span></pre><p><span style="FONT-SIZE: 10pt">该程序用完全不通的方法使用使用<span lang="EN-US">rand_shuffle。Fibonacci 发生器封装在一个类中，该类能从先前的“使用”中记忆运行结果。在本例中，类</span></span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">FiboRand</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"> 维护了一个数组和两个索引变量I和j。<p /></span></p><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">FiboRand类继承自unary_function()</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"> 模板:<p /></span></p><pre><span lang="EN-US">template &amp;lt;class Arg&amp;gt;</span></pre><pre><span lang="EN-US">class FiboRand : public unary_function&amp;lt;Arg, Arg&amp;gt; {...</span></pre><p><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">Arg是用户自定义数据类型。该类还定以了两个成员函数，一个是构造函数，另一个是operator()（）函数，该操作符允许random_shuffle()算法象一个函数一样“调用”一个FiboRand对象。</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">绑定器函数对象</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">一个</span><span style="FONT-SIZE: 10.5pt">绑</span><span style="FONT-SIZE: 10pt">定器使用另一个函数对象<span lang="EN-US">f()和参数值V创建一个函数对象。被绑定函数对象必须为双目函数，也就是说有两个参数,A和B。STL 中的帮定器有：<p /></span></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">bind1st()</span></tt><span lang="EN-US" style="FONT-SIZE: 10pt"> 创建一个函数对象，该函数对象将值V作为第一个参数A。<p /></span></p><p style="MARGIN-LEFT: 36pt; TEXT-INDENT: -18pt"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Symbol">·<span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; </span></span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">bind2nd()</span></tt><span style="FONT-SIZE: 10pt">创建一个函数对象，该函数对象将值<span lang="EN-US">V作为第二个参数B。<p /></span></span></p><p><span style="FONT-SIZE: 10pt">举例如下：<span lang="EN-US"><p /></span></span></p><p class="MsoNormal"><b><span lang="EN-US" style="FONT-SIZE: 10pt">Listing 11. binder.cpp</span></b><span lang="EN-US" style="FONT-SIZE: 10pt"> <p /></span></p><pre><span lang="EN-US">#include &amp;lt;iostream.h&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;algorithm&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;functional&amp;gt;</span></pre><pre><span lang="EN-US">#include &amp;lt;list&amp;gt;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">using namespace std;</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">// Data</span></pre><pre><span lang="EN-US">int iarray[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};</span></pre><pre><span lang="EN-US">list&amp;lt;int&amp;gt; aList(iarray, iarray + 10);</span></pre><pre><span lang="EN-US">&amp;nbsp;<p /></span></pre><pre><span lang="EN-US">int main()</span></pre><pre><span lang="EN-US">{</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>int k = 0;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>count_if(aList.begin(), aList.end(),</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>bind1st(greater&amp;lt;int&amp;gt;(), 8), k);</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>cout &amp;lt;&amp;lt; &amp;quot;Number elements &amp;lt; 8 == &amp;quot; &amp;lt;&amp;lt; k &amp;lt;&amp;lt; endl;</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>return 0;</span></pre><pre><span lang="EN-US">}</span></pre><p><span lang="EN-US" style="FONT-SIZE: 10pt">Algorithm </span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">count_if()计算满足特定条件的元素的数目。</span></tt><span style="FONT-SIZE: 10pt"> 这是通过将一个函数对象和一个参数捆绑到为一个对象，并将该对象作为算法的第三个参数实现的。 注意这个表达式<span lang="EN-US">:<p /></span></span></p><pre><span lang="EN-US">bind1st(greater&amp;lt;int&amp;gt;(), 8)</span></pre><p><span style="FONT-SIZE: 10pt">该表达式将<span lang="EN-US">greater&amp;lt;int&amp;gt;()和一个参数值8捆绑为一个函数对象。由于使用了bind1st()，所以该函数相当于计算下述表达式： <p /></span></span></p><pre><span lang="EN-US">8 &amp;gt; q</span></pre><p><span style="FONT-SIZE: 10pt">表达式中的<span lang="EN-US">q是容器中的对象。因此，完整的表达式<p /></span></span></p><pre><span lang="EN-US">count_if(aList.begin(), aList.end(),</span></pre><pre><span lang="EN-US"><span>&amp;nbsp; </span>bind1st(greater&amp;lt;int&amp;gt;(), 8), k);</span></pre><p><span style="FONT-SIZE: 10pt">计算所有小于或等于<span lang="EN-US">8的对象的数目。 <p /></span></span></p><h3><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">否定函数对象</span><span lang="EN-US" style="FONT-SIZE: 10.5pt"><p /></span></h3><p><span style="FONT-SIZE: 10pt">所谓否定<span lang="EN-US">(negator)函数对象，就是它从另一个函数对象创建而来，如果原先的函数返回真，则否定函数对象返回假。有两个否定函数对象：not1()和not2()。</span></span><tt><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 黑体">not1()接受单目函数对象，not2()接受双目函数对象。否定函数对象通常和帮定器一起使用。例如，上节中用bind1nd来搜索q&amp;lt;=8的值：<p /></span></tt></p><pre><span lang="EN-US"><span>&amp;nbsp; </span>count_if(aList.begin(), aList.end(),</span></pre><pre><span lang="EN-US"><span>&amp;nbsp;&amp;nbsp;&amp;nbsp; </span>bind1st(greater&amp;lt;int&amp;gt;(), 8), k);</span></pre><p><span style="FONT-SIZE: 10pt">如果要搜索<span lang="EN-US">q&amp;gt;8的对象，则用bind2st。而现在可以这样写：<p /></span></span></p><pre><span lang="EN-US">start = find_if(aList.begin(), aList.end(),<br />
<span>&amp;nbsp; </span>not1(bind1nd(greater&amp;lt;int&amp;gt;(), 6)));</span></pre><p><span style="FONT-SIZE: 10pt">你必须使用<span lang="EN-US">not1，因为bind1nd返回单目函数。 <p /></span></span></p><h2><span style="FONT-FAMILY: 黑体">总结：使用标准模板库</span><span lang="EN-US"> (STL)</span><span lang="EN-US"><p /></span></h2><p><span style="FONT-SIZE: 10pt">尽管很多程序员仍然在使用标准<span lang="EN-US">C函数，但是这就好像骑着毛驴寻找Mercedes一样。你当然最终也会到达目标，但是你浪费了很多时间。<p /></span></span></p><p><span style="FONT-SIZE: 10pt">尽管有时候使用标准<span lang="EN-US">C函数确实方便(如使用sprintf()进行格式化输出)。但是C函数不使用异常机制来报告错误，也不适合处理新的数据类型。而且标准C函数经常使用内存分配技术，没有经验的程序员很容易写出bug来。.<p /></span></span></p><p><span lang="EN-US" style="FONT-SIZE: 10pt">C++标准库则提供了更为安全，更为灵活的数据集处理方式。STL最初由HP实验室的Alexander Stepanov和Meng Lee开发。最近，C++标准委员会采纳了STL，尽管在不同的实现之间仍有细节差别。<p /></span></p><p><span lang="EN-US" style="FONT-SIZE: 10pt">STL的最主要的两个特点：数据结构和算法的分离，非面向对象本质。访问对象是通过象指针一样的迭代器实现的；容器是象链表，矢量之类的数据结构，并按模板方式提供；算法是函数模板，用于操作容器中的数据。由于STL以模板为基础，所以能用于任何数据类型和结构。</span></p></span>]]></description> 
<guid isPermaLink="false">6069682@http://http88.bokee.com/</guid> 
<dc:subject>项目开发经验</dc:subject> 
<dc:date>2007-01-26T17:06:37Z</dc:date> 
</item> 
<item> 
<title><![CDATA[ Visual C++ .NET编程：托管C++概述]]></title> 
<link>http://http88.bokee.com/6069544.html</link> 
<description><![CDATA[<p>2000年6月，Microsoft推出了&amp;quot;Microsoft.NET下一代互联网软件和服务战略&amp;quot;，引起IT行业的广泛关注。2000年9月， Microsoft在旧金山发布了Enterprise 2000。同月，Microsoft原总裁兼首席执行官鲍尔默来到中国就&amp;quot;下一代互联网&amp;quot;的主题进行演讲，在中国掀起了一股&amp;quot;.NET旋风&amp;quot;。2000年 11月，Microsoft在Comdex计算机大展上发布了Visual Studio.NET软件，并展示了其.NET发展战略的框架体系和开发工具的相关特性，全面加速了Microsoft以.NET技术进军市场的步伐。 <br /><br />　　Microsoft的.NET战略意味着：Microsoft以及在Microsoft平台上的开发者将会制造服务，而不是制造软件。在未来几年之内，Microsoft将陆续发布有关.NET的平台和工具，用于在因特网上开发Web服务。那时，工作在.NET上的用户、开发人员和 IT工作人员都不再购买软件、安装软件和维护软件。取而代之的是，他们将定制服务，软件会自动安装，所有的维护和升级也会通过互联网进行。 &amp;quot;Microsoft.NET 代表了一个集合、一个环境、一个可以作为平台支持下一代Internet的可编程结构。&amp;quot;这就是鲍尔默对.NET的描述。<br /><br />　　作为.NET的最新特性组成部分，Microsoft .NET Framework是一个用于构建，部署和运行Web服务及应用程序的平台。它为将现有投资与下一代应用程序和服务的集成提供了高产的，基于标准的，多语言环境，同时它还用于解决Internet级应用程序的部署和操作问题。.NET框架包含三个主要部分：通用语言运行时，一组层次化的统一的类库，及组件化版本的动态服务器主页(称为ASP.NET)。<br /><br />　　用于开发.NET Framework的语言有Visual C#、VB.NET和C++托管扩展(Managed Extensions for C++)。其中C#是开发.NET的元语言，而C++托管扩展是在C++基础上建立起来的，用来为Visual C++程序员开发.NET框架应用程序而设计。为叙述方便，我们将C++托管扩展就称之为&amp;quot;托管C++&amp;quot;。<br />为了帮助C/C++以及Visual C++程序员或爱好者快速使用托管C++开发.NET Framework程序，我们将陆续推出相关的一系列文章。<br /><br />　　本篇&amp;quot;托管C++概述&amp;quot;主要讲述了什么是托管C++、开发.NET Framework(框架)的项目类型以及与标准C++之间的区别。<br /><br />　　<strong><font color="#ac0000">1、什么是托管C++？</font></strong><br /><br />　　在回答这个问题，首先要搞清楚什么是&amp;quot;托管&amp;quot;(Managed)。托管是.NET的一个专门概念，它是融于通用语言运行时(CLR)中的一种新的编程理念，因此我们完全可以把&amp;quot;托管&amp;quot;视为&amp;quot;.NET&amp;quot;。那么什么是&amp;quot;通用语言运行时&amp;quot;？通用语言运行时是.NET 框架应用程序的执行引挚。它提供了许多服务，其中包括：代码管理(装入和执行)、类型安全性验证、元数据(高级类型信息)访问、为管理对象管理内存、管理代码，COM对象和预生成的DLLs(非管理代码和数据)的交互操作性、对开发人员服务的支持等等。<br /><br />　　也就是说，使用托管C++意味着，我们的代码可以被CLR所管理，并能开发出具有最新特性如垃圾自动收集、程序间相互访问等的.NET框架应用程序。<br /><br />　　由托管概念所引发的C++应用程序包括托管代码、托管数据和托管类三个组成部分。　　<br /><br />　　(1) <strong>托管代码</strong>：. Net环境提供了许多核心的运行(RUNTIME)服务，比如异常处理和安全策略。为了能使用这些服务，必须要给运行环境提供一些信息代码(元数据)，这种代码就是托管代码。所有的C#、VB.NET、JScript.NET默认时都是托管的，但Visual C++默认时不是托管的，必须在编译器中使用命令行选项(/CLR)才能产生托管代码。<br /><br />　　(2) <strong>托管数据</strong>：与托管代码密切相关的是托管数据。托管数据是由公共语言运行的垃圾回收器进行分配和释放的数据。默认情况下，C#、Visual Basic 和 JScript.NET 数据是托管数据。不过，通过使用特殊的关键字，C# 数据可以被标记为非托管数据。Visual C++数据在默认情况下是非托管数据，即使在使用 /CLR 开关时也不是托管的。<br /><br />　　(3)<strong> 托管类</strong>：尽管Visual C++数据在默认情况下是非托管数据，但是在使用C++的托管扩展时，可以使用&amp;quot;__gc&amp;quot;关键字将类标记为托管类。就像该名称所显示的那样，它表示类实例的内存由垃圾回收器管理。另外，一个托管类也完全可以成为 .NET 框架的成员，由此可以带来的好处是，它可以与其他语言编写的类正确地进行相互操作，如托管的C++类可以从Visual Basic类继承等。但同时也有一些限制，如托管类只能从一个基类继承等。需要说明的是，在托管C++应用程序中既可使用托管类也可以使用非托管类。这里的非托管类不是指标准C++类，而是使用托管C++语言中的__nogc关键字的类。</p><strong><font color="#ac0000">2、用托管C++可以开发.NET框架的项目类型<br /><br /></font></strong>　　使用托管C++应该是C++程序员编写.NET框架应用程序最好的一种选择，通过集成在Visual Studio.NET开发环境的托管C++向导，我们可以创建以下几种开发.NET框架的项目类型：<br /><br />　　(1) 托管C++应用程序:用来创建一个支持托管扩展的单独C++应用程序，使用它还可创建任何类型的应用程序，包括.NET框架客户应用程序。<br /><br />　　(2) 托管C++类库:用来创建一个支持托管扩展的C++DLL，使用它可以生成一个能被.NET框架应用程序调用的托管类型的组件。<br /><br />　　(3) 托管C++空项目：用来创建一个空的托管项目，该项目只含有支持托管扩展的正确编译和链接的开关选项。使用它能将一个已有的C++源文件进入到一个托管环境中。<br /><br />　　(4) 托管C++ Web服务：用于创建两个项目，一个是C++托管扩展项目，另一个是部署项目。<br /><br />　　<strong><font color="#ac0000">3、托管C++与标准C++的主要区别</font></strong><br /><br />　　尽管托管C++是从标准C++建立而来的，但它与标准C++有着本质上的区别，这主要体现在以下几个方面：<br /><br />　　<strong>(1) 广泛采用&amp;quot;名称空间&amp;quot;(namespace)</strong><br /><br />　　名称空间是类型的一种逻辑命名方案，.NET使用该命名方案用于将类型按相关功能的逻辑类别进行分组，利用名称空间可以使开发人员更容易在代码中浏览和引用类型。当然，我们也可将名称空间理解成是一个&amp;quot;类库名&amp;quot;。 <br /><br />　　尽管很早Microsoft就在Visual C++中支持名称空间的编程方式，但是很少引起Visual C++程序员的普遍关注。现在在托管C++程序中，我们必须使用这一方式，即使用#using和using关键字。例如下面的简单程序代码是在控制台上输出&amp;quot;Hello World&amp;quot;：<br /><br /><table width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td><p>#using <br />using namespace System;</p><p><br />int main(void)<br />{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; Console::WriteLine(S&amp;quot;Hello World&amp;quot;);<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; return 0;<br />}</p></td></tr></tbody></table><br />　　代码中，#using是用来将一个元数据文件输入到托管C++程序中，这些文件可以是包含托管数据和结构的MSIL (Microsoft intermediate language，微软中间语言)文件，如DLL、EXE、OBJ文件等。mscorlib.dll是.NET框架的一个核心类库，包含主要的名称空间 System。程序的第二行代码&amp;quot;using namespace System;&amp;quot;用来使用System名称空间。System是.NET框架根名称空间，包含最基本的类型，如用于数据流的输入/输出的System:: IO等。<br /><br />　　在对托管C++程序开发的不断深入，我们不久就会发现，许多类型的引用都要在程序的前面使用#using和using来进行。<br /><br />　　<strong>(2) 基本数据类型的变化</strong><br /><br />　　我们知道，标准C++语言的数据类型是非常丰富的。而托管C++的数据类型更加丰富，不仅包含了标准C++中的数据类型，而且新增了__int64 (64位整型)、Decimal(96位十进制数)、String*(字符串类型)和Object*(对象类型)等类型，表1-1列出它们各自数据类型。<br /><br /><table cellspacing="0" width="600" align="center" border="1"><tbody><tr><td width="139" height="19">类型描述</td><td width="203" height="19">标准C++类型名</td><td width="170" height="19">托管C++类型名</td><td width="70" height="19">长度(位)</td></tr><tr><td width="139">布尔型</td><td width="203">bool</td><td width="170">bool</td><td width="70">8</td></tr><tr><td width="139" height="19">字符型</td><td width="203" height="19">char</td><td width="170" height="19">signed char</td><td width="70" height="19">8</td></tr><tr><td width="139">无符号字符型</td><td width="203">unsigned char</td><td width="170">char</td><td width="70">8</td></tr><tr><td width="139">短整型</td><td width="203">short [int]</td><td width="170">short</td><td width="70">16</td></tr><tr><td width="139">无符号短整型</td><td width="203">unsigned short [int]</td><td width="170">unsigned short</td><td width="70">16</td></tr><tr><td width="139">整型</td><td width="203">int</td><td width="170">int 或 long</td><td width="70">32</td></tr><tr><td width="139">无符号整型</td><td width="203">unsigned [int]</td><td width="170">unsigned int 或 long</td><td width="70">32</td></tr><tr><td width="139">长整型</td><td width="203">long [int]</td><td width="170">long</td><td width="70">32</td></tr><tr><td width="139">无符号长整型</td><td width="203">unsigned long [int]</td><td width="170">unsigned long</td><td width="70">32</td></tr><tr><td width="139">单精度浮点型</td><td width="203">float</td><td width="170">float</td><td width="70">32</td></tr><tr><td width="139">双精度浮点型</td><td width="203">double</td><td width="170">double</td><td width="70">64</td></tr><tr><td width="139">长双精度浮点型</td><td width="203">long double</td><td width="170">--</td><td width="70">64</td></tr><tr><td width="139">Unicode字符</td><td width="203">--</td><td width="170">wchar_t</td><td width="70">16</td></tr><tr><td width="139">64位整型</td><td width="203">--</td><td width="170">__int64</td><td width="70">64</td></tr><tr><td width="139">无符号64位整型</td><td width="203">--</td><td width="170">unsigned __int64</td><td width="70">64</td></tr><tr><td width="139">96位十进制值</td><td width="203">--</td><td width="170">Decimal</td><td width="70">96</td></tr><tr><td width="139">对象类型</td><td width="203">--</td><td width="170">Object*</td><td width="70">32</td></tr><tr><td width="139">字符串类型</td><td width="203">--</td><td width="170">String*</td><td width="70">--</td></tr></tbody></table><br /><br />　　需要注意的是，String和Object在定义一个变量时，注意要有星号(&amp;quot;*&amp;quot;)，但这个变量不是指针变量，这与标准C++的含义是不一样的。例如上面的代码可以改为：<br /><br /><table width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td>#using <br />using namespace System;<br />int main(void)<br />{<br />String* hello = S&amp;quot;Hello World&amp;quot;;<br />Console::WriteLine(hello);<br />return 0;<br />}</td></tr></tbody></table><strong>(3) 新增三个托管C++类型：__gc class、__value class和__gc interface<br /><br /></strong>　　一个__gc类或结构意味着该类或结构的生命周期是由.NET开发平台自动管理及垃圾自动收集，用户不必自已去调用delete来删除。定义一个__gc类或结构和标准C++基本相似，所不同的是在class或struct前加上__gc，例如下面的代码：<br /><br /><table width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td><p>__gc class G </p><p>{<br />public:<br />int k;<br />int sum(int);<br />};<br /><br />G::sum(int i) </p><p>{</p><p>return i*(i + 1)/2;</p><p>}</p><p><br />int main()<br />{<br />G * g = new G;<br />Console::WriteLine(g-&amp;gt;sum(4)); // 结果输出10<br />return 0;<br />}</p></td></tr></tbody></table><br />　　<strong><font color="#ff0000">但要注意：</font></strong><br /><br />　　A. 一个__gc类不能从一个非托管类中继承，且不能包含从它派生的非托管类。但一个__gc类最多可以从一个托管类中继承。<br /><br />　　B. 一个__gc类不能定义成一个友元类或包含一个友元成员函数。所谓友元函数，是用来让外部函数访问类中的私有和保护类型成员。<br /><br />　　C. 一个__gc类不能声明或定义以及重载new或delete操作以及不能包含using等声明。<br /><br />　　__value类是用来使用具有短生命期的小型数据项，它不同于__gc类。__gc类数据分配在CLR堆中，而__value类对象是在运行栈或称为 NDP(.NET Developer Platform，.NET开发者平台)堆中创建的，从而避免了垃圾回收器不断分配和释放空间而带来的开销。一个__value类可以声明成为一个局部变量、参数和返回值，也可嵌入到一个__gc类中或是作为一个静态变量或在C++堆中分配的变量。例如下面的代码：<br /><br /><table width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td>#using <br />using namespace System;<br />__value struct V { int i; };<br />__gc struct G { V v; }; // 嵌入到__gc类中<br />V f(V v) { // 定义一个全局函数，其值存储在运行栈中<br />v.i += 1; // 不影响原来形参v的值<br />return v; // 返回V结构类型的值<br />}<br />int main(void) <br />{<br />V v1 = {10}; // 在运行栈中声明并初始化<br />V v2 = f(v1); // 调用f函数，此时v1中的i为10，而v2中的i为11<br />G *pG = new G; // 为G实例分配堆空间<br />pG-&amp;gt;v = v1; // pG的v中的i为10<br />pG-&amp;gt;v.i += v2.i; // pG的v中的i为10+11=21<br />Console::WriteLine(v1.i); // 输出结果为10<br />Console::WriteLine(v2.i); // 输出结果为11<br />Console::WriteLine(pG-&amp;gt;v.i); // 输出结果为21<br />return 0;<br />}</td></tr></tbody></table><br />　　除此之外，所有的__gc对象都是从类System::Object派生而来，因而能够很容易使用作用在__gc类中的集合和映射功能。然而 __value类型并没有与这个基类所共享，因而不能直接将__value作为函数中的Object*实参。为了解决这个问题，.NET允许我们使用 __box关键字将一个__value类型视为一个__gc对象。此时__value类型被封装成一个__gc类桩子(Stub)，并被复制到NDP堆中。由于在托管C++中，box不具备隐式转换的功能，因此在转换时必须指明转换的类型。<br /><br />　　托管C++中的__gc接口最能体现COM接口的思想，它的定义和声明是非常简单的，它除了关键字不同外，与一个__gc类的声明极为相似。例如下面的代码定义了一个接口IMyBase，其中包含了一个f的方法：<br /><br /><table width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td>__gc __interface Ibase {<br />void f();<br />};</td></tr></tbody></table><br />　　需要说明的是，接口中所有的方法默认时都是纯虚的且都是公有的，我们不需要在方法之前使用virtual关键字或在方法之后加上&amp;quot;= 0&amp;quot;。其次，在一个__gc接口中不能包含数据成员以及静态成员，也不能包含任何类的声明。下面举一个示例来说明__gc接口的使用：<br /><br /><table width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td>#using <br />using namespace System;<br /><br />__gc __interface Ibase1 {<br />int f(int);<br />};<br />__gc __interface Ibase2 {<br />int f(int);<br />};<br />__gc struct C: Ibase1, Ibase2 {<br />int f(int i) { // 接口方法的实现<br />return 2*i-1;<br />};<br />};<br /><br />int main(void){<br />C* c = new C;<br />Console::WriteLine((c -&amp;gt; f(1)).ToString()); // 输出结果为1<br />Console::WriteLine((__try_cast (c)-&amp;gt;f(2)).ToString()); <br />// 输出结果为3<br /><br />Console::WriteLine((__try_cast (c)-&amp;gt;f(3)).ToString());<br />// 输出结果为5<br /><br />return 0;<br />}</td></tr></tbody></table><p><br />　　代码中，__try_cast用来将某个对象转换成一个指定类型，并当类型转换失败时自动处理由此产生的异常。ToString用来将对象描述成一个字符串。</p><p><strong>(4) 简化属性操作<br /><br /></strong>　　在__gc类中可以使用.NET的属性，这个属性简化了属性函数的调用操作，这与标准C++中的属性不一样。在标准C++中分别通过get_和put_成员函数来设置或获取相关属性的值。现在，托管C++中的属性操作就好比是对一个属性变量进行操作，例如下列代码：<br /><br /></p><table width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td>#using <br />using namespace System;<br /><br />__gc class G {<br />public:<br />__property int get_Size() {<br />Console::WriteLine(S&amp;quot;get_属性&amp;quot;);<br />return nSize;<br />};<br />__property void set_Size(int i) { <br />Console::WriteLine(S&amp;quot;set_属性&amp;quot;);<br />nSize = i;<br />};<br />private:<br />int nSize;<br />};<br /><br />int main() {<br />G * pG = new G;<br />pG-&amp;gt;Size = 10; // 调用set_Size<br />int i = pG-&amp;gt;Size; // 调用get_Size<br />Console::WriteLine(i);<br />}</td></tr></tbody></table><br />　　程序结果为：<br /><br />　　　set_属性<br /><br />　　　get_属性<br /><br />　　　10<br /><br />　　需要说明的是，托管C++使用__property关键字来定义一个属性的成员函数。从代码中可以看出设置和获取属性的成员函数名称中分别使用了 set_和get_，这样编译器会自动生成一个伪成员变量Size，这个变量名是set_和get_成员函数后面的名称。注意不要再在get_成员函数代码中使用这个伪成员变量Size，它会引起该函数的递归调用。<br /><br />　　<strong>(5) 托管C++的委派</strong><br /><br />　　在C/C++中，一个函数的地址就是内存地址。这个地址不会带有任何其它附加信息，如函数的参数个数、参数类型、函数的返回值类型以及这个函数的调用规范等。总之，C/C++的回调函数不具备类型安全性。而.NET框架在回调函数的基础增加了提供类型安全的机制，称为委派。<br /><br />　　托管C++的委派方法不像C#那么复杂，它简化了委派绝大部分的内部机制，因而使得它的使用变成非常简单容易。例如下面的代码：<br /><br /><table width="100%" bgcolor="#ffffff" border="0"><tbody><tr><td>#using <br />using namespace System;<br /><br />__delegate int GetDayOfWeek(); // 委派方法的声明<br />__gc class MyCalendar<br />{<br />public:<br />MyCalendar() : m_nDayOfWeek(4) {}<br />int MyGetDayOfWeek() {<br />Console::WriteLine(&amp;quot;非静态方法&amp;quot;); <br />return m_nDayOfWeek; <br />}<br />static int MyStaticGetDayOfWeek() { <br />Console::WriteLine(&amp;quot;静态方法&amp;quot;); <br />return 6; <br />}<br />private:<br />int m_nDayOfWeek;<br />};<br /><br />int main(void)<br />{<br />GetDayOfWeek * pGetDayOfWeek; // 声明委派类型变量<br />int nDayOfWeek;<br /><br />// 将类的静态方法MyStaticGetDayOfWeek绑定成委派<br />pGetDayOfWeek = new GetDayOfWeek(0, &amp;amp;MyCalendar::MyStaticGetDayOfWeek);<br />nDayOfWeek = pGetDayOfWeek-&amp;gt;Invoke(); // 委派的调用<br />Console::WriteLine(nDayOfWeek);<br /><br />// 将一个类的实例绑定成委派<br />MyCalendar * pcal = new MyCalendar();<br />pGetDayOfWeek = <br />static_cast(Delegate::Combine(pGetDayOfWeek,<br />new GetDayOfWeek(pcal, &amp;amp;MyCalendar::MyGetDayOfWeek)));<br />nDayOfWeek = pGetDayOfWeek-&amp;gt;Invoke();<br />Console::WriteLine(nDayOfWeek);<br /><br />// 删除绑定委派的类实例<br />pGetDayOfWeek = <br />static_cast(Delegate::Remove(pGetDayOfWeek,<br />new GetDayOfWeek(pcal, &amp;amp;MyCalendar::MyGetDayOfWeek)));<br /><br />return 0;<br />}</td></tr></tbody></table><br />　　输出结果是：<br /><br />　　　静态方法<br />　　<br />　　　6<br /><br />　　　静态方法<br /><br />　　　非静态方法<br /><br />　　　4<br /><br />　　<strong><font color="#ac0000">4、结速语</font></strong><br /><br />　　总之，使用托管C++是C++程序员编写.NET框架应用程序最好的一种选择，在充分理解.NET框架基础上，避免了使用其他语言如C#、VB.NET所带来的额外开销。]]></description> 
<guid isPermaLink="false">6069544@http://http88.bokee.com/</guid> 
<dc:subject>项目开发经验</dc:subject> 
<dc:date>2007-01-26T15:57:42Z</dc:date> 
</item> 
<item> 
<title><![CDATA[Log4j属性文件的配置]]></title> 
<link>http://http88.bokee.com/5997758.html</link> 
<description><![CDATA[<p><strong>步骤：</strong></p><p>1. place <font color="#ff0000"><strong>log4j.properties</strong></font> to classes path;</p><p>2. declare logger with class var: <strong><font color="#ff0000">private static Logger logger = Logger.getLogger(MyApp.class);</font></strong></p><p>3. use logger to log: <font color="#ff0000"><strong>logger.debug(&amp;quot;1 application.&amp;quot;);</strong></font></p><p>Example config file log4j.properties:</p><p># Set root logger level to DEBUG and its only appender to A1.<br />log4j.rootLogger=<font color="#ff0000">ERROR, A1, A2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # set default log level</font></p><p><font color="#ff0000">log4j.category.org.apache.struts=WARN<br />log4j.category.org.apache.commons.logging=WARN<br />log4j.category.com.myproject=DEBUG&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # set individual log level apply to special package<br />log4j.category.org.pi=DEBUG</font></p><p># A1 is set to be a ConsoleAppender.<br />log4j.appender.A1=org.apache.log4j.ConsoleAppender<br />log4j.appender.A1.layout=org.apache.log4j.PatternLayout<br />#log4j.appender.A1.layout.ConversionPattern=%5p [%t] (%F:%M:%L) - %m%n<br />log4j.appender.A1.layout.ConversionPattern=%5p [%d{HH:mm:ss}] (%F:%M:%L) - %m%n</p><p># A2 is set to be a RollingFileAppender <br />log4j.appender.A2=org.apache.log4j.RollingFileAppender<br />log4j.appender.A2.File=<font color="#ff0000"><strong>E:/system.log&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; #here use: /&amp;nbsp; don't use:\<br /></strong></font>log4j.appender.A2.MaxFileSize=2048KB<br />log4j.appender.A2.MaxBackupIndex=100<br />log4j.appender.A2.layout=org.apache.log4j.PatternLayout<br />log4j.appender.A2.layout.ConversionPattern=%5p [%d{dd MMM HH:mm:ss}] (%F:%M:%L) - %m%n</p><p><strong>实践：</strong></p><p>1、先把那个log4j-1.2.8.jar down 下来。</p><p>2、新建一个java工程(我是用的Eclipse3.0)，然后将log4j-1.2.8.jar 添加到工程的java build path中 lib里。</p><p>&amp;nbsp;3、新建一个Logs类。</p><p>import org.apache.log4j.Logger ;<br />/**<br />&amp;nbsp;* 日志输出<br />&amp;nbsp;* @author dxg<br />&amp;nbsp;* <br />&amp;nbsp;*/<br />public class Logs {<br />&amp;nbsp;String log4j;<br />&amp;nbsp;static Logger logger = Logger.getLogger(Logs.class.getName ()) ;<br />}</p><p>4、新建一个属性文件，后缀为.properties。例如我的：Log4j.properties，文件的内容如下：</p><p>log4j.rootLogger=debug, A1,A2<br />log4j.appender.A1=org.apache.log4j.ConsoleAppender<br />log4j.appender.A1.layout=org.apache.log4j.PatternLayout<br />log4j.appender.A1.layout.ConversionPattern= &amp;quot;%-4r [%t] %-5p %c %x - %m%n<br />log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%r][%t][%c]-[%p](%F:%L) %m%n</p><p>log4j.appender.A2=org.apache.log4j.RollingFileAppender<br />log4j.appender.A2.File=c:\logout.log<br />log4j.appender.A2.MaxFileSize=100KB<br />log4j.appender.A2.MaxBackupIndex=1<br />log4j.appender.A2.layout=org.apache.log4j.PatternLayout<br />log4j.appender.A2.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} {%r}[%t][%c]-[%p](%F:%L) %m%n</p><p>5、属性文件的目录放置到工程里的根目录</p><p>WebRoot\WEB-INF\classes</p><p>6、新建一个测试类。并在类中引用logs,</p><p><br />&amp;nbsp;public static boolean fileReader1() {<br />&amp;nbsp; boolean bl = false;<br />&amp;nbsp; String line = null;<br />&amp;nbsp; try {<br />&amp;nbsp;&amp;nbsp; BufferedReader br = new BufferedReader(<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new FileReader(&amp;quot;d:\\sss.txt&amp;quot;));<br />&amp;nbsp;&amp;nbsp; while ((line = br.readLine()) != null) {<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; //System.out.println(line);//读取文件<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; Logs.logger.debug(line);<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; bl = true;<br />&amp;nbsp;&amp;nbsp; }<br />&amp;nbsp; } catch (Exception e) {<br />&amp;nbsp;&amp;nbsp; System.out.println(&amp;quot;&amp;quot; + e.toString());<br />&amp;nbsp;&amp;nbsp; Logs.logger.debug(e.toString());<br />&amp;nbsp; } finally {<br />&amp;nbsp;&amp;nbsp; //System.out.println(&amp;quot;It's just a test!&amp;quot;);<br />&amp;nbsp; }<br />&amp;nbsp; return bl;<br />&amp;nbsp;}</p>]]></description> 
<guid isPermaLink="false">5997758@http://http88.bokee.com/</guid> 
<dc:subject>html&amp;web</dc:subject> 
<dc:date>2006-12-28T23:53:54Z</dc:date> 
</item> 
<item> 
<title><![CDATA[Axis开发Web Service的实例]]></title> 
<link>http://http88.bokee.com/5994855.html</link> 
<description><![CDATA[<p>一、Axis安装 1、环境 J2SE SDK 1.3 or 1.4: 我使用 1.4.2 Servlet Container: 我使用的Tomcat 5.0 </p><p>2、到 <a href="http://ws.apache.org/Axis/">http://ws.apache.org/Axis/</a>网站下载Axis安装包</p><p>3、解压缩安装包，将Axis_UNZIP_PATH\Axis-version\webapps下的Axis包拷贝到TOMCAT_HOME\webapps\下，以下约定Axis_HOME为该TOMCAT_HOME\webapps\Axis目录</p><p>4、启动tomcat,访问<a href="http://localhost:8080/Axis">http://localhost:8080/Axis</a> 检查安装是否成功</p><p>5、以上步骤执行成功，可以开发webservice例子了</p><p>Axis支持三种web service的部署和开发，分别为：</p><p>1、Dynamic Invocation Interface ( DII)</p><p>2、Stubs方式</p><p>3、Dynamic Proxy方式</p><p>二、编写DII(Dynamic Invocation Interface )方式web服务</p><p>1.编写服务端程序HelloClient</p><p><br />public class HelloClient<br />{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; public String getName(String name)<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; {<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return &amp;quot;hello &amp;quot;+name;<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; }<br />} </p><p>2、将源码拷贝到Axis_HOME下，重命名为 HelloClient.jws </p><p>3、访问连接<a href="http://localhost:8080/Axis/HelloClient.jws?wsdl">http://localhost:8080/Axis/HelloClient.jws?wsdl</a>，页面显示Axis自动生成的wsdl </p><p>4、编写访问服务的客户端 TestHelloClient.java </p><p><br />import org.apache.Axis.client.Call;<br />import org.apache.Axis.client.Service;<br />import javax.xml.namespace.QName;<br />import javax.xml.rpc.ServiceException;<br />import java.net.MalformedURLException;<br />import java.rmi.RemoteException;</p><p>public class SayHelloClient2<br />{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void main(String[] args)<br />&amp;nbsp;{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try<br />&amp;nbsp;&amp;nbsp;{<br />&amp;nbsp; String endpoint =<br />&amp;nbsp; &amp;quot;<a href="http://localhost:8080/Axis/HelloClient.jws">http://localhost:8080/Axis/HelloClient.jws</a>&amp;quot;;</p><p>&amp;nbsp;&amp;nbsp;&amp;nbsp; Service service = new Service();<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Call call = null;</p><p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; call = (Call) service.createCall();</p><p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; call.setOperationName(new QName(<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;<a href="http://localhost:8080/Axis/HelloClient.jws">http://localhost:8080/Axis/HelloClient.jws</a>&amp;quot;, <br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;getName&amp;quot;));<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; call.setTargetEndpointAddress<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;(new java.net.URL(endpoint));</p><p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String ret = (String) call.invoke(new Object[] <br />&amp;nbsp;&amp;nbsp;&amp;nbsp;{&amp;quot;zhangsan&amp;quot;});<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println(&amp;quot;return value is &amp;quot; + ret);<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } <br />&amp;nbsp;&amp;nbsp;catch (Exception ex)<br />&amp;nbsp;&amp;nbsp;{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ex.printStackTrace();<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; }<br />} </p><p>三、编写Dynamic Proxy方式访问服务 </p><p>1、编写部署服务端程序，同上边DII方式，本次仍使用上边部署的HelloClient </p><p>2、编写代理接口 </p><p><br />public interface HelloClientInterface<br />extends java.rmi.Remote<br />{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; public String getName(String name)<br />&amp;nbsp;throws java.rmi.RemoteException;<br />} </p><p>3、编写并执行客户端程序TestHelloClient.java </p><p><br />import javax.xml.rpc.Service;<br />import javax.xml.rpc.ServiceFactory;<br />import java.net.URL;<br />import javax.xml.namespace.QName;</p><p>public class TestHelloClient <br />{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void main(String[] args)<br />&amp;nbsp;{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String wsdlUrl = <br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;<a href="http://localhost:8080/Axis/HelloClient.jws?wsdl">http://localhost:8080/Axis/HelloClient.jws?wsdl</a>&amp;quot;;<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String nameSpaceUri = <br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;<a href="http://localhost:8080/Axis/HelloClient.jws">http://localhost:8080/Axis/HelloClient.jws</a>&amp;quot;;<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String serviceName = &amp;quot;HelloClientService&amp;quot;;<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String portName = &amp;quot;HelloClient&amp;quot;;</p><p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ServiceFactory serviceFactory = <br />&amp;nbsp;&amp;nbsp;&amp;nbsp;ServiceFactory.newInstance();<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Service afService =<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;serviceFactory.createService(new URL(wsdlUrl),<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new QName(nameSpaceUri, serviceName));<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HelloClientInterface proxy = (HelloClientInterface)<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; afService.getPort(new QName(<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nameSpaceUri, portName), <br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HelloClientInterface.class);<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;(&amp;quot;return value is &amp;quot;+proxy.getName(&amp;quot;john&amp;quot;) ) ;<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }catch(Exception ex)<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ex.printStackTrace() ;<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; }<br />} </p><p>四、编写wsdd发布web服务，编写stub client访问web服务 </p><p>1、编写服务端程序server,SayHello.java，编译server.SayHello.java </p><p><br />package server;<br />public class SayHello<br />{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; public String getName(String name)<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; {<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return &amp;quot;hello &amp;quot;+name;<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; }<br />}<br />2.编写LogHandler.java<br />import org.apache.Axis.AxisFault;<br />import org.apache.Axis.Handler;<br />import org.apache.Axis.MessageContext;<br />import org.apache.Axis.handlers.BasicHandler;</p><p>import java.util.Date;</p><p>public class LogHandler <br />extends BasicHandler <br />{<br />&amp;nbsp;public void invoke<br />(MessageContext msgContext) <br />throws AxisFault<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; {<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /** Log an access each time <br />&amp;nbsp;&amp;nbsp;we get invoked.<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; */<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try {<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Handler serviceHandler<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;= msgContext.getService();</p><p>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Integer numAccesses =<br />&amp;nbsp; (Integer)serviceHandler.getOption(&amp;quot;accesses&amp;quot;);<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (numAccesses == null)<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; numAccesses = new Integer(0);<br />numAccesses = new Integer<br />(numAccesses.intValue() + 1);<br />Date date = new Date();<br />&amp;nbsp;String result = <br />&amp;nbsp;date + &amp;quot;: service &amp;quot; +<br />msgContext.getTargetService() +<br />&amp;quot; accessed &amp;quot; + numAccesses + &amp;quot; time(s).&amp;quot;;<br />serviceHandler.setOption<br />(&amp;quot;accesses&amp;quot;, numAccesses);<br />System.out.println(result);<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } catch (Exception e)<br />&amp;nbsp;&amp;nbsp;{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw AxisFault.makeFault(e);<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; }<br />} </p><p>3、编写wsdd文件 </p><p><br />deploy.wsdd<br />&amp;lt;deployment xmlns=<br />&amp;quot;<a href="http://xml.apache.org/Axis/wsdd/">http://xml.apache.org/Axis/wsdd/</a>&amp;quot;<br />&amp;nbsp; xmlns:java=<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;<a href="http://xml.apache.org/Axis/wsdd/providers/java">http://xml.apache.org/Axis/wsdd/providers/java</a>&amp;quot;&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; <br />&amp;nbsp;&amp;nbsp; &amp;lt;handler name=&amp;quot;print&amp;quot; type=&amp;quot;java:LogHandler&amp;quot;/&amp;gt; <br />&amp;nbsp;&amp;lt;service name=&amp;quot;sayhello&amp;quot;<br />&amp;nbsp;provider=&amp;quot;java:RPC&amp;quot;&amp;gt;<br />&amp;nbsp;&amp;nbsp; &amp;lt;requestFlow&amp;gt;<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;handler type=&amp;quot;print&amp;quot;/&amp;gt;<br />&amp;nbsp;&amp;nbsp; &amp;lt;/requestFlow&amp;gt;<br />&amp;nbsp; &amp;lt;parameter name=&amp;quot;className&amp;quot; <br />&amp;nbsp; value=&amp;quot;server.SayHello&amp;quot;/&amp;gt;<br />&amp;nbsp; &amp;lt;parameter name=&amp;quot;allowedMethods&amp;quot;<br />&amp;nbsp; value=&amp;quot;*&amp;quot;/&amp;gt;&amp;nbsp; <br />&amp;nbsp;&amp;lt;/service&amp;gt;<br />&amp;lt;/deployment&amp;gt; </p><p>3、将编译后的文件拷贝到Axis_HOME/WEB-INF/classes下，如：D:\tomcat\webapps\Axis\WEB-INF\classes </p><p>4、发布服务： </p><p>java org.apache.Axis.client.AdminClient deploy.wsdd </p><p>5、生成client stub文件 </p><p>a:方式1 </p><p>将SayHello.java拷贝到Axis_HOME/下，重命名为SayHello.jws， </p><p>执行下面的命令生存client stub </p><p><br />java org.apache.Axis.wsdl.WSDL2Java <br />-p client&amp;nbsp; <a href="http://localhost:8080">http://localhost:8080</a><br />/Axis/services/SayHello.jws?wsdl </p><p>b:方式2 </p><p>执行如下命令生成SayHello.wsdl </p><p><br />java org.apache.Axis.wsdl.Java2WSDL<br />-oSayHello.wsdl -lhttp://localhost:8080<br />/Axis/services/SayHello -nsayhello server.SayHello </p><p>执行如下命令生成client stub </p><p><br />java org.apache.Axis.wsdl.WSDL2Java <br />SayHello.wsdl&amp;nbsp; -p client </p><p>生成的stub client文件列表为： </p><p>1.SayHello.java </p><p>2.SayHelloService.java。 </p><p>3.SayHelloServiceLocator.java </p><p>4.SayHelloSoapBindingStub.java </p><p>6、编写客户端程序，编译并执行 </p><p><br />public class SayHelloClient<br />{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void main(String[] args)<br />&amp;nbsp;{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try<br />&amp;nbsp;&amp;nbsp;{<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; SayHelloService service = new client.<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SayHelloServiceLocator();<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; client.SayHello_PortType <br />&amp;nbsp;&amp;nbsp;&amp;nbsp; client = service.getSayHello();<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String retValue=client.getName(&amp;quot;zhangsan&amp;quot;);<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println(retValue);<br />} <br />catch (Exception e)<br />{<br />&amp;nbsp;System.err.println<br />&amp;nbsp;(&amp;quot;Execution failed. Exception: &amp;quot; + e);<br />&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }<br />&amp;nbsp;&amp;nbsp;&amp;nbsp; }<br />} <br /></p><p />]]></description> 
<guid isPermaLink="false">5994855@http://http88.bokee.com/</guid> 
<dc:subject>项目开发经验</dc:subject> 
<dc:date>2006-12-27T23:43:57Z</dc:date> 
</item> 
<item> 
<title><![CDATA[JAVA 设计模式的另类理解]]></title> 
<link>http://http88.bokee.com/5993160.html</link> 
<description><![CDATA[<p>1、FACTORY?</p><p>　　追MM少不了请吃饭了，麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西，虽然口味有所不同，但不管你带MM去麦当劳或肯德基，只管向服务员说“来四个鸡翅</p><p>”就行了。麦当劳和肯德基就是生产鸡翅的Factory 。</p><p>　　工厂模式：客户类和工厂类分开。消费者任何时候需要某种产品，只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时，工厂类也</p><p>要做相应的修改。如：如何创建及如何向客户端提供。</p><p><br />　　2、BUILDER?</p><p>　　MM最爱听的就是“我爱你”这句话了，见到不同地方的MM,要能够用她们的方言跟她说这句话哦，我有一个多种语言翻译机，上面每种语言都有一个按键，见</p><p>到MM我只要按对应的键，它就能够用相应的语言说出“我爱你”这句话了，国外的MM也可以轻松搞掂，这就是我的“我爱你”builder。（这一定比美军在伊拉克</p><p>用的翻译机好卖）</p><p>　　建造模式：将产品的内部表象和产品的生成过程分割开来，从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的</p><p>变化，客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。</p><p><br />　　3、FACTORY METHOD?</p><p>　　请MM去麦当劳吃汉堡，不同的MM有不同的口味，要每个都记住是一件烦人的事情，我一般采用Factory Method模式，带着MM到服务员那儿，说“要一个汉堡</p><p>”，具体要什么样的汉堡呢，让MM直接跟服务员说就行了。</p><p>　　工厂方法模式：核心工厂类不再负责所有产品的创建，而是将具体创建的工作交给子类去做，成为一个抽象工厂角色，仅负责给出具体工厂类必须实现的接口</p><p>，而不接触哪一个产品类应当被实例化这种细节。</p><p><br />　　4、PROTOTYPE?</p><p>　　跟MM用QQ聊天，一定要说些深情的话语了，我搜集了好多肉麻的情话，需要时只要copy出来放到QQ里面就行了，这就是我的情话prototype了。（100块钱一</p><p>份，你要不要）</p><p>　　原始模型模式：通过给出一个原型对象来指明所要创建的对象的类型，然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增</p><p>加或减少产品类，产品类不需要非得有任何事先确定的等级结构，原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。</p><p>　　5、SINGLETON?</p><p>　　俺有6个漂亮的老婆，她们的老公都是我，我就是我们家里的老公Sigleton，她们只要说道“老公”，都是指的同一个人，那就是我(刚才做了个梦啦，哪有这</p><p>么好的事) </p><p>　　单例模式：单例模式确保某一个类只有一个实例，而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可</p><p>使用。 [b:9ceca65206]结构型模式[/b:9ceca65206]</p><p>　　6、ADAPTER?</p><p>　　在朋友聚会上碰到了一个美女Sarah，从香港来的，可我不会说粤语，她不会说普通话，只好求助于我的朋友kent了，他作为我和Sarah之间的Adapter，让我</p><p>和Sarah可以相互交谈了(也不知道他会不会耍我)</p><p>　　适配器（变压器）模式：把一个类的接口变换成客户端所期待的另一种接口，从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可</p><p>以根据参数返还一个合适的实例给客户端。</p><p><br />　　7、BRIDGE?</p><p>　　早上碰到MM，要说早上好，晚上碰到MM，要说晚上好；碰到MM穿了件新衣服，要说你的衣服好漂亮哦，碰到MM新做的发型，要说你的头发好漂亮哦。不要问我</p><p>“早上碰到MM新做了个发型怎么说”这种问题，自己用BRIDGE组合一下不就行了。</p><p>　　桥梁模式：将抽象化与实现化脱耦，使得二者可以独立的变化，也就是说将他们之间的强关联变成弱关联，也就是指在一个软件系统的抽象化和实现化之间使</p><p>用组合/聚合关系而不是继承关系，从而使两者可以独立的变化。</p><p>&amp;nbsp;&amp;nbsp;&amp;nbsp; 8、COMPOSITE?</p><p>　　Mary今天过生日。“我过生日，你要送我一件礼物。”“嗯，好吧，去商店，你自己挑。”“这件T恤挺漂亮，买，这条裙子好看，买，这个包也不错，买。</p><p>”“喂，买了三件了呀，我只答应送一件礼物的哦。”“什么呀，T恤加裙子加包包，正好配成一套呀，小姐，麻烦你包起来。”“……”，MM都会用Composite</p><p>模式了，你会了没有？</p><p>　　合成模式：合成模式将对象组织到树结构中，可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用</p><p>树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。</p><p>　　9、DECORATOR?</p><p>　　Mary过完轮到Sarly过生日，还是不要叫她自己挑了，不然这个月伙食费肯定玩完，拿出我去年在华山顶上照的照片，在背面写上“最好的的礼物，就是爱你</p><p>的Fita”，再到街上礼品店买了个像框（卖礼品的MM也很漂亮哦），再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……，我们都是Decorator，最终都</p><p>在修饰我这个人呀，怎么样，看懂了吗？</p><p>　　装饰模式：装饰模式以对客户端透明的方式扩展对象的功能，是继承关系的一个替代方案，提供比继承更多的灵活性。动态给一个对象增加功能，这些功能可</p><p>以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。</p><p><br />　　10、FACADE?</p><p>　　我有一个专业的Nikon相机，我就喜欢自己手动调光圈、快门，这样照出来的照片才专业，但MM可不懂这些，教了半天也不会。幸好相机有Facade设计模式，</p><p>把相机调整到自动档，只要对准目标按快门就行了，一切由相机自动调整，这样MM也可以用这个相机给我拍张照片了。</p><p>　　门面模式：外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口，使得子系统更易于使用。每一个子系统只有一个门</p><p>面类，而且此门面类只有一个实例，也就是说它是一个单例模式。但整个系统可以有多个门面类。</p><p><br />　　11、FLYWEIGHT?</p><p>　　每天跟MM发短信，手指都累死了，最近买了个新手机，可以把一些常用的句子存在手机里，要用的时候，直接拿出来，在前面加上MM的名字就可以发送了，再</p><p>不用一个字一个字敲了。共享的句子就是Flyweight，MM的名字就是提取出来的外部特征，根据上下文情况使用。</p><p>　　享元模式：FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状</p><p>态。内蕴状态存储在享元内部，不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态，它们是相互独立的。将可以共享</p><p>的状态和不可以共享的状态从常规类中区分开来，将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象，而应当使用一个工厂对象负责创建</p><p>被共享的对象。享元模式大幅度的降低内存中对象的数量。</p><p><br />　　12、PROXY?</p><p>　　跟MM在网上聊天，一开头总是“hi,你好”,“你从哪儿来呀？”“你多大了？”“身高多少呀？”这些话，真烦人，写个程序做为我的Proxy吧，凡是接收到</p><p>这些话都设置好了自动的回答，接收到其他的话时再通知我回答，怎么样，酷吧。</p><p>　　代理模式：代理模式给某一个对象提供一个代理对象，并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。</p><p>某些情况下，客户不想或者不能够直接引用一个对象，代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理</p><p>模式可以并不知道真正的被代理对象，而仅仅持有一个被代理对象的接口，这时候代理对象不能够创建被代理对象，被代理对象必须有系统的其他角色代为创建并</p><p>传入。 [b:9ceca65206]行为模式[/b:9ceca65206]</p><p><br />　　13、CHAIN OF RESPONSIBLEITY?</p><p>　　晚上去上英语课，为了好开溜坐到了最后一排，哇，前面坐了好几个漂亮的MM哎，找张纸条，写上“Hi,可以做我的女朋友吗？如果不愿意请向前传”，纸条</p><p>就一个接一个的传上去了，糟糕，传到第一排的MM把纸条传给老师了，听说是个老处女呀，快跑!</p><p>　　责任链模式：在责任链模式中，很多对象由每一个对象对其下家的引用而接 　　起来形成一条链。请求在这个链上传递，直到链上的某一个对象决定处理此</p><p>请求。客户并不知道链上的哪一个对象最终处理这个请求，系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择：承担责任或者把</p><p>责任推给下家。一个请求可以最终不被任何接收端对象所接受。</p><p><br />　　14、COMMAND?</p><p>　　俺有一个MM家里管得特别严，没法见面，只好借助于她弟弟在我们俩之间传送信息，她对我有什么指示，就写一张纸条让她弟弟带给我。这不，她弟弟又传送</p><p>过来一个COMMAND，为了感谢他，我请他吃了碗杂酱面，哪知道他说：“我同时给我姐姐三个男朋友送COMMAND，就数你最小气，才请我吃面。”</p><p>　　命令模式：命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开，委派给不同的对象。命令模式允许请求的</p><p>一方和发送的一方独立开来，使得请求的一方不必知道接收请求的一方的接口，更不必知道请求是怎么被接收，以及操作是否执行，何时被执行以及是怎么被执行</p><p>的。系统支持命令的撤消。<br />15、INTERPRETER?</p><p>　　俺有一个《泡MM真经》，上面有各种泡MM的攻略，比如说去吃西餐的步骤、去看电影的方法等等，跟MM约会时，只要做一个Interpreter，照着上面的脚本执</p><p>行就可以了。</p><p>　　解释器模式：给定一个语言后，解释器模式可以定义出其文法的一种表示，并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解</p><p>释器模式将描述怎样在有了一个简单的文法后，使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模</p><p>式中需要定义一个代表文法的命令类的等级结构，也就是一系列的组合规则。每一个命令对象都有一个解释方法，代表对命令对象的解释。命令对象的等级结构中</p><p>的对象的任何排列组合都是一个语言。</p><p><br />　　16、ITERATOR?</p><p>　　我爱上了Mary，不顾一切的向她求婚。 　　Mary：“想要我跟你结婚，得答应我的条件” 　　我：“什么条件我都答应，你说吧” 　　Mary：“我看上了</p><p>那个一克拉的钻石” 　　我：“我买，我买，还有吗？” 　　Mary：“我看上了湖边的那栋别墅” 　　我：“我买，我买，还有吗？” 　　Mary：“我看上</p><p>那辆法拉利跑车” 　　我脑袋嗡的一声，坐在椅子上，一咬牙：“我买，我买，还有吗？”…… </p><p>　　迭代子模式：迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集，聚集对象是能够包容一组对</p><p>象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中，从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以</p><p>上的迭代子对象，每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。</p><p><br />　　17、MEDIATOR?</p><p>　　四个MM打麻将，相互之间谁应该给谁多少钱算不清楚了，幸亏当时我在旁边，按照各自的筹码数算钱，赚了钱的从我这里拿，赔了钱的也付给我，一切就OK啦</p><p>，俺得到了四个MM的电话。</p><p>　　调停者模式：调停者模式包装了一系列对象相互作用的方式，使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时</p><p>，不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的</p><p>行为和协作抽象化，把对象在小尺度的行为上与其他对象的相互作用分开处理。</p><p><br />　　18、MEMENTO?</p><p>　　同时跟几个MM聊天时，一定要记清楚刚才跟MM说了些什么话，不然MM发现了会不高兴的哦，幸亏我有个备忘录，刚才与哪个MM说了什么话我都拷贝一份放到备</p><p>忘录里面保存，这样可以随时察看以前的记录啦。</p><p>　　备忘录模式：备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下，将一个对象的状态捉住，并外部</p><p>化，存储起来，从而可以在将来合适的时候把这个对象还原到存储起来的状态。</p><p><br />　　19、OBSERVER?</p><p>　　想知道咱们公司最新MM情报吗？加入公司的MM情报邮件组就行了，tom负责搜集情报，他发现的新情报不用一个一个通知我们，直接发布给邮件组，我们作为</p><p>订阅者（观察者）就可以及时收到情报啦</p><p>　　观察者模式：观察者模式定义了一种一队多的依赖关系，让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时，会通知所有观察者</p><p>对象，使他们能够自动更新自己。</p><p><br />　　20、STATE?</p><p>　　跟MM交往时，一定要注意她的状态哦，在不同的状态时她的行为会有不同，比如你约她今天晚上去看电影，对你没兴趣的MM就会说“有事情啦”，对你不讨厌</p><p>但还没喜欢上的MM就会说“好啊，不过可以带上我同事么？”，已经喜欢上你的MM就会说“几点钟？看完电影再去泡吧怎么样？”，当然你看电影过程中表现良好</p><p>的话，也可以把MM的状态从不讨厌不喜欢变成喜欢哦。</p><p>　　状态模式：状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的</p><p>状态对象里，每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候，其行为也随之改变。状态模式需要对每</p><p>一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时，系统便改变所选的子类。</p><p><br />　　21、STRATEGY?</p><p>　　跟不同类型的MM约会，要用不同的策略，有的请电影比较好，有的则去吃小吃效果不错，有的去海边浪漫最合适，单目的都是为了得到MM的芳心，我的追MM锦</p><p>囊中有好多Strategy哦。</p><p>　　策略模式：策略模式针对一组算法，将每一个算法封装到具有共同接口的独立的类中，从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端</p><p>的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类，各种算法在具体的策略类中提供。由于算法和环境独立开来，算法的增减，修改</p><p>都不会影响到环境和客户端。</p><p><br />　　22、TEMPLATE METHOD?</p><p>　　看过《如何说服女生上床》这部经典文章吗？女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤</p><p>(Template method)，但每个步骤针对不同的情况，都有不一样的做法，这就要看你随机应变啦(具体实现)。</p><p>　　模板方法模式：模板方法模式准备一个抽象类，将部分逻辑以具体方法以及具体构造子的形式实现，然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同</p><p>的子类可以以不同的方式实现这些抽象方法，从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架，而将逻辑的细节留给具体的子类去实现。</p><p><br />　　23、VISITOR?</p><p>　　情人节到了，要给每个MM送一束鲜花和一张卡片，可是每个MM送的花都要针对她个人的特点，每张卡片也要根据个人的特点来挑，我一个人哪搞得清楚，还是</p><p>找花店老板和礼品店老板做一下Visitor，让花店老板根据MM的特点选一束花，让礼品店老板也根据每个人特点选一张卡，这样就轻松多了。</p><p>　　访问者模式：访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话，接受这个操作的数据结构可以保持不变。访问</p><p>者模式适用于数据结构相对未定的系统，它把数据结构和作用于结构上的操作之间的耦合解脱开，使得操作集合可以相对自由的演化。访问者模式使得增加新的操</p><p>作变的很容易，就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中，而不是分散到一个个的节点类中。当使用访问者模式时，要将尽</p><p>可能多的对象浏览逻辑放在访问者类中，而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。<br /></p>]]></description> 
<guid isPermaLink="false">5993160@http://http88.bokee.com/</guid> 
<dc:subject>项目开发经验</dc:subject> 
<dc:date>2006-12-27T14:16:57Z</dc:date> 
</item> 

</channel> 
</rss> 
