﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-巢穴</title><link>http://www.cppblog.com/ccl0326/</link><description>about:blank</description><language>zh-cn</language><lastBuildDate>Sun, 05 Apr 2026 01:14:53 GMT</lastBuildDate><pubDate>Sun, 05 Apr 2026 01:14:53 GMT</pubDate><ttl>60</ttl><item><title>linux内核情景分析笔记-存储管理</title><link>http://www.cppblog.com/ccl0326/archive/2011/03/15/141904.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Tue, 15 Mar 2011 09:47:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2011/03/15/141904.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/141904.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2011/03/15/141904.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/141904.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/141904.html</trackback:ping><description><![CDATA[<p>第2章 存储管理<br>LINUX页式管理<br>PGD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PMD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PTE<br>页表目标&nbsp;&nbsp;&nbsp;&nbsp; 中间目录&nbsp;&nbsp;&nbsp;&nbsp; 页表&nbsp;&nbsp;&nbsp;&nbsp; 页表项</p>
<p>LINUX在32位地址下采取二层映射<br>#define PGDIR_SHIFT 22<br>#define PTRS_PER_PGD 1024</p>
<p>#define PMD_SHIFT 22<br>#define PTRS_PER_PMD 1</p>
<p>#define PTRS_PER_PTE 1024<br>根据以上宏定义,PMD被完美的架空了，而相当于采取了二层映射</p>
<p>其中PGD用了线性地址的最高10位 与&nbsp; MMU 对应<br>线性地址的中间10位是所对应的PTE在PT中的索引<br>剩下的最低12位则是页中的偏移量</p>
<p>虚拟地址 = 段基地址：段偏移量<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 16位&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 32位<br>更准确的讲是段选择子了吧<br><br></p>
<p>在LINUX中段基地址 = 0（下面的____KERNEL_CS等），所以可以认为线性地址与虚拟地址总是相等的，但其本质不是一个东西<br></p>
<p><br>0xC0000000-0xFFFFFFFF为内核占用<br>0x0-0xBFFFFFFF为用户控件 </p>
<p><br>内核的虚拟内存为简单的线性映射<br>#__PAGE_OFFSET (0xC0000000)<br>#define PAGE_OFFSET&nbsp; ((unsigned long) __PAGE_OFFSET)<br>#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET)<br>#define __va(x)&nbsp;((void *)((unsigned long)(x) +PAGE_OFFSET)</p>
<p>__pa是从虚拟地址转换成物理地址<br>__va是从物理地址转换成虚拟地址</p>
<p><br>在GDT中有4个段描述符<br>其索引是2-5<br>分别是<br>__KERNEL_CS&nbsp;内核代码段<br>__KERNEL_DS&nbsp;内核数据段<br>__USER_CS&nbsp;用户代码段<br>__USER_DS&nbsp;用户数据段</p>
<p>#define start_thread(regs,new_eip,new_esp) do {\<br>&nbsp;__asm__("movl %0,%%fs;movl %0,%%gs"::"r"(0)); \<br>&nbsp;set_fs(USER_DS);<br>&nbsp;regs-&gt;xds = __USER_DS;&nbsp;\<br>&nbsp;regs-&gt;xes = __USER_DS; \<br>&nbsp;regs-&gt;xss = __USER_DS; \<br>&nbsp;regs-&gt;xcs = __USER_CS; \<br>&nbsp;regs-&gt;eip = new_eip;&nbsp;&nbsp; \<br>&nbsp;regs-&gt;esp = new_esp;&nbsp;&nbsp; \</p>
<p>}while(0)</p>
<p>通过这段宏可以看出，LINUX没用段式存储，虽然它也走了这个流程</p>
<p>&nbsp;</p>
<p>MMU的流程 MMU使用物理地址</p>
<p>页式映射<br>从REG CR3拿PGD的地址<br>找到页面目录，线性地址中的高10位为索引，找到页面目录项，从中拿高20位作为页面表的索引，页面表与4k字节边界对齐，CPU自动补充前12位为0得到页面表地址。</p>
<p>然后拿线性地址的中间10位，得到页面表中的索引，拿到页面表项，页面表项的高20位在低位补充12个0，再加上线性地址的低12位组成物理地址。</p>
<p><br>mm_struct 任务相关的虚拟内存<br>vm_area_struct 一段虚拟内存的抽象，也可以理解为段<br>mm_struct中拥有vm_area_struct的指针<br>在vm_area_struct多的时候使用avl树来存储<br>mem_map_t&nbsp; 物理页表<br>zone_struct 物理内存的区结构，zone_struct把物理内存分成了几个部分<br>ZONE_DMA 0&nbsp;供DMA使用<br>ZONE_NORMAL&nbsp;普通使用<br>ZONE_HIGHMEN&nbsp;高段内存，内核映射不到</p>
<p>物理内存之间区的划分并不是强制的，如果某一个区已经没有内存可用，是可以去别的区拿内存的</p>
<p>其实一直对内核的寻址有些疑问<br>不过刚刚似乎想通了<br>内核会做预映射，把PGD第768项以后的都做映射，也就是1G的空间<br>而这种映射应该是满足__pa()宏，即线性地址与物理地址是线性映射的。<br>所以最终__pa()宏被用作在内核代码中显性的获得某个线性地址所对应的物理地址<br>而MMU负责把一个线性地址隐式的转成了物理地址，而这已转换与内核代码无关。<br>不知这样理解是否正确？<br></p>
<p>今天只看到了这里<br>待续&#8230;&#8230;<br><br>说起来把这么个东西放到首页很不好意思，主要目的是希望有看到的人帮我指正一下我所认知的错误或者解惑。谢谢啦:)</p>
<img src ="http://www.cppblog.com/ccl0326/aggbug/141904.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2011-03-15 17:47 <a href="http://www.cppblog.com/ccl0326/archive/2011/03/15/141904.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 关于CppUnit的使用？</title><link>http://www.cppblog.com/ccl0326/archive/2011/03/03/141026.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Thu, 03 Mar 2011 01:56:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2011/03/03/141026.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/141026.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2011/03/03/141026.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/141026.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/141026.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp; 首先，我是初学者，从无使用CppUnit的经验。</p>
<p>&nbsp;&nbsp;&nbsp; 项目进展到后期，想提高稳定性，在这个时候是否适合加入单元测试呢？<br>&nbsp;&nbsp;&nbsp; 找了几篇文章看了看，似乎谈单元测试就不得不谈TDD，但我看到的绝大多数人的观点是舍TDD,留单元测试<br>&nbsp;&nbsp;&nbsp; 好了，暂且不谈TDD，只谈单元测试，其中我看到一篇Blog的观点是，使用单元测试，来逐步重构代码，其论点就是真正适合单元测试的代码，能够更符合SOLID原则。<br>&nbsp;&nbsp;&nbsp;&nbsp; 这个看法是我目前比较认同的。<br>&nbsp;&nbsp;&nbsp;&nbsp; 另外看到一种使用方式是，只对算法类和一些基础类，进行单元测试。对于这种使用方式，我实不知使用CppUnit这种现成的较为自动化的单元测试的框架意义在哪。<br>&nbsp;&nbsp;&nbsp; 最后一个问题就是，如果我认为的前一种观点是正确的，那在我们的这个项目进行到的这个阶段，想要提高稳定性去做单元测试，是需要大量重构的，这样一个量会不会反而会影响稳定性？</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp; 欢迎大家随意发表自己的观点哈，我只是想多了解一下。以便做出认为自己更适合的判断。</p>
<img src ="http://www.cppblog.com/ccl0326/aggbug/141026.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2011-03-03 09:56 <a href="http://www.cppblog.com/ccl0326/archive/2011/03/03/141026.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 关于lua的coroutine</title><link>http://www.cppblog.com/ccl0326/archive/2011/02/15/140073.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Tue, 15 Feb 2011 03:01:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2011/02/15/140073.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/140073.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2011/02/15/140073.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/140073.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/140073.html</trackback:ping><description><![CDATA[本质是串行,没有同步开销<br>虽然在用户态下，但是还是需要维护coroutine的状态，<br>也就是也会拥有自己的上下文切换的开销<br>更像是用户态下实现的线程，但抢占式的线程不论切换粒度再大或再小，都会有不可预知的行为，所要做同步，而coroutine是协作式的任务，<br>由自己交把自己的权利交出去，同步问题自然不用考虑了<br>自己小记一下
<img src ="http://www.cppblog.com/ccl0326/aggbug/140073.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2011-02-15 11:01 <a href="http://www.cppblog.com/ccl0326/archive/2011/02/15/140073.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lisp的本质(The Nature of Lisp)(转)</title><link>http://www.cppblog.com/ccl0326/archive/2011/02/15/140071.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Tue, 15 Feb 2011 01:29:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2011/02/15/140071.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/140071.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2011/02/15/140071.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/140071.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/140071.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Lisp的本质(The Nature of Lisp)<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 作者 Slava Akhmechet<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 译者 Alec Jang<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 出处: http://www.defmacro.org/ramblings/lisp.html<br><br><br>简介<br><br>最初在web的某些角落偶然看到有人赞美Lisp时, 我那时已经是一个颇有经验的程序员。<br>在我的履历上, 掌握的语言范围相当广泛, 象C++, Java, C#主流语言等等都不在话下, <br>我觉得我差不多知道所有的有关编程语言的事情。对待编程语言的问题上, 我觉得自己不<br>太会遇到什么大问题。其实我大错特错了。<br><br>我试着学了一下Lisp, 结果马上就撞了墙。我被那些范例代码吓坏了。我想很多初次接触<br>Lisp语言的人, 一定也有过类似的感受。Lisp的语法太次了。一个语言的发明人, 居然不<br>肯用心弄出一套漂亮的语法, 那谁还会愿意学它。反正, 我是确确实实被那些难看的无数<br>的括号搞蒙了。<br><br>回过神来之后, 我和Lisp社区的那伙人交谈, 诉说我的沮丧心情。结果, 立马就有一大套<br>理论砸过来, 这套理论在Lisp社区处处可见, 几成惯例。比如说: Lisp的括号只是表面现<br>象; Lisp的代码和数据的表达方式没有差别, 而且比XML语法高明许多, 所以有无穷的好<br>处; Lisp有强大无比的元语言能力, 程序员可以写出自我维护的代码; Lisp可以创造出针<br>对特定应用的语言子集; Lisp的运行时和编译时没有明确的分界; 等等, 等等, 等等。这<br>么长的赞美词虽然看起来相当动人, 不过对我毫无意义。没人能给我演示这些东西是如何<br>应用的, 因为这些东西一般来说只有在大型系统才会用到。我争辩说, 这些东西传统语言<br>一样办得到。在和别人争论了数个小时之后, 我最终还是放弃了学Lisp的念头。为什么要<br>花费几个月的时间学习语法这么难看的语言呢? 这种语言的概念这么晦涩, 又没什么好懂<br>的例子。也许这语言不是该我这样的人学的。<br><br>几个月来, 我承受着这些Lisp辩护士对我心灵的重压。我一度陷入了困惑。我认识一些绝<br>顶聪明的人, 我对他们相当尊敬, 我看到他们对Lisp的赞美达到了宗教般的高度。这就是<br>说, Lisp中一定有某种神秘的东西存在, 我不能忍受自己对此的无知, 好奇心和求知欲最<br>终不可遏制。我于是咬紧牙关埋头学习Lisp, 经过几个月的时间费劲心力的练习, 终于,<br>我看到了那无穷无尽的泉水的源头。在经过脱胎换骨的磨练之后, 在经过七重地狱的煎熬<br>之后, 终于, 我明白了。<br><br>顿悟在突然之间来临。曾经许多次, 我听到别人引用雷蒙德(译者注: 论文&lt;&lt;大教堂和市<br>集&gt;&gt;的作者, 著名的黑客社区理论家)的话: "Lisp语言值得学习。当你学会Lisp之后, 你<br>会拥有深刻的体验。就算你平常并不用Lisp编程, 它也会使你成为更加优秀的程序员"。<br>过去, 我根本不懂这些话的含义, 我也不相信这是真的。可是现在我懂得了。这些话蕴含<br>的真理远远超过我过去的想像。我内心体会到一种神圣的情感, 一瞬间的顿悟, 几乎使我<br>对电脑科学的观念发生了根本的改变。<br><br>顿悟的那一刻, 我成了Lisp的崇拜者。我体验到了宗教大师的感受: 一定要把我的知识传<br>布开来, 至少要让10个迷失的灵魂得到拯救。按照通常的办法, 我把这些道理(就是刚开<br>始别人砸过来的那一套, 不过现在我明白了真实的含义)告诉旁人。结果太令人失望了, <br>只有少数几个人在我坚持之下, 发生了一点兴趣, 但是仅仅看了几眼Lisp代码, 他们就退<br>却了。照这样的办法, 也许费数年功夫能造就了几个Lisp迷, 但我觉得这样的结果太差强<br>人意了, 我得想一套有更好的办法。<br><br>我深入地思考了这个问题。是不是Lisp有什么很艰深的东西, 令得那么多老练的程序员都<br>不能领会? 不是, 没有任何绝对艰深的东西。因为我能弄懂, 我相信其他人也一定能。那<br>么问题出在那里? 后来我终于找到了答案。我的结论就是, 凡是教人学高级概念, 一定要<br>从他已经懂得的东西开始。如果学习过程很有趣, 学习的内容表达得很恰当, 新概念就会<br>变得相当直观。这就是我的答案。所谓元编程, 所谓数据和代码形式合一, 所谓自修改代<br>码, 所谓特定应用的子语言, 所有这些概念根本就是同族概念, 彼此互为解释, 肯定越讲<br>越不明白。还是从实际的例子出发最有用。<br><br>我把我的想法说给Lisp程序员听, 遭到了他们的反对。"这些东西本身当然不可能用熟悉<br>的知识来解释, 这些概念完全与众不同, 你不可能在别人已有的经验里找到类似的东西",<br>可是我认为这些都是遁词。他们又反问我, "你自己为啥不试一下?" 好吧, 我来试一下。<br>这篇文章就是我尝试的结果。我要用熟悉的直观的方法来解释Lisp, 我希望有勇气的人读<br>完它, 拿杯饮料, 深呼吸一下, 准备被搞得晕头转向。来吧, 愿你获得大能。<br><br>重新审视XML<br><br>千里之行始于足下。让我们的第一步从XML开始。可是XML已经说得更多的了, 还能有什么<br>新意思可说呢? 有的。XML自身虽然谈谈不上有趣, 但是XML和Lisp的关系却相当有趣。<br>XML和Lisp的概念有着惊人的相似之处。XML是我们通向理解Lisp的桥梁。好吧, 我们且把<br>XML当作活马医。让我们拿好手杖, 对XML的无人涉及的荒原地带作一番探险。我们要从一<br>个全新的视角来考察这个题目。<br><br>表面上看, XML是一种标准化语法, 它以适合人阅读的格式来表达任意的层次化数据<br>(hirearchical data)。象任务表(to-do list), 网页, 病历, 汽车保险单, 配置文件等<br>等, 都是XML用武的地方。比如我们拿任务表做例子:<br><br>&lt;todo name="housework"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;item priority="high"&gt;Clean the house.&lt;/item&gt;<br>&nbsp;&nbsp;&nbsp; &lt;item priority="medium"&gt;Wash the dishes.&lt;/item&gt;<br>&nbsp;&nbsp;&nbsp; &lt;item priority="medium"&gt;Buy more soap.&lt;/item&gt;<br>&lt;/todo&gt;<br><br>解析这段数据时会发生什么情况? 解析之后的数据在内存中怎样表示? 显然, 用树来表示<br>这种层次化数据是很恰当的。说到底, XML这种比较容易阅读的数据格式, 就是树型结构<br>数据经过序列化之后的结果。任何可以用树来表示的数据, 同样可以用XML来表示, 反之<br>亦然。希望你能懂得这一点, 这对下面的内容极其重要。<br><br>再进一步。还有什么类型的数据也常用树来表示? 无疑列表(list)也是一种。上过编译课<br>吧? 还模模糊糊记得一点吧? 源代码在解析之后也是用树结构来存放的, 任何编译程序都<br>会把源代码解析成一棵抽象语法树, 这样的表示法很恰当, 因为源代码就是层次结构的: <br>函数包含参数和代码块, 代码快包含表达式和语句, 语句包含变量和运算符等等。<br><br>我们已经知道, 任何树结构都可以轻而易举的写成XML, 而任何代码都会解析成树, 因此,<br>任何代码都可以转换成XML, 对不对? 我举个例子, 请看下面的函数:<br><br>int add(int arg1, int arg2)<br>{<br>&nbsp;&nbsp;&nbsp; return arg1+arg2;<br>}<br><br>能把这个函数变成对等的XML格式吗? 当然可以。我们可以用很多种方式做到, 下面是其<br>中的一种, 十分简单:<br><br>&lt;define-function return-type="int" name="add"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;arguments&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;argument type="int"&gt;arg1&lt;/argument&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;argument type="int"&gt;arg2&lt;/argument&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/arguments&gt;<br>&nbsp;&nbsp;&nbsp; &lt;body&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;return&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;add value1="arg1" value2="arg2" /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/return&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/body&gt;<br>&lt;/define&gt;<br><br>这个例子非常简单, 用哪种语言来做都不会有太大问题。我们可以把任何程序码转成XML,<br>也可以把XML转回到原来的程序码。我们可以写一个转换器, 把Java代码转成XML, 另一个<br>转换器把XML转回到Java。一样的道理, 这种手段也可以用来对付C++(这样做跟发疯差不<br>多么。可是的确有人在做, 看看GCC-XML(http://www.gccxml.org)就知道了)。进一步说,<br>凡是有相同语言特性而语法不同的语言, 都可以把XML当作中介来互相转换代码。实际上<br>几乎所有的主流语言都在一定程度上满足这个条件。我们可以把XML作为一种中间表示法,<br>在两种语言之间互相译码。比方说, 我们可以用Java2XML把Java代码转换成XML, 然后用<br>XML2CPP再把XML转换成C++代码, 运气好的话, 就是说, 如果我们小心避免使用那些C++不<br>具备的Java特性的话, 我们可以得到完好的C++程序。这办法怎么样, 漂亮吧?<br><br>这一切充分说明, 我们可以把XML作为源代码的通用存储方式, 其实我们能够产生一整套<br>使用统一语法的程序语言, 也能写出转换器, 把已有代码转换成XML格式。如果真的采纳<br>这种办法, 各种语言的编译器就用不着自己写语法解析了, 它们可以直接用XML的语法解<br>析来直接生成抽象语法树。<br><br>说到这里你该问了, 我们研究了这半天XML, 这和Lisp有什么关系呢? 毕竟XML出来之时,<br>Lisp早已经问世三十年了。这里我可以保证, 你马上就会明白。不过在继续解释之前, 我<br>们先做一个小小的思维练习。看一下上面这个XML版本的add函数例子, 你怎样给它分类, <br>是代码还是数据? 不用太多考虑都能明白, 把它分到哪一类都讲得通。它是XML, 它是标<br>准格式的数据。我们也知道, 它可以通过内存中的树结构来生成(GCC-XML做的就是这个事<br>情)。它保存在不可执行的文件中。我们可以把它解析成树节点, 然后做任意的转换。显<br>而易见, 它是数据。不过且慢, 虽然它语法有点陌生, 可它又确确实实是一个add函数, <br>对吧?&nbsp; 一旦经过解析, 它就可以拿给编译器编译执行。我们可以轻而易举写出这个XML <br>代码解释器, 并且直接运行它。或者我们也可以把它译成Java或C++代码, 然后再编译运<br>行。所以说, 它也是代码。<br><br>我们说到那里了? 不错, 我们已经发现了一个有趣的关键之点。过去被认为很难解的概念<br>已经非常直观非常简单的显现出来。代码也是数据, 并且从来都是如此。这听起来疯疯癫<br>癫的, 实际上却是必然之事。我许诺过会以一种全新的方式来解释Lisp, 我要重申我的许<br>诺。但是我们此刻还没有到预定的地方, 所以还是先继续上边的讨论。<br><br>刚才我说过, 我们可以非常简单地实现XML版的add函数解释器, 这听起来好像不过是说说<br>而已。谁真的会动手做一下呢? 未必有多少人会认真对待这件事。随便说说, 并不打算真<br>的去做, 这样的事情你在生活中恐怕也遇到吧。你明白我这样说的意思吧, 我说的有没有<br>打动你? 有哇, 那好, 我们继续。<br><br>重新审视Ant<br><br>我们现在已经来到了月亮背光的那一面, 先别忙着离开。再探索一下, 看看我们还能发现<br>什么东西。闭上眼睛, 想一想2000年冬天的那个雨夜, 一个名叫James Duncan Davidson <br>的杰出的程序员正在研究Tomcat的servlet容器。那时, 他正小心地保存好刚修改过的文<br>件, 然后执行make。结果冒出了一大堆错误, 显然有什么东西搞错了。经过仔细检查, 他<br>想, 难道是因为tab前面加了个空格而导致命令不能执行吗? 确实如此。老是这样, 他真<br>的受够了。乌云背后的月亮给了他启示, 他创建了一个新的Java项目, 然后写了一个简单<br>但是十分有用的工具, 这个工具巧妙地利用了Java属性文件中的信息来构造工程, 现在<br>James可以写makefile的替代品, 它能起到相同的作用, 而形式更加优美, 也不用担心有<br>makefile那样可恨的空格问题。这个工具能够自动解释属性文件, 然后采取正确的动作来<br>编译工程。真是简单而优美。<br><br>(作者注: 我不认识James, James也不认识我, 这个故事是根据网上关于Ant历史的帖子<br>虚构的)<br><br>使用Ant构造Tomcat之后几个月, 他越来越感到Java的属性文件不足以表达复杂的构造指<br>令。文件需要检出, 拷贝, 编译, 发到另外一台机器, 进行单元测试。要是出错, 就发邮<br>件给相关人员, 要是成功, 就继续在尽可能高层的卷(volumn)上执行构造。追踪到最后, <br>卷要回复到最初的水平上。确实, Java的属性文件不够用了, James需要更有弹性的解决<br>方案。他不想自己写解析器(因为他更希望有一个具有工业标准的方案)。XML看起来是个<br>不错的选择。他花了几天工夫把Ant移植到XML，于是，一件伟大的工具诞生了。<br><br>Ant是怎样工作的？原理非常简单。Ant把包含有构造命令的XML文件(算代码还是算数据, <br>你自己想吧)，交给一个Java程序来解析每一个元素，实际情况比我说的还要简单得多。<br>一个简单的XML指令会导致具有相同名字的Java类装入，并执行其代码。<br><br>&nbsp;&nbsp;&nbsp; &lt;copy todir="../new/dir"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="src_dir" /&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/copy&gt;<br><br>这段文字的含义是把源目录复制到目标目录，Ant会找到一个"copy"任务(实际上就是一个<br>Java类), 通过调用Java的方法来设置适当参数(todir和fileset)，然后执行这个任务。<br>Ant带有一组核心类, 可以由用户任意扩展, 只要遵守若干约定就可以。Ant找到这些类, <br>每当遇到XML元素有同样的名字, 就执行相应的代码。过程非常简单。Ant做到了我们前面<br>所说的东西: 它是一个语言解释器, 以XML作为语法, 把XML元素转译为适当的Java指令。<br>我们可以写一个"add"任务, 然后, 当发现XML中有add描述的时候, 就执行这个add任务。<br>由于Ant是非常流行的项目, 前面展示的策略就显得更为明智。毕竟, 这个工具每天差不<br>多有几千家公司在使用。<br><br>到目前为之, 我还没有说Ant在解析XML时所遇到困难。你也不用麻烦去它的网站上去找答<br>案了, 不会找到有价值的东西。至少对我们这个论题来说是如此。我们还是继续下一步讨<br>论吧。我们答案就在那里。<br><br>为什么是XML<br><br>有时候正确的决策并非完全出于深思熟虑。我不知道James选择XML是否出于深思熟虑。也<br>许仅仅是个下意识的决定。至少从James在Ant网站上发表的文章看起来, 他所说的理由完<br>全是似是而非。他的主要理由是移植性和扩展性, 在Ant案例上, 我看不出这两条有什么<br>帮助。使用XML而不是Java代码, 到底有什么好处? 为什么不写一组Java类, 提供api来满<br>足基本任务(拷贝目录, 编译等等), 然后在Java里直接调用这些代码? 这样做仍然可以保<br>证移植性, 扩展性也是毫无疑问的。而且语法也更为熟悉, 看着顺眼。那为什么要用 XML<br>呢? 有什么更好的理由吗?<br><br>有的。虽然我不确定James是否确实意识到了。在语义的可构造性方面, XML的弹性是Java<br>望尘莫及的。我不想用高深莫测的名词来吓唬你, 其中的道理相当简单, 解释起来并不费<br>很多功夫。好, 做好预备动作, 我们马上就要朝向顿悟的时刻做奋力一跃。<br><br>上面的那个copy的例子, 用Java代码怎样实现呢? 我们可以这样做:<br><br>&nbsp;&nbsp;&nbsp; CopyTask copy = new CopyTask();<br>&nbsp;&nbsp;&nbsp; Fileset fileset = new Fileset();<br><br>&nbsp;&nbsp;&nbsp; fileset.setDir("src_dir");<br>&nbsp;&nbsp;&nbsp; copy.setToDir("../new/dir");<br>&nbsp;&nbsp;&nbsp; copy.setFileset(fileset);<br><br>&nbsp;&nbsp;&nbsp; copy.excute();<br><br>这个代码看起来和XML的那个很相似, 只是稍微长一点。差别在那里? 差别在于XML构造了<br>一个特殊的copy动词, 如果我们硬要用Java来写的话, 应该是这个样子:<br><br>&nbsp;&nbsp;&nbsp; copy("../new/dir");<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fileset("src_dir");<br>&nbsp;&nbsp;&nbsp; }<br><br>看到差别了吗? 以上代码(如果可以在Java中用的化), 是一个特殊的copy算符, 有点像<br>for循环或者Java5中的foreach循环。如果我们有一个转换器, 可以把XML转换到Java, 大<br>概就会得到上面这段事实上不可以执行的代码。因为Java的技术规范是定死的, 我们没有<br>办法在程序里改变它。我们可以增加包, 增加类, 增加方法, 但是我们没办法增加算符, <br>而对于XML, 我们显然可以任由自己增加这样的东西。对于XML的语法树来说, 只要原意, <br>我们可以任意增加任何元素, 因此等于我们可以任意增加算符。如果你还不太明白的话, <br>看下面这个例子, 加入我们要给Java引入一个unless算符:<br><br>&nbsp;&nbsp;&nbsp; unless(someObject.canFly())<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; someObject.transportByGround():<br>&nbsp;&nbsp;&nbsp; }<br><br>在上面的两个例子中, 我们打算给Java语法扩展两个算符, 成组拷贝文件算符和条件算符<br>unless, 我们要想做到这一点, 就必须修改Java编译器能够接受的抽象语法树, 显然我们<br>无法用Java标准的功能来实现它。但是在XML中我们可以轻而易举地做到。我们的解析器<br>根据 XML元素, 生成抽象语法树, 由此生成算符, 所以, 我们可以任意引入任何算符。<br><br>对于复杂的算符来说, 这样做的好处显而易见。比如, 用特定的算符来做检出源码, 编译<br>文件, 单元测试, 发送邮件等任务, 想想看有多么美妙。对于特定的题目, 比如说构造软<br>件项目, 这些算符的使用可以大幅减低少代码的数量。增加代码的清晰程度和可重用性。<br>解释性的XML可以很容易的达到这个目标。XML是存储层次化数据的简单数据文件, 而在<br>Java中, 由于层次结构是定死的(你很快就会看到, Lisp的情况与此截然不同), 我们就没<br>法达到上述目标。也许这正是Ant的成功之处呢。<br><br>你可以注意一下最近Java和C#的变化(尤其是C#3.0的技术规范), C#把常用的功能抽象出<br>来, 作为算符增加到C#中。C#新增加的query算符就是一个例子。它用的还是传统的作法:<br>C#的设计者修改抽象语法树, 然后增加对应的实现。如果程序员自己也能修改抽象语法树<br>该有多好! 那样我们就可以构造用于特定问题的子语言(比如说就像Ant这种用于构造项目<br>的语言), 你能想到别的例子吗? 再思考一下这个概念。不过也不必思考太甚, 我们待会<br>还会回到这个题目。那时候就会更加清晰。<br><br>离Lisp越来越近<br><br>我们先把算符的事情放一放, 考虑一下Ant设计局限之外的东西。我早先说过, Ant可以通<br>过写Java类来扩展。Ant解析器会根据名字来匹配XML元素和Java类, 一旦找到匹配, 就执<br>行相应任务。为什么不用Ant自己来扩展Ant呢? 毕竟核心任务要包含很多传统语言的结构<br>(例如"if"), 如果Ant自身就能提供构造任务的能力(而不是依赖java类), 我们就可以得<br>到更高的移植性。我们将会依赖一组核心任务(如果你原意, 也不妨把它称作标准库), 而<br>不用管有没有Java 环境了。这组核心任务可以用任何方式来实现, 而其他任务建筑在这<br>组核心任务之上, 那样的话, Ant就会成为通用的, 可扩展的, 基于XML的编程语言。考虑<br>下面这种代码的可能性:<br><br>&nbsp;&nbsp;&nbsp; &lt;task name="Test"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;echo message="Hello World" /&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/task&gt;<br>&nbsp;&nbsp;&nbsp; &lt;Test /&gt;<br><br>如果XML支持"task"的创建, 上面这段代码就会输出"Hello World!". 实际上, 我们可以<br>用Java写个"task"任务, 然后用Ant-XML来扩展它。Ant可以在简单原语的基础上写出更复<br>杂的原语, 就像其他编程语言常用的作法一样。这也就是我们一开始提到的基于XML的编<br>程语言。这样做用处不大(你知道为甚么吗?), 但是真的很酷。<br><br>再看一回我们刚才说的Task任务。祝贺你呀, 你在看Lisp代码!!! 我说什么? 一点都不像<br>Lisp吗? 没关系, 我们再给它收拾一下。<br><br>比XML更好<br><br>前面一节说过, Ant自我扩展没什么大用, 原因在于XML很烦琐。对于数据来说, 这个问题<br>还不太大, 但如果代码很烦琐的话, 光是打字上的麻烦就足以抵消它的好处。你写过Ant <br>的脚本吗? 我写过, 当脚本达到一定复杂度的时候, XML非常让人厌烦。想想看吧, 为了<br>写结束标签, 每个词都得打两遍, 不发疯算好的!<br><br>为了解决这个问题, 我们应当简化写法。须知, XML仅仅是一种表达层次化数据的方式。<br>我们并不是一定要使用尖括号才能得到树的序列化结果。我们完全可以采用其他的格式。<br>其中的一种(刚好就是Lisp所采用的)格式, 叫做s表达式。s表达式要做的和XML一样, 但<br>它的好处是写法更简单, 简单的写法更适合代码输入。后面我会详细讲s表达式。这之前<br>我要清理一下XML的东西。考虑一下关于拷贝文件的例子:<br><br>&nbsp;&nbsp;&nbsp; &lt;copy toDir="../new/dir"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="src_dir"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/copy&gt;<br><br>想想看在内存里面, 这段代码的解析树在内存会是什么样子? 会有一个"copy"节点, 其下<br>有一个 "fileset"节点, 但是属性在哪里呢? 它怎样表达呢? 如果你以前用过XML, 并且<br>弄不清楚该用元素还是该用属性, 你不用感到孤单, 别人一样糊涂着呢。没人真的搞得清<br>楚。这个选择与其说是基于技术的理由, 还不如说是闭着眼瞎摸。从概念上来讲, 属性也<br>是一种元素, 任何属性能做的, 元素一样做得到。XML引入属性的理由, 其实就是为了让<br>XML写法不那么冗长。比如我们看个例子:<br><br>&nbsp;&nbsp;&nbsp; &lt;copy&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;toDir&gt;../new/dir&lt;/toDir&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;dir&gt;src_dir&lt;/dir&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/fileset&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/copy&gt;<br><br>两下比较, 内容的信息量完全一样, 用属性可以减少打字数量。如果XML没有属性的话, <br>光是打字就够把人搞疯掉。<br><br>说完了属性的问题, 我们再来看一看s表达式。之所以绕这么个弯, 是因为s表达式没有属<br>性的概念。因为s表达式非常简练, 根本没有必要引入属性。我们在把XML转换成s表达式<br>的时候, 心里应该记住这一点。看个例子, 上面的代码译成s表达式是这样的:<br><br>&nbsp;&nbsp;&nbsp; (copy <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (todir "../new/dir")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (fileset (dir "src_dir")))<br><br>仔细看看这个例子, 差别在哪里? 尖括号改成了圆括号, 每个元素原来是有一对括号标记<br>包围的, 现在取消了后一个(就是带斜杠的那个)括号标记。表示元素的结束只需要一个")"<br>就可以了。不错, 差别就是这些。这两种表达方式的转换, 非常自然, 也非常简单。s表<br>达式打起字来, 也省事得多。第一次看s表达式(Lisp)时, 括号很烦人是吧? 现在我们明<br>白了背后的道理, 一下子就变得容易多了。至少, 比XML要好的多。用s表达式写代码, 不<br>单是实用, 而且也很让人愉快。s表达式具有XML的一切好处, 这些好处是我们刚刚探讨过<br>的。现在我们看看更加Lisp风格的task例子:<br><br>&nbsp;&nbsp;&nbsp; (task (name "Test")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (echo (message "Hellow World!")))<br>&nbsp;&nbsp;&nbsp; (Test)<br><br>用Lisp的行话来讲, s表达式称为表(list)。对于上面的例子, 如果我们写的时候不加换<br>行, 用逗号来代替空格, 那么这个表达式看起来就非常像一个元素列表, 其中又嵌套着其<br>他标记。<br><br>&nbsp;&nbsp;&nbsp; (task, (name, "test"), (echo, (message, "Hello World!")))<br><br>XML自然也可以用这样的风格来写。当然上面这句并不是一般意义上的元素表。它实际上<br>是一个树。这和XML的作用是一样的。称它为列表, 希望你不会感到迷惑, 因为嵌套表和<br>树实际上是一码事。Lisp的字面意思就是表处理(list processing), 其实也可以称为树<br>处理, 这和处理XML节点没有什么不同。<br><br>经受这一番折磨以后, 现在我们终于相当接近Lisp了, Lisp的括号的神秘本质(就像许多<br>Lisp狂热分子认为的)逐渐显现出来。现在我们继续研究其他内容。<br><br>重新审视C语言的宏<br><br>到了这里, 对XML的讨论你大概都听累了, 我都讲累了。我们先停一停, 把树, s表达式,<br>Ant这些东西先放一放, 我们来说说C的预处理器。一定有人问了, 我们的话题和C有什么<br>关系? 我们已经知道了很多关于元编程的事情, 也探讨过专门写代码的代码。理解这问题<br>有一定难度, 因为相关讨论文章所使用的编程语言, 都是你们不熟悉的。但是如果只论概<br>念的话, 就相对要简单一些。我相信, 如果以C语言做例子来讨论元编程, 理解起来一定<br>会容易得多。好, 我们接着看。<br><br>一个问题是, 为什么要用代码来写代码呢? 在实际的编程中, 怎样做到这一点呢? 到底元<br>编程是什么意思? 你大概已经听说过这些问题的答案, 但是并不懂得其中缘由。为了揭示<br>背后的真理, 我们来看一下一个简单的数据库查询问题。这种题目我们都做过。比方说, <br>直接在程序码里到处写SQL语句来修改表(table)里的数据, 写多了就非常烦人。即便用<br>C#3.0的LINQ, 仍然不减其痛苦。写一个完整的SQL查询(尽管语法很优美)来修改某人的地<br>址, 或者查找某人的名字, 绝对是件令程序员倍感乏味的事情, 那么我们该怎样来解决这<br>个问题? 答案就是: 使用数据访问层。 <br><br>概念挺简单, 其要点是把数据访问的内容(至少是那些比较琐碎的部分)抽象出来, 用类来<br>映射数据库的表, 然后用访问对象属性访问器(accessor)的办法来间接实现查询。这样就<br>极大地简化了开发工作量。我们用访问对象的方法(或者属性赋值, 这要视你选用的语言<br>而定)来代替写SQL查询语句。凡是用过这种方法的人, 都知道这很节省时间。当然, 如果<br>你要亲自写这样一个抽象层, 那可是要花非常多的时间的--你要写一组类来映射表, 把属<br>性访问转换为SQL查询, 这个活相当耗费精力。用手工来做显然是很不明智的。但是一旦<br>你有了方案和模板, 实际上就没有多少东西需要思考的。你只需要按照同样的模板一次又<br>一次重复编写相似代码就可以了。事实上很多人已经发现了更好的方法, 有一些工具可以<br>帮助你连接数据库, 抓取数据库结构定义(schema), 按照预定义的或者用户定制的模板来<br>自动编写代码。<br><br>如果你用过这种工具, 你肯定会对它的神奇效果深为折服。往往只需要鼠标点击数次, 就<br>可以连接到数据库, 产生数据访问源码, 然后把文件加入到你的工程里面, 十几分钟的工<br>作, 按照往常手工方式来作的话, 也许需要数百个小时人工(man-hours)才能完成。可是,<br>如果你的数据库结构定义后来改变了怎么办? 那样的话, 你只需把这个过程重复一遍就可<br>以了。甚至有一些工具能自动完成这项变动工作。你只要把它作为工程构造的一部分, 每<br>次编译工程的时候, 数据库部分也会自动地重新构造。这真的太棒了。你要做的事情基本<br>上减到了0。如果数据库结构定义发生了改变, 并在编译时自动更新了数据访问层的代码,<br>那么程序中任何使用过时的旧代码的地方, 都会引发编译错误。<br><br>数据访问层是个很好的例子, 这样的例子还有好多。从GUI样板代码, WEB代码, COM和<br>CORBA存根, 以及MFC和ATL等等。在这些地方, 都是有好多相似代码多次重复。既然这些<br>代码有可能自动编写, 而程序员时间又远远比CPU时间昂贵, 当然就产生了好多工具来自<br>动生成样板代码。这些工具的本质是什么呢? 它们实际上就是制造程序的程序。它们有一<br>个神秘的名字, 叫做元编程。所谓元编程的本义, 就是如此。<br><br>元编程本来可以用到无数多的地方, 但实际上使用的次数却没有那么多。归根结底, 我们<br>心里还是在盘算, 假设重复代码用拷贝粘贴的话, 大概要重复6,7次, 对于这样的工作量,<br>值得专门建立一套生成工具吗? 当然不值得。数据访问层和COM存根往往需要重用数百次,<br>甚至上千次, 所以用工具生成是最好的办法。而那些仅仅是重复几次十几次的代码, 是没<br>有必要专门做工具的。不必要的时候也去开发代码生成工具, 那就显然过度估计了代码生<br>成的好处。当然, 如果创建这类工具足够简单的话, 还是应当尽量多用, 因为这样做必然<br>会节省时间。现在来看一下有没有合理的办法来达到这个目的。<br><br>现在, C预处理器要派上用场了。我们都用过C/C++的预处理器, 我们用它执行简单的编译<br>指令, 来产生简单的代码变换(比方说, 设置调试代码开关), 看一个例子:<br><br>&nbsp;&nbsp;&nbsp; #define triple(X) X+X+X<br><br>这一行的作用是什么? 这是一个简单的预编译指令, 它把程序中的triple(X)替换称为<br>X+X+X。例如, 把所有的triple(5)都换成5+5+5, 然后再交给编译器编译。这就是一个简<br>单的代码生成的例子。要是C的预处理器再强大一点, 要是能够允许连接数据库, 要是能<br>多一些其他简单的机制, 我们就可以在我们程序的内部开发自己的数据访问层。下面这个<br>例子, 是一个假想的对C宏的扩展:<br><br>&nbsp;&nbsp;&nbsp; #get-db-schema("127.0.0.1")<br>&nbsp;&nbsp;&nbsp; #iterate-through-tables<br>&nbsp;&nbsp;&nbsp; #for-each-table<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class #table-name<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp; #end-for-each<br><br>我们连接数据库结构定义, 遍历数据表, 然后对每个表创建一个类, 只消几行代码就完成<br>了这个工作。这样每次编译工程的时候, 这些类都会根据数据库的定义同步更新。显而易<br>见, 我们不费吹灰之力就在程序内部建立了一个完整的数据访问层, 根本用不着任何外部<br>工具。当然这种作法有一个缺点, 那就是我们得学习一套新的"编译时语言", 另一个缺点<br>就是根本不存在这么一个高级版的C预处理器。需要做复杂代码生成的时候, 这个语言(译<br>者注: 这里指预处理指令, 即作者所说的"编译时语言")本身也一定会变得相当复杂。它<br>必须支持足够多的库和语言结构。比如说我们想要生成的代码要依赖某些ftp服务器上的<br>文件, 预处理器就得支持ftp访问, 仅仅因为这个任务而不得不创造和学习一门新的语言,<br>真是有点让人恶心(事实上已经存在着有此能力的语言, 这样做就更显荒谬)。我们不妨再<br>灵活一点, 为什么不直接用 C/C++自己作为自己的预处理语言呢?&nbsp; 这样子的话, 我们可<br>以发挥语言的强大能力, 要学的新东西也只不过是几个简单的指示字 , 这些指示字用来<br>区别编译时代码和运行时代码。<br><br>&nbsp;&nbsp;&nbsp; &lt;%<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"Enter a number: ";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cin&gt;&gt;n;<br>&nbsp;&nbsp;&nbsp; %&gt;<br>&nbsp;&nbsp;&nbsp; for(int i=0;i&lt; &lt;% n %&gt;;i++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"hello"&lt;&lt;endl;<br>&nbsp;&nbsp;&nbsp; }<br><br>你明白了吗? 在&lt;%和%&gt;标记之间的代码是在编译时运行的, 标记之外的其他代码都是普通<br>代码。编译程序时, 系统会提示你输入一个数, 这个数在后面的循环中会用到。而for循<br>环的代码会被编译。假定你在编译时输入5, for循环的代码将会是:<br><br>&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;5; i++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"hello"&lt;&lt;endl;<br>&nbsp;&nbsp;&nbsp; }<br><br>又简单又有效率, 也不需要另外的预处理语言。我们可以在编译时就充分发挥宿主语言( <br>此处是C/C++)的强大能力, 我们可以很容易地在编译时连接数据库, 建立数据访问层, 就<br>像JSP或者ASP创建网页那样。我们也用不着专门的窗口工具来另外建立工程。我们可以在<br>代码中立即加入必要的工具。我们也用不着顾虑建立这种工具是不是值得, 因为这太容易<br>了, 太简单了。这样子不知可以节省多少时间啊。<br><br>你好, Lisp<br><br>到此刻为止, 我们所知的关于Lisp的指示可以总结为一句话: Lisp是一个可执行的语法更<br>优美的XML, 但我们还没有说Lisp是怎样做到这一点的, 现在开始补上这个话题。 <br><br>Lisp有丰富的内置数据类型, 其中的整数和字符串和其他语言没什么分别。像71或者<br>"hello"这样的值, 含义也和C++或者Java这样的语言大体相同。真正有意思的三种类型是<br>符号(symbol), 表和函数。这一章的剩余部分, 我都会用来介绍这几种类型, 还要介绍<br>Lisp环境是怎样编译和运行源码的。这个过程用Lisp的术语来说通常叫做求值。通读这一<br>节内容, 对于透彻理解元编程的真正潜力, 以及代码和数据的同一性, 和面向领域语言的<br>观念, 都极其重要。万勿等闲视之。我会尽量讲得生动有趣一些, 也希望你能获得一些<br>启发。那好, 我们先讲符号。<br><br>大体上, 符号相当于C++或Java语言中的标志符, 它的名字可以用来访问变量值(例如<br>currentTime, arrayCount, n, 等等), 差别在于, Lisp中的符号更加基本。在C++或<br>Java里面, 变量名只能用字母和下划线的组合, 而Lisp的符号则非常有包容性, 比如, 加<br>号(+)就是一个合法的符号, 其他的像-, =, hello-world, *等等都可以是符号名。符号<br>名的命名规则可以在网上查到。你可以给这些符号任意赋值, 我们这里先用伪码来说明这<br>一点。假定函数set是给变量赋值(就像等号=在C++和Java里的作用), 下面是我们的例子:<br><br>&nbsp;&nbsp;&nbsp; set(test, 5)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 符号test的值为5<br>&nbsp;&nbsp;&nbsp; set(=, 5)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 符号=的值为5<br>&nbsp;&nbsp;&nbsp; set(test, "hello")&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 符号test的值为字符串"hello"<br>&nbsp;&nbsp;&nbsp; set(test, =)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 此时符号=的值为5, 所以test的也为5<br>&nbsp;&nbsp;&nbsp; set(*, "hello")&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 符号*的值为"hello"<br><br>好像有什么不对的地方? 假定我们对*赋给整数或者字符串值, 那做乘法时怎么办? 不管<br>怎么说, *总是乘法呀? 答案简单极了。Lisp中函数的角色十分特殊, 函数也是一种数据<br>类型, 就像整数和字符串一样, 因此可以把它赋值给符号。乘法函数Lisp的内置函数, 默<br>认赋给*, 你可以把其他函数赋值给*, 那样*就不代表乘法了。你也可以把这函数的值存<br>到另外的变量里。我们再用伪码来说明一下:<br><br>&nbsp;&nbsp;&nbsp; *(3,4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 3乘4, 结果是12<br>&nbsp;&nbsp;&nbsp; set(temp, *)&nbsp;&nbsp;&nbsp; // 把*的值, 也就是乘法函数, 赋值给temp<br>&nbsp;&nbsp;&nbsp; set(*, 3)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 把3赋予*<br>&nbsp;&nbsp;&nbsp; *(3,4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 错误的表达式, *不再是乘法, 而是数值3<br>&nbsp;&nbsp;&nbsp; temp(3,4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // temp是乘法函数, 所以此表达式的值为3乘4等于12<br>&nbsp;&nbsp;&nbsp; set(*, temp)&nbsp;&nbsp;&nbsp; // 再次把乘法函数赋予*<br>&nbsp;&nbsp;&nbsp; *(3,4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 3乘4等于12<br><br>再古怪一点, 把减号的值赋给加号:<br><br>&nbsp;&nbsp;&nbsp; set(+, -)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 减号(-)是内置的减法函数<br>&nbsp;&nbsp;&nbsp; +(5, 4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 加号(+)现在是代表减法函数, 结果是5减4等于1<br><br>这只是举例子, 我还没有详细讲函数。Lisp中的函数是一种数据类型, 和整数, 字符串, <br>符号等等一样。一个函数并不必然有一个名字, 这和C++或者Java语言的情形很不相同。<br>在这里函数自己代表自己。事实上它是一个指向代码块的指针, 附带有一些其他信息(例<br>如一组参数变量)。只有在把函数赋予其他符号时, 它才具有了名字, 就像把一个数值或<br>字符串赋予变量一样的道理。你可以用一个内置的专门用于创建函数的函数来创建函数,<br>然后把它赋值给符号fn, 用伪码来表示就是:<br><br>&nbsp;&nbsp;&nbsp; fn [a]<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return *(a, 2);<br>&nbsp;&nbsp;&nbsp; }<br><br>这段代码返回一个具有一个参数的函数, 函数的功能是计算参数乘2的结果。这个函数还<br>没有名字, 你可以把此函数赋值给别的符号:<br><br>&nbsp;&nbsp;&nbsp; set(times-two, fn [a] {return *(a, 2)})<br><br>我们现在可以这样调用这个函数:<br><br>&nbsp;&nbsp;&nbsp; time-two(5)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 返回10<br><br>我们先跳过符号和函数, 讲一讲表。什么是表? 你也许已经听过好多相关的说法。表, 一<br>言以蔽之, 就是把类似XML那样的数据块, 用s表达式来表示。表用一对括号括住, 表中元<br>素以空格分隔, 表可以嵌套。例如(这回我们用真正的Lisp语法, 注意用分号表示注释):<br><br>&nbsp;&nbsp;&nbsp; ()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 空表<br>&nbsp;&nbsp;&nbsp; (1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 含一个元素的表<br>&nbsp;&nbsp;&nbsp; (1 "test")&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 两元素表, 一个元素是整数1, 另一个是字符串<br>&nbsp;&nbsp;&nbsp; (test "hello")&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 两元素表, 一个元素是符号, 另一个是字符串<br>&nbsp;&nbsp;&nbsp; (test (1 2) "hello")&nbsp;&nbsp;&nbsp; ; 三元素表, 一个符号test, 一个含有两个元素1和2的<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 表, 最后一个元素是字符串<br><br>当Lisp系统遇到这样的表时, 它所做的, 和Ant处理XML数据所做的, 非常相似, 那就是试<br>图执行它们。其实, Lisp源码就是特定的一种表, 好比Ant源码是一种特定的XML一样。<br>Lisp执行表的顺序是这样的, 表的第一个元素当作函数, 其他元素当作函数的参数。如果<br>其中某个参数也是表, 那就按照同样的原则对这个表求值, 结果再传递给最初的函数作为<br>参数。这就是基本原则。我们看一下真正的代码:<br><br>&nbsp;&nbsp;&nbsp; (* 3 4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 相当于前面列举过的伪码*(3,4), 即计算3乘4<br>&nbsp;&nbsp;&nbsp; (times-two 5)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 返回10, times-two按照前面的定义是求参数的2倍<br>&nbsp;&nbsp;&nbsp; (3 4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 错误, 3不是函数<br>&nbsp;&nbsp;&nbsp; (time-two)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 错误, times-two要求一个参数<br>&nbsp;&nbsp;&nbsp; (times-two 3 4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 错误, times-two只要求一个参数<br>&nbsp;&nbsp;&nbsp; (set + -)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 把减法函数赋予符号+<br>&nbsp;&nbsp;&nbsp; (+ 5 4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 依据上一句的结果, 此时+表示减法, 所以返回1<br>&nbsp;&nbsp;&nbsp; (* 3 (+ 2 2))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 2+2的结果是4, 再乘3, 结果是12<br><br>上述的例子中, 所有的表都是当作代码来处理的。怎样把表当作数据来处理呢? 同样的,<br>设想一下, Ant是把XML数据当作自己的参数。在Lisp中, 我们给表加一个前缀'来表示数<br>据。<br><br>&nbsp;&nbsp;&nbsp; (set test '(1 2))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; test的值为两元素表<br>&nbsp;&nbsp;&nbsp; (set test (1 2))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 错误, 1不是函数<br>&nbsp;&nbsp;&nbsp; (set test '(* 3 4))&nbsp;&nbsp;&nbsp;&nbsp; ; test的值是三元素表, 三个元素分别是*, 3, 4<br><br>我们可以用一个内置的函数head来返回表的第一个元素, tail函数来返回剩余元素组成的<br>表。<br><br>&nbsp;&nbsp;&nbsp; (head '(* 3 4))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 返回符号*<br>&nbsp;&nbsp;&nbsp; (tail '(* 3 4))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 返回表(3 4)<br>&nbsp;&nbsp;&nbsp; (head (tal '(* 3 4)))&nbsp;&nbsp; ; 返回3<br>&nbsp;&nbsp;&nbsp; (head test)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 返回*<br><br>你可以把Lisp的内置函数想像成Ant的任务。差别在于, 我们不用在另外的语言中扩展<br>Lisp(虽然完全可以做得到), 我们可以用Lisp自己来扩展自己, 就像上面举的times-two<br>函数的例子。Lisp的内置函数集十分精简, 只包含了十分必要的部分。剩下的函数都是作<br>为标准库来实现的。<br><br>Lisp宏<br><br>我们已经看到, 元编程在一个类似jsp的模板引擎方面的应用。我们通过简单的字符串处<br>理来生成代码。但是我们可以做的更好。我们先提一个问题, 怎样写一个工具, 通过查找<br>目录结构中的源文件来自动生成Ant脚本。<br><br>用字符串处理的方式生成Ant脚本是一种简单的方式。当然, 还有一种更加抽象, 表达能<br>力更强, 扩展性更好的方式, 就是利用XML库在内存中直接生成XML节点, 这样的话内存中<br>的节点就可以自动序列化成为字符串。不仅如此, 我们的工具还可以分析这些节点, 对已<br>有的XML文件做变换。通过直接处理XML节点。我们可以超越字符串处理, 使用更高层次的<br>概念, 因此我们的工作就会做的更快更好。<br><br>我们当然可以直接用Ant自身来处理XML变换和制作代码生成工具。或者我们也可以用Lisp<br>来做这项工作。正像我们以前所知的, 表是Lisp内置的数据结构, Lisp含有大量的工具来<br>快速有效的操作表(head和tail是最简单的两个)。而且, Lisp没有语义约束, 你可以构造<br>任何数据结构, 只要你原意。<br><br>Lisp通过宏(macro)来做元编程。我们写一组宏来把任务列表(to-do list)转换为专用领<br>域语言。<br><br>回想一下上面to-do list的例子, 其XML的数据格式是这样的:<br><br>&nbsp;&nbsp;&nbsp; &lt;todo name = "housework"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;item priority = "high"&gt;Clean the hose&lt;/item&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;item priority = "medium"&gt;Wash the dishes&lt;/item&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;item priority = "medium"&gt;Buy more soap&lt;/item&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/todo&gt;<br><br>相应的s表达式是这样的:<br><br>&nbsp;&nbsp;&nbsp; (todo "housework"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (item (priority high) "Clean the house")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (item (priority medium) "Wash the dishes")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (item (priority medium) "Buy more soap"))<br><br>假设我们要写一个任务表的管理程序, 把任务表数据存到一组文件里, 当程序启动时, 从<br>文件读取这些数据并显示给用户。在别的语言里(比如说Java), 这个任务该怎么做? 我们<br>会解析XML文件, 从中得出任务表数据, 然后写代码遍历XML树, 再转换为Java的数据结构<br>(老实讲, 在Java里解析XML真不是件轻松的事情), 最后再把数据展示给用户。现在如果<br>用Lisp, 该怎么做?<br><br>假定要用同样思路的化, 我们大概会用Lisp库来解析XML。XML对我们来说就是一个Lisp <br>的表(s表达式), 我们可以遍历这个表, 然后把相关数据提交给用户。可是, 既然我们用<br>Lisp, 就根本没有必要再用XML格式保存数据, 直接用s表达式就好了, 这样就没有必要做<br>转换了。我们也用不着专门的解析库, Lisp可以直接在内存里处理s表达式。注意, Lisp <br>编译器和.net编译器一样, 对Lisp程序来说, 在运行时总是随时可用的。<br><br>但是还有更好的办法。我们甚至不用写表达式来存储数据, 我们可以写宏, 把数据当作代<br>码来处理。那该怎么做呢? 真的简单。回想一下, Lisp的函数调用格式:<br><br>&nbsp;&nbsp;&nbsp; (function-name arg1 arg2 arg3)<br><br>其中每个参数都是s表达式, 求值以后, 传递给函数。如果我们用(+ 4 5)来代替arg1, <br>那么, 程序会先求出结果, 就是9, 然后把9传递给函数。宏的工作方式和函数类似。主要<br>的差别是, 宏的参数在代入时不求值。<br><br>&nbsp;&nbsp;&nbsp; (macro-name (+ 4 5))<br><br>这里, (+ 4 5)作为一个表传递给宏, 然后宏就可以任意处理这个表, 当然也可以对它求<br>值。宏的返回值是一个表, 然后有程序作为代码来执行。宏所占的位置, 就被替换为这个<br>结果代码。我们可以定义一个宏把数据替换为任意代码, 比方说, 替换为显示数据给用户<br>的代码。<br><br>这和元编程, 以及我们要做的任务表程序有什么关系呢? 实际上, 编译器会替我们工作, <br>调用相应的宏。我们所要做的, 仅仅是创建一个把数据转换为适当代码的宏。<br><br>例如, 上面曾经将过的C的求三次方的宏, 用Lisp来写是这样子:<br><br>&nbsp;&nbsp;&nbsp; (defmacro triple (x)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; `(+ ~x ~x ~x))<br><br>(译注: 在Common Lisp中, 此处的单引号应当是反单引号, 意思是对表不求值, 但可以对<br>表中某元素求值, 记号~表示对元素x求值, 这个求值记号在Common Lisp中应当是逗号。<br>反单引号和单引号的区别是, 单引号标识的表, 其中的元素都不求值。这里作者所用的记<br>号是自己发明的一种Lisp方言Blaise, 和common lisp略有不同, 事实上, 发明方言是<br>lisp高手独有的乐趣, 很多狂热分子都热衷这样做。比如Paul Graham就发明了ARC, 许多<br>记号比传统的Lisp简洁得多, 显得比较现代)<br><br>单引号的用处是禁止对表求值。每次程序中出现triple的时候, <br><br>&nbsp;&nbsp;&nbsp; (triple 4)<br><br>都会被替换成:<br><br>&nbsp;&nbsp;&nbsp; (+ 4 4 4)<br><br>我们可以为任务表程序写一个宏, 把任务数据转换为可执行码, 然后执行。假定我们的输<br>出是在控制台:<br><br>&nbsp;&nbsp;&nbsp; (defmacro item (priority note)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; `(block <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (print stdout tab "Prority: " ~(head (tail priority)) endl)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (print stdout tab "Note: " ~note endl endl)))<br><br>我们创造了一个非常小的有限的语言来管理嵌在Lisp中的任务表。这个语言只用来解决特<br>定领域的问题, 通常称之为DSLs(特定领域语言, 或专用领域语言)。<br><br>特定领域语言<br><br>本文谈到了两个特定领域语言, 一个是Ant, 处理软件构造。一个是没起名字的, 用于处<br>理任务表。两者的差别在于, Ant是用XML, XML解析器, 以及Java语言合在一起构造出来<br>的。而我们的迷你语言则完全内嵌在Lisp中, 只消几分钟就做出来了。<br><br>我们已经说过了DSL的好处, 这也就是Ant用XML而不直接用Java的原因。如果使用Lisp, <br>我们可以任意创建DSL, 只要我们需要。我们可以创建用于网站程序的DSL, 可以写多用户<br>游戏, 做固定收益贸易(fixed income trade), 解决蛋白质折叠问题, 处理事务问题, 等<br>等。我们可以把这些叠放在一起, 造出一个语言, 专门解决基于网络的贸易程序, 既有网<br>络语言的优势, 又有贸易语言的好处。每天我们都会收获这种方法带给我们的益处, 远远<br>超过Ant所能给予我们的。<br><br>用DSL解决问题, 做出的程序精简, 易于维护, 富有弹性。在Java里面, 我们可以用类来<br>处理问题。这两种方法的差别在于, Lisp使我们达到了一个更高层次的抽象, 我们不再受<br>语言解析器本身的限制, 比较一下用Java库直接写的构造脚本和用Ant写的构造脚本其间<br>的差别。同样的, 比较一下你以前所做的工作, 你就会明白Lisp带来的好处。<br><br>接下来<br><br>学习Lisp就像战争中争夺山头。尽管在电脑科学领域, Lisp已经算是一门古老的语言, 直<br>到现在仍然很少有人真的明白该怎样给初学者讲授Lisp。尽管Lisp老手们尽了很大努力,<br>今天新手学习Lisp仍然是困难重重。好在现在事情正在发生变化, Lisp的资源正在迅速增<br>加, 随着时间推移, Lisp将会越来越受关注。<br><br>Lisp使人超越平庸, 走到前沿。学会Lisp意味着你能找到更好的工作, 因为聪明的雇主会<br>被你与众不同的洞察力所打动。学会Lisp也可能意味着明天你可能会被解雇, 因为你总是<br>强调, 如果公司所有软件都用Lisp写, 公司将会如何卓越, 而这些话你的同事会听烦的。<br>Lisp值得努力学习吗? 那些已经学会Lisp的人都说值得, 当然, 这取决于你的判断。<br><br>你的看法呢?<br><br>这篇文章写写停停, 用了几个月才最终完成。如果你觉得有趣, 或者有什么问题, 意见或<br>建议, 请给我发邮件coffeemug@gmail.com, 我会很高兴收到你的反馈。 <br>
<script type=text/javascript>
if ($ != jQuery) {
$ = jQuery.noConflict();
}
var isLogined = false;
var cb_blogId = 21153;
var cb_entryId = 762180;
var cb_blogApp = "Leap-abead";
var cb_blogUserGuid = "e35e360b-63cf-dd11-9e4d-001cf0cd104b";
var cb_entryCreatedDate = '2007/5/28 10:42:00';
</script>
<img src ="http://www.cppblog.com/ccl0326/aggbug/140071.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2011-02-15 09:29 <a href="http://www.cppblog.com/ccl0326/archive/2011/02/15/140071.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>互斥锁与条件变量的语义</title><link>http://www.cppblog.com/ccl0326/archive/2010/12/16/136638.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Thu, 16 Dec 2010 07:35:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2010/12/16/136638.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/136638.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2010/12/16/136638.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/136638.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/136638.html</trackback:ping><description><![CDATA[<p>互斥锁与条件变量的语义</p>
<p><br>互斥锁，我要对一块共享数据操作，但是我怕同时你也操作，那就乱套了，所以我要加锁，这个时候我就开始操作这块共享数据，而你进不了临界区，等我操作完了，把锁丢掉，你就可以拿到锁进去操作了</p>
<p>&nbsp;</p>
<p>条件变量，我要看一块共享数据里某一个条件是否达成，我很关心这个，如果我用互斥锁，不停的进入临界区看条件是否达成，这简直太悲剧了，这样一来，我醒的时候会占CPU资源，但是却干不了什么时，只是频繁的看条件是否达成，而且这对别人来说也是一种损失，我每次加上锁，别人就进不了临界区干不了事了。好吧，轮询总是痛苦的，咱等别人通知吧，于是条件变量出现了，我依旧要拿个锁，进了临界区，看到了共享数据，发现，咦，条件还不到，于是我就调用pthread_cond_wait(),先把锁丢了，好让别人可以去对共享数据做操作，然后呢？然后我就睡了，直到特定的条件发生，别人修改完了共享数据，给我发了个消息，我又重新拿到了锁，继续干俺要干的事情了&#8230;&#8230;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/ccl0326/aggbug/136638.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2010-12-16 15:35 <a href="http://www.cppblog.com/ccl0326/archive/2010/12/16/136638.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线程内幕</title><link>http://www.cppblog.com/ccl0326/archive/2010/12/16/136635.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Thu, 16 Dec 2010 06:37:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2010/12/16/136635.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/136635.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2010/12/16/136635.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/136635.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/136635.html</trackback:ping><description><![CDATA[<p>一.<br>在主线程中调用<br>(1)pthread_create( &amp;thread_a, NULL, thread_function, NULL);<br>(2)pthread_create( &amp;thread_b, NULL, thread_function, NULL);<br>(3)pthread_create( &amp;thread_c, NULL, thread_function, NULL);</p>
<p>&nbsp;</p>
<p><br>在段2处，线程b可以认为线程a已经存在<br>但是在段2执行完以后，主线程并不知道线程a和线程b谁先执行，并不能在这里做线程a先于线程b执行的假设<br>因为线程的时间片分配在这里是未知的</p>
<p>&nbsp;</p>
<p>二.<br>myglobal=myglobal+1;<br>myglobal是全局变量,多个线程同时在做累加的工作<br>是否应该为myglobal=myglobal+1;加锁呢？<br>肯定是应该加锁<br>首先我们并不知道myglobal=myglobal+1;又或是++ myglobal;能否被编译成一条汇编指令<br>就算如此++ myglobal被编译成了原子操作<br>但考虑到多核处理器，其原子操作可能在多CPU上同时处理<br>其结果仍然是不可预估的</p>
<p><a href="http://www.ibm.com/developerworks/cn/linux/thread/posix_thread2/index.html"><br>以上内容转述自http://www.ibm.com/developerworks/cn/linux/thread/posix_thread2/index.html</a><br></p>
<img src ="http://www.cppblog.com/ccl0326/aggbug/136635.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2010-12-16 14:37 <a href="http://www.cppblog.com/ccl0326/archive/2010/12/16/136635.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>第4章 </title><link>http://www.cppblog.com/ccl0326/archive/2010/12/16/136612.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Thu, 16 Dec 2010 03:39:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2010/12/16/136612.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/136612.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2010/12/16/136612.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/136612.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/136612.html</trackback:ping><description><![CDATA[<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>UNIX</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">网络编程笔记</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt">做下笔记，以供不时之需:)<br></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">第</span><span lang=EN-US><font face=Calibri>4</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">章</span><span lang=EN-US><font face=Calibri> </font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>socket(int family,<span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int type,<span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int protocol);</font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">非负描述字</span><span lang=EN-US><font face=Calibri>-</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">成功，</span><span lang=EN-US><font face=Calibri>-1-</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">出错</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">协议族</span><span lang=EN-US><font face=Calibri><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">套接口类型</span><span lang=EN-US><font face=Calibri><span style="mso-spacerun: yes">&nbsp; </span><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">协议类型常量值</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-spacerun: yes">&nbsp;&nbsp; </span>AF_INET<span style="mso-spacerun: yes">&nbsp; </span>IPv4</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">协议</span><span lang=EN-US><font face=Calibri><span style="mso-tab-count: 1"> </span>SOCK_STREAM </font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">字节流</span><span lang=EN-US><font face=Calibri><span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>IPPROTO_TCP<span style="mso-spacerun: yes">&nbsp; </span>TCP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">传输协议</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-spacerun: yes">&nbsp;&nbsp; </span>AF_INET6 IPv6</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">协议</span><span lang=EN-US><font face=Calibri><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span>SOCK_DGRAM<span style="mso-spacerun: yes">&nbsp; </span></font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">数据报</span><span lang=EN-US><font face=Calibri><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>IPPROTO_UDP<span style="mso-spacerun: yes">&nbsp; </span>UDP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">传输协议</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>AF_LOCAL UNIX</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">域协议</span><span lang=EN-US><font face=Calibri><span style="mso-spacerun: yes">&nbsp; </span>SOCK_SEQPACKET</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">有序分组</span><span lang=EN-US><font face=Calibri><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>IPPROTO_SCTP SCTP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">传输协议</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-spacerun: yes">&nbsp;&nbsp; </span>AF_ROUTE </font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">路由套接口</span><span lang=EN-US><font face=Calibri><span style="mso-spacerun: yes">&nbsp; </span>SOCK_RAW<span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">原始套接口</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-spacerun: yes">&nbsp;&nbsp; </span>AF_KEY<span style="mso-spacerun: yes">&nbsp;&nbsp; </span></font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">密钥套接口</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>protocol</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">可以设为</span><span lang=EN-US><font face=Calibri>0</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">，以选择所给定的</span><span lang=EN-US><font face=Calibri>family</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">和</span><span lang=EN-US><font face=Calibri>type</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">组合的系统缺省值</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>connect(int sockfd,<span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>const struct sockaddr *servaddr,<span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp; </span>socklen_t addrlen);</font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1"><font face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">要连接的套接口描述字</span><span lang=EN-US><span style="mso-tab-count: 1"><font face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">指向描述连接地址的套接口地址的结构指针</span><span lang=EN-US><span style="mso-tab-count: 1"><font face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">指向套接口地址的结构大小</span><span lang=EN-US><span style="mso-tab-count: 1"><font face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>0-</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">成功，</span><span lang=EN-US><font face=Calibri>-1-</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">失败</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>ETIMEDOUT</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">错误，客户端未收到</span><span lang=EN-US><font face=Calibri>SYN</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">分节的响应</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">在</span><span lang=EN-US><font face=Calibri>4.4BSD</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">内核中</span><span lang=EN-US><font face=Calibri>SYN</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">分节会在首次发出后的</span><span lang=EN-US><font face=Calibri>6</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">秒，</span><span lang=EN-US><font face=Calibri>24</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">秒后再发出</span><span lang=EN-US><font face=Calibri>SYN</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">分节，如果</span><span lang=EN-US><font face=Calibri>75</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">秒后仍无响应，返回错误</span><span lang=EN-US style="COLOR: #548dd4; mso-themecolor: text2; mso-themetint: 153"><font face=Calibri>(TCPv2 p828)</font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>ECONNERFUSED</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">错误，客户端收到</span><span lang=EN-US><font face=Calibri>RST</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">分节时就返回错误</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">其中产生</span><span lang=EN-US><font face=Calibri>RST</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">的三个条件是：</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>1.</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">目的地为某端口的</span><span lang=EN-US><font face=Calibri>SYN</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">到达，然而该端口上没有正在监听的服务器</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red"><font face=Calibri>2.TCP</font></span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">想取消一个已有连接</span><span lang=EN-US style="COLOR: red"><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red"><font face=Calibri>3.TCP</font></span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">接受到一个根本不存在的连接上的分节</span><font face=Calibri><span lang=EN-US style="COLOR: #548dd4; mso-themecolor: text2; mso-themetint: 153">(TCPv1 246-250)</span><span lang=EN-US style="COLOR: red"><o:p></o:p></span></font></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red"><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>EHOSTUNREACH</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">或</span><span lang=EN-US><font face=Calibri>ENETUNREACH</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">错误</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">在发出</span><span lang=EN-US><font face=Calibri>SYN</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">分节的中间某个路由器引发了目的地不可达的</span><span lang=EN-US><font face=Calibri>ICMP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">错误，客户主机保存该消息，但仍然继续发送</span><span lang=EN-US><font face=Calibri>SYN</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">，直到</span><span lang=EN-US><font face=Calibri>75</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">秒后，如果仍未有回应，则把错误消息返回给进程。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">以下情况也会产生此类错误</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red"><font face=Calibri>1.</font></span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">本地系统的转发表，根本没有到达远地系统的路径</span><span lang=EN-US style="COLOR: red"><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red"><font face=Calibri>2.connect</font></span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">调用根本不等待就返回</span><span lang=EN-US style="COLOR: red"><font face=Calibri>(</font></span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">非阻塞</span><span lang=EN-US style="COLOR: red"><font face=Calibri>?)<o:p></o:p></font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red"><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="COLOR: #00b050; FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">注</span><span lang=EN-US style="COLOR: #00b050"><font face=Calibri>:connect</font></span><span style="COLOR: #00b050; FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">失败则该套接口不再可用，必须关闭。</span><span lang=EN-US style="COLOR: #00b050"><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: #00b050"><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: #00b050"><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>bind(int sockfd,<span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>const struct sockaddr *myaddr,<span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>socklen_t addrlen);</font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>0-</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">成功</span><span lang=EN-US><font face=Calibri>,-1</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">失败</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-spacerun: yes"><font face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">要绑定的套接口描述字</span><span lang=EN-US><span style="mso-spacerun: yes"><font face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">指向描述要绑定的套接口地址的结构指针</span><span lang=EN-US><span style="mso-tab-count: 1"><font face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">指向套接口地址的结构大小</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">如果一个</span><span lang=EN-US><font face=Calibri>TCP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">客户端或服务器未曾调用</span><span lang=EN-US><font face=Calibri>bind</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">捆绑一个端口，当调用</span><span lang=EN-US><font face=Calibri>connect</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">或</span><span lang=EN-US><font face=Calibri>listen</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">时，内核会为相应的套接口选择一个临时端口</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">进程可把特定</span><span lang=EN-US><font face=Calibri>IP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">地址捆绑到它的套接口上，这个特定</span><span lang=EN-US><font face=Calibri>IP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">必须是主机的网络接口之一。对于</span><span lang=EN-US><font face=Calibri>TCP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">客户，这就限定了套接口只接受目的地为这个特定</span><span lang=EN-US><font face=Calibri>IP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">的客户连接。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>TCP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">客户通过不把</span><span lang=EN-US><font face=Calibri>IP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">捆绑到它的套接口上，内核会根据所用外出网络接口来选择源</span><span lang=EN-US><font face=Calibri>IP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">地址，而所用外出的接口则取决于到达服务器所需的路径。</span><span lang=EN-US style="COLOR: #548dd4; mso-themecolor: text2; mso-themetint: 153"><font face=Calibri>(TCPv2 p737)</font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">如果</span><span lang=EN-US><font face=Calibri>TCP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">服务器没有把</span><span lang=EN-US><font face=Calibri>IP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">地址捆绑到它的套接口上，内核就把客户发送的</span><span lang=EN-US><font face=Calibri>SYN</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">的宿</span><span lang=EN-US><font face=Calibri>IP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">地址作为服务器的源地址。</span><span lang=EN-US style="COLOR: #548dd4; mso-themecolor: text2; mso-themetint: 153"><font face=Calibri>(TCPv2 p943)<o:p></o:p></font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: black; mso-themecolor: text1"><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: black; mso-themecolor: text1"><font face=Calibri>EADDRINUSE</font></span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-themecolor: text1">错误</span><span style="COLOR: black; mso-themecolor: text1"><font face=Calibri> </font></span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-themecolor: text1">地址已使用</span><span lang=EN-US style="COLOR: black; mso-themecolor: text1"><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: black; mso-themecolor: text1"><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: black; mso-themecolor: text1"><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>listen(<span style="mso-spacerun: yes">&nbsp;&nbsp; </span>int sockfd , <span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int backlog);</font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>0-</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">成功，</span><span lang=EN-US><font face=Calibri>-1</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">失败</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-spacerun: yes"><font face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">要转换成被动的套接口</span><span lang=EN-US><span style="mso-tab-count: 1"><font face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">排队的最大已连接个数</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>1.</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">未完成连接队列</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">处于</span><span lang=EN-US><font face=Calibri>SYN_RCVD</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">状态的套接口队列</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>2.</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">已完成连接队列</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">处于</span><span lang=EN-US><font face=Calibri>ESTABLISHED</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">状态的套接口队列</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">在队列满时，服务器如果收到</span><span lang=EN-US><font face=Calibri>SYN</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">分节，会忽略掉，因为队列满是暂时性的，忽略掉可以以期待客户端的再次连接。而如果返回</span><span lang=EN-US><font face=Calibri>RST</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">分节，会时客户端放弃连接。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">在三次握手完成之后，但在服务器调用</span><span lang=EN-US><font face=Calibri>accept</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">之前到达的数据，应有服务器</span><span lang=EN-US><font face=Calibri>TCP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">排队，最大数据量为相应的已连接套接口的接受缓冲区大小。<br><br><br><br></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>Int accept(<span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int sockfd,<span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>struct sockaddr* cliaddr,<span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>socklen_t* addrlen);</font></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">非负描述字</span><span lang=EN-US><font face=Calibri>-</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">成功</span><span lang=EN-US><font face=Calibri>,-1-</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">失败</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 3"><font face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">被动的监听套接口</span><span lang=EN-US><span style="mso-spacerun: yes"><font face=Calibri>&nbsp; </font></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">对端的套接口地址的结构</span><span lang=EN-US><span style="mso-tab-count: 1"><font face=Calibri>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">对端的套接口地址的结构大小</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><font face=Calibri>Int close(int sockfd);</font></span></p>
<p class=MsoListParagraph style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-char-indent-count: 0; mso-list: l0 level1 lfo1"><span lang=EN-US style="mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin"><span style="mso-list: Ignore"><font face=Calibri>0-</font><span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">成功</span><span lang=EN-US><font face=Calibri>,-1-</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">失败</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: 20.25pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">要关闭的套接口</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">缺省行为是修改套接口标记为已关闭，函数会立即返回，此时该套接口描述字已不能再由进程使用。而</span><span lang=EN-US><font face=Calibri>TCP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">将尝试发送已排队等待发送到对端的任何数据，发送完毕后开始正常的</span><span lang=EN-US><font face=Calibri>TCP</font></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin">连接终止序列。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p><font face=Calibri>&nbsp;</font></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 10.5pt; FONT-FAMILY: 'Calibri','sans-serif'; mso-ascii-theme-font: minor-latin; mso-fareast-font-family: 宋体; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-size: 11.0pt; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">SO_LINGER</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-bidi-font-size: 11.0pt; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">套接口选项可以改变此缺省行为。</span></span></p>
<img src ="http://www.cppblog.com/ccl0326/aggbug/136612.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2010-12-16 11:39 <a href="http://www.cppblog.com/ccl0326/archive/2010/12/16/136612.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>懂得</title><link>http://www.cppblog.com/ccl0326/archive/2010/12/12/136236.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Sun, 12 Dec 2010 13:55:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2010/12/12/136236.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/136236.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2010/12/12/136236.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/136236.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/136236.html</trackback:ping><description><![CDATA[<p>第一次周六周日在加班<br>第一次担起责任<br>第一次怕自己不行<br>第一次有那么一点理解到底什么是主程<br>这一天仅作纪念:)</p>
<img src ="http://www.cppblog.com/ccl0326/aggbug/136236.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2010-12-12 21:55 <a href="http://www.cppblog.com/ccl0326/archive/2010/12/12/136236.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏服务端程序员群</title><link>http://www.cppblog.com/ccl0326/archive/2010/10/19/130461.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Tue, 19 Oct 2010 08:35:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2010/10/19/130461.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/130461.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2010/10/19/130461.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/130461.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/130461.html</trackback:ping><description><![CDATA[群号:16043631 已经250人了 还差250<br>帮朋友宣传一下:)<br>
<img src ="http://www.cppblog.com/ccl0326/aggbug/130461.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2010-10-19 16:35 <a href="http://www.cppblog.com/ccl0326/archive/2010/10/19/130461.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]《深度探索C++对象模型》读书笔记[二]</title><link>http://www.cppblog.com/ccl0326/archive/2010/10/14/129825.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Thu, 14 Oct 2010 02:36:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2010/10/14/129825.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/129825.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2010/10/14/129825.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/129825.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/129825.html</trackback:ping><description><![CDATA[<p>2002-7-6</p>
<p>3.3 Data Member的存取<br>1．&nbsp;&nbsp; 不管什么情况，每一个static data member只有一个实体，放在程序的data segment之中，每次程序取用static member，不管是通过operator::还是member selection operator，都会被内部转化为对该唯一extern实体的直接参考操作。每一个static member的存取以及与class的关联不会导致任何执行时间或空间上的额外负担。如果有两个classes，每一个都声明了一个static member freeList，那么当它们都放在程序的data segment时，就会导致名称冲突，编译器的解决方法是使用name-mangling，暗中对每一个static data member编码，以获得一个独一无二的程序识别代码。</p>
<p>2．&nbsp;&nbsp; 有多少个编译器，就有多少种name-mangling做法，任何name-mangling做法都有两个要点：</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一种算法，推导出独一无二的名称；</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果编译系统或者环境工具必须和使用者交谈，那些独一无二的名称可被轻易推导回原先的名称。</p>
<p>3．&nbsp;&nbsp; 取一个static data member的地址，会得到一个指向其数据类型的常量指针，而不是指向其class member的指针。</p>
<p>4．&nbsp;&nbsp; nonstatic data members直接放在每一个class object之中，除非经过显示的explicit或隐含的implicit class object，没有办法直接存取它们。只要程序员在一个member function中直接处理一个nonstatic data member，所谓implicit class object就会发生，其实质是编译器会为这个member function增添一个const this指针，而在函数体内通过这个this指针来存取nontatic data member。</p>
<p>5．&nbsp;&nbsp; 欲对一个nonstatic data member进行存取操作，编译器需要把class object的起始地址加上data member的编译量offset，如地址&amp;someObject.someMember等于&amp;someobject + (&amp;theClass::someMember &#8211; 1);指向data member的指针，其offset值总是会被加上1，这样可以使编译系统区分出一个指向class第一个data member的指针和一个没有指向任何data member的指针。</p>
<p>6．&nbsp;&nbsp; 每一个nonstatic data member的偏移量在编译时期即可获知，甚至如果member属于一个单一或多重继承体系中base class subobject也是一样，因此其存取效率和一个C struct member或一个nonderived class的member的存取效率是一样的。但是在虚拟继承的情况下就另当别论了：如果该nonstatic data member是一个virtual base class的member，并且通过指针来存取的话，在编译时期就不会得知这个member真正的offset位置，所以这个存取操作必须延迟至执行期，经由一个额外的间接导引才能够解决。</p>
<p>2002-7-7</p>
<p>3.4 &#8220;继承&#8221;与Data Member<br>1．&nbsp;&nbsp; 在C++继承模型中，一个derived class object所表现出来的东西，是其自己的members加上其base classes members的总和。C++并未规定derived class members和base classes members的排列次序。不过，在大部分编译器上，除virtual base class外，base class members总是先出现。</p>
<p>2．&nbsp;&nbsp; 一般而言，具体继承concrete inheritance并不会增加空间或存取时间上的额外负担。</p>
<p>3．&nbsp;&nbsp; 把两个原本独立不相干的classes凑成一对type/subtype，并带有继承关系容易犯两个错误。一是可能会重复设计一些相同操作的函数，一般而言，选择某些函数做成inline函数，是设计class的一个重要课题；二是把一个class分解为多层，有可能会为了表现class体系之抽象化，因为编译器的边界调整而膨胀所需空间。其根本原因是C++保证出现在derived class中的base class subobject有其完整原样性。</p>
<p>4．&nbsp;&nbsp; C++最初问世时，许多编译器把vptr放在class object的尾端，这样可以保留base class C struct的对象布局。此后，某些编译器开始把vptr放在class object的开始处，这样会给多重继承下通过指向class members之指针调用virtual function带来一些帮助，否则，在执行期不仅必须备妥从class object起点处开始量起的offset，而且必须备妥class vptr之间的offset。</p>
<p>5．&nbsp;&nbsp; 单一继承提供了一种自然多态的形态，是关于class体系中base type和derived type之间的转换。一般来说，base class和derived class objects都是从相同的地址开始。但若将vptr放在class object的起始处，如果base class没有virtual function而derived class有，那么单一继承的自然多态就会打破。此时，把一个derived object转换为其base类型就需要编译器的介入，用以调整地址。而在既是多重继承又是虚拟继承的情况下，编译器的介入则更有必要。</p>
<p>6．&nbsp;&nbsp; 多重继承的复杂度在于derived class和其上一个base class乃至上上一个base class之间的非自然关系，其主要问题发生在derived class objects和其第二或后继的base class objects之间的转换。对一个多重派生对象，将其地址指定给最左端base class的指针，情况将和单一继承相同，而第二个或后继的base class的地址指定操作则需要修改地址，加上或减去（若是downcast）介于中间的base class subobjects的大小。C++并未要求多重继承时derived class object中各个base class subjectes的排列次序，目前各个编译器都是根据声明次序来排列它们。</p>
<p>7．&nbsp;&nbsp; class内如果内含一个或多个virtual bass class subobjects，将被分割为两部分：一个不变局部和一个共享局部。不变局部总是拥有固定的offset，其数据用以指定共享局部的位置，可以直接存取；而共享局部表现的就是virtual base class subobject，其位置会因为每次的派生操作而变化，只可间接存取。各家编译器实现技术之间的差异就在于间接存取的方法不同。</p>
<p>8．&nbsp;&nbsp; 一般而言，virtual base class最有效的一种运用方式是：一个没有任何data member的抽象class。</p>
<p>2002-7-14</p>
<p>3.5 对象成员的效率<br>如果没有把优化开关打开，就很难猜测一个程序的效率表现，因为程序代码潜在性的受到某些与编译器有关的东西的影响。程序员如果关心效率，应该实际测试，不要光凭推论或常识判断或假设。优化操作并不一定总是能够有效运行。</p>
<p>2002-7-15</p>
<p>3.6 指向Data Members的指针<br>指向data members的指针可用来详细调查class members的底层布局，可用来决定vptr是放在class的起始处还是尾端，还可用来决定class中access sections的次序。</p>
<p>取一个nonstatic data member的地址，将会得到它在class的offset；而取一个static data member的地址或者取一个绑定于真正class object身上的data member的地址，将会得到该member在内存中的真正地址。这也正是someType someClass::*和someTye *潜在的区别。</p>
<p>2002-7-16</p>
<p>Function语意学 The Semantics of Function<br>C++支持三种类型的member functions：static、nonstatic和virtual，每一种类型的调用方式都不同。</p>
<p>4.1 Members的各种调用方式<br>1．&nbsp;&nbsp; C++的设计准则之一便是nonstatic member function至少必须和一般的nonmember function有着相同的效率。编译器内部会将member函数实体转换为对等的nonmember函数实体，其步骤为：</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 改写函数原型signature以安插一个额外的参数this到member function中，使得class object可以调用该函数。其中，this是const指针，若该函数为const，则反映在this上面的结果是this指向的data也为const；</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 将每一个对nonstatic data member的存取操作改为经由this指针来存取；</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 将member function重新写成一个外部函数，对函数名称进行mangling处理；</p>
<p>此后，每一个函数调用操作也都必须转换，用以提供相应的实参。</p>
<p>2．&nbsp;&nbsp; 关于虚拟函数的内部转换步骤：若normalize是一个virtual member function，ptr-&gt;normalize();会被内部转化为(*ptr-&gt;vptr[t])(ptr); 事实上，vptr名称也会被mangled，因为可能存在有多个vptrs；t是vitrual table slot的索引值，关联到normalize函数；第二个ptr表示this指针。</p>
<p>3．&nbsp;&nbsp; 使用class scope operator明确调用一个vitual function，或经由一个class object调用一个vitual function其决议方式会和nontatic member function一样！故virtual function的一个inline函数实体可被扩展开来，因而提供极大的效率利益。</p>
<p>4．&nbsp;&nbsp; static member function的主要特征是没有this指针，这导致它不能直接存取其class中的nonstatic members，不能被声明为const、volatile或virtual，也不需要经由class object才能调用。static member function会被提出于class声明之外，并给予一个经过mangled的适当名称。如果取一个static member function的地址，得到的将是其在内存中的地址，其地址类型并不是一个指向class member function的指针，而是一个nonmember函数指针。static member function的一个意想不到的好处是可以成为一个callback函数，也可以成功地应用在thread函数身上。</p>
<p>2002-07-17</p>
<p>4.2 Virtual Member Functions虚拟成员函数<br>1．&nbsp;&nbsp; C++中，多态polymorphism表示以一个public base class指针或reference寻址出一个derived class object。识别一个class是否支持多态，唯一适当的方法试看它是否有任何virtual function。只要class拥有一个virtual function，它就需要一份额外的执行期型别判断信息。</p>
<p>2．&nbsp;&nbsp; 一个class只会有一个virtual table，其中内含对应class object中所有的active virtual functions的函数实体的地址。这些active virtual functions包括：</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个class定义的函数实体。它会改写overriding一个可能存在的base class virtual function。</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 继承自base class的函数实体。此时该class不改写base class virtual function。</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个pure_virtual_called()函数实体，它既可以扮演pure virtual function的空间保卫者，也可以当作执行期异常处理函数。如果该函数被调用，通常的操作是结束程序。</p>
<p>3．&nbsp;&nbsp; 每一个virtual function都被指派一个固定不变的索引值，该值在整个继承体系中保持与特定virtual function的关联。这样就可以在编译时期设定virtual function的调用。</p>
<p>2002-7-20</p>
<p>4．&nbsp;&nbsp; 多重继承下，一个上层basse classes数目为n的derived class，它将内含n-1个额外的virtual tables。其主要实体与最左端的base class共享，其中包含所有virtual functios的地址；n-1个次要实体与其它base classes有关，其中只包含出现在对应base class中virtual functions的地址。</p>
<p>5．&nbsp;&nbsp; 在多重继承中支持virtual function，其复杂度围绕在第二个及后继base class上，以及执行期this指针调整上。第二（或后继）base class会影响对virtual function支持的3种情况：</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过指向第二个base class的指针，调用derived class virtual function；</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过指向derived class的指针，调用第二个base class中一个继承而来的virtual function；</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 允许virtual function函数的返回值类型有所变化，可能是base type，也可能是publicly derived type。</p>
<p>6．&nbsp;&nbsp; 关于执行期this指针调整比较有效率的解决方法是thunk。所谓thunk是一小端assembly码，用来以适当的offset值来调整this指针并跳到相应的virtual function。thunk技术允许virtual table slot继续内含一个简单的指针，此时多重继承将不需要任何空间上的额外负担！slots中的地址可以直接指向virtual function，也可以指向一个相关的thunk。</p>
<p>4.3 函数的效能<br>nonmember、static member和nonstatic member function在内部都会转化为完全相同的形式，三者效率相同。</p>
<p>2002-08-08</p>
<p>4.4 指向Member Function的指针<br>对一个nonstatic member function取址，得到的是该函数在内存中的地址；而面对一个virtual function，得到的将是一个索引值。这个值是不完整的，必须被绑定于一个class object上，才能够通过它调用函数。指向member function的指针的声明语法，以及指向member selection运算符的指针，其作用是作为this指针的空间保留者。因此，static member function的类型是函数指针，而不是指向member function的指针。</p>
<p>使用一个member function指针，如果并不用于virtual function、多重继承、virtual base class等情况的话，其成本并不比使用一个nonmember function指针要高。</p>
<p>4.5 Inline Functions<br>关键词inline只是一项请求。如果在某个层次上，函数的执行成本比一般的函数调用及返回机制所带来的负荷低，那么该请求被接受，编译器就用一个表达式合理地将函数扩展开来。真正的inline函数扩展操作是在函数调用的那一点上。在inline扩展期间，每一个形式参数会被对应的实际参数所取代，inline函数中的每一个局部变量都必须被放在函数调用的一个封闭区段中，并拥有一个独一无二的名称。这会带来参数的求值操作以及临时性对象的管理。</p>
<p>2002-08-11</p>
<p>构造、解构、拷贝语意学&nbsp; Semantics of Construction, Destruction, and Copy<br>1．&nbsp;&nbsp; 一般而言，class的data member应该被初始化，而且只在constructor中或其它member functions中初始化，其它任何操作都将破坏其封装性质，使其维护和修改更加困难。</p>
<p>2．&nbsp;&nbsp; 可以定义并调用invoke一个pure virtual function，但它只能被静态调用，不能经由虚拟机制调用。每一个derived class destructor会被编译器加以扩展，静态调用每一个virtual base class以及上一层base class的destructor。因此，不管base class的virtual destructor是否声明为pure，它必须被定义。</p>
<p>5.1 无继承情况下的对象构造<br>C++ Standard要求编译器尽量延迟nontrivial members的实际合成操作，直到真正遇到其使用场所为止。</p>
<p>5.2 继承体系下的对象构造<br>一般而言，继承体系下编译器对constructor所作的扩充操作以及次序大约如下：</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所有virtual base class constructors必须从左到右、从深到浅被调用：如果class被列于member initialization list中，那么任何明确指定的参数都必须传递过去，否则如果class有一个default constructor，也应该调用它；class中的每一个virtual base class subobject的偏移量offset必须在执行期可被存取；如果class object是最底层most-derived的class，其constructors可能被调用，某些用以支持这个行为的机制必须被方进来。</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以base class的声明次序调用上一层base class constructors：如果base class被列于member initialization list中，那么任何明确指定的参数都必须传递过去，否则若它有default constructor或default memberwise copy constructor，那么就调用它；如果base class是多重继承下的第二或后继的base class，那么this指针必须有所调整。</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果class object有virtual table pointer(s)，它（们）必须被设定初值，指向适当的virtual table(s)。</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果有一个member没有出现在member initialization list中，但它有default constructor，调用之。</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 将member initialization list中的data members的初始化操作以members的声明次序放进constructor的函数本身。</p>
<p>2002-8-18</p>
<p>5.3对象复制语意学 Object Copy Semantics<br>1．&nbsp;&nbsp; 只有在默认行为所导致的语意不安全或者不正确以致发生别名化aliasing或者内存泄漏memory leak时，才需要设计一个copy assignment operator。否则，程序反倒会执行得较慢。</p>
<p>2．&nbsp;&nbsp; 如果仅仅是为了把NRV优化开关打开而提供一个copy constructor，那么就没有必要一定要提供一个copy assignment operator。</p>
<p>3．&nbsp;&nbsp; copy assignment operator有一个非正交情况，那就是它缺乏一个平行于member initialization list的member assignment list。调用base class的copy assignment operator示例：</p>
<p>Point::operator = (p3d); 或 (*(Point*)this) = p3d; 或 (Point &amp;)(*this) = p3d;</p>
<p>4．&nbsp;&nbsp; 事实上，copy assignment operator在虚拟继承情况下行为不佳，需要小心设计和说明。许多编译器甚至并不尝试取得正确的语意，它们在每一个中间的copy assignment operator中调用每一个base class instance，于是造成virtual base copy assignment operator的多个实体被调用。建议尽可能不要允许一个virtual base class的拷贝操作，并不要在任何virtual base class中声明data member。</p>
<p>5.5解构语意学 Semantics of Destruction<br>如果class没有定义destructor，那么只有在其内带的member object或base class拥有destructor时，编译器才会自动合成出一个destructor。一个由程序员定义的destructor被扩展的方式类似constructors被扩展的方式，只是顺序相反：</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; destructor的函数本体首先被执行；</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果class拥有member class objects，而后者拥有destructors，那么它们将以声明的相反顺序而调用；</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果object内带一个vptr，则现在被重新设定以指向适当base class之virtual table；</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果有任何直接的nonvirtual base classes拥有destructor，它们将以声明的相反顺序而调用；</p>
<p>&#252;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果有任何virtual base classes拥有destructor，而前面讨论的这个class是most-derived class，那么它们会以原先构造顺序的相反顺序被调用。</p>
<p>2002-8-19</p>
<p>执行期语意学 Runtime Semantics<br>6.1对象的构造和解构<br>1．&nbsp;&nbsp; 一般而言，constructor和destructor的安插都如你所预期。但如果一个区段或函数中有一个以上的离开点，情况就会复杂一些，destructor会放在每一个离开点之前。通常，我们要求将object尽可能放在使用它的那个程序区附近，这样做可以节省不必要的对象产生和销毁操作。</p>
<p>2．&nbsp;&nbsp; C++程序中所有的global objects都被放置在程序的data segment中，如果不明确指定初值，object所配置的内存内容将为0（C并不自动设定初值）。如果global object有constructor和destructor的话，我们说它需要静态的初始化和内存释放操作。</p>
<p>2002-8-20</p>
<p>3．&nbsp;&nbsp; virtual base class的subobject在每个derived class中的位置可能会变动，不能在编译时期确定。以一个derived class的pointer或reference来存取virtual base class subobject，是一种nonconstant expression，必须在执行期方可评估求值。</p>
<p>4．&nbsp;&nbsp; 使用静态初始化的object有一些缺点。其一，无法放入try区段，任何throw操作必将触发exception handling library的默认函数terminate()；其二，程序员必须为控制&#8220;需要跨越模块做静态初始化&#8221;objects的依赖顺序而产生的复杂度付出代价。建议根本就不要使用那些需要静态初始化的global objects。</p>
<p>5．&nbsp;&nbsp; 新的C++标准要求编译单位中的static local class objects必须在相应函数第一次被调用时才被构造，而且必须以相反的次序销毁。由于这些objects是在需要时才被构造，因此编译时期无法预期其集合和顺序。为支持新标准，可能要对被产生出来的static local class objects保持一个执行期链表。</p>
<p>2003-8-1</p>
<p>6．&nbsp;&nbsp; 对于对象数组定义，晚近的编译器一般会提供两个函数，分别用于处理没有virtual base class的class，以及内带virtual base class的class ，它们通常被称为vec_new、vec_vnew。前者类型通常为：</p>
<p>void* vec_new(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 初始化程序员未提供初值的连续元素</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *array,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 数组起始地址若为0，则动态分配</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t elem_size,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 每一个class object的大小</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int elem_count,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 数组中的元素数目</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void (*constructor) (void *),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // class的default constructor指针</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void (*destructor) (void *, char)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // class的destructor指针，以0填入</p>
<p>); 如果程序员提供带有默认参数值的default constructor，编译器要做特殊处理，以传入默认参数值！</p>
<p>对应销毁数组的两个函数分别为vec_delete、vec_vdelete。前者类型通常为：</p>
<p>void* vec_delete(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *array,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 数组起始地址</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t elem_size,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 每一个class object的大小</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int elem_count,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 数组中的元素数目</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void (*destructor) (void *, char)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // class的destructor指针</p>
<p>);</p>
<p>6.2 new和delete运算符<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意区分operator new和new operator！前者负责分配内存；后者先调用前者分配内存，然后调用constructor以实施初始化。</p>
<p>完</p>
<p>《深度探索C++对象模型》读书笔记 </p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/xjtuse_mal/archive/2007/03/01/1517809.aspx">http://blog.csdn.net/xjtuse_mal/archive/2007/03/01/1517809.aspx</a></p>
<img src ="http://www.cppblog.com/ccl0326/aggbug/129825.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2010-10-14 10:36 <a href="http://www.cppblog.com/ccl0326/archive/2010/10/14/129825.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>