??xml version="1.0" encoding="utf-8" standalone="yes"?>色欲久久久天天天综合网精品,999久久久国产精品,国产精自产拍久久久久久蜜http://www.shnenglu.com/ccl0326/about:blankzh-cnTue, 06 May 2025 21:23:20 GMTTue, 06 May 2025 21:23:20 GMT60linux内核情景分析W记-存储理http://www.shnenglu.com/ccl0326/archive/2011/03/15/141904.htmlVincentVincentTue, 15 Mar 2011 09:47:00 GMThttp://www.shnenglu.com/ccl0326/archive/2011/03/15/141904.htmlhttp://www.shnenglu.com/ccl0326/comments/141904.htmlhttp://www.shnenglu.com/ccl0326/archive/2011/03/15/141904.html#Feedback0http://www.shnenglu.com/ccl0326/comments/commentRss/141904.htmlhttp://www.shnenglu.com/ccl0326/services/trackbacks/141904.htmlW??存储理
LINUX式理
PGD          PMD          PT        PTE
表目标     中间目录     表     表?/p>

LINUX?2位地址下采取二层映?br>#define PGDIR_SHIFT 22
#define PTRS_PER_PGD 1024

#define PMD_SHIFT 22
#define PTRS_PER_PMD 1

#define PTRS_PER_PTE 1024
Ҏ以上宏定?PMD被完的架空了,而相当于采取了二层映?/p>

其中PGD用了U性地址的最?0??nbsp; MMU 对应
U性地址的中?0位是所对应的PTE在PT中的索引
剩下的最?2位则是页中的偏移?/p>

虚拟地址 = D基地址Q段偏移?br>                            16?nbsp;     32?br>更准的讲是D选择子了?br>

在LINUX中段基地址 = 0Q下面的____KERNEL_CS{)Q所以可以认为线性地址与虚拟地址L相等的,但其本质不是一个东?br>


0xC0000000-0xFFFFFFFF为内核占?br>0x0-0xBFFFFFFF为用h?


内核的虚拟内存ؓ单的U性映?br>#__PAGE_OFFSET (0xC0000000)
#define PAGE_OFFSET  ((unsigned long) __PAGE_OFFSET)
#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET)
#define __va(x) ((void *)((unsigned long)(x) +PAGE_OFFSET)

__pa是从虚拟地址转换成物理地址
__va是从物理地址转换成虚拟地址


在GDT中有4个段描述W?br>其烦引是2-5
分别?br>__KERNEL_CS 内核代码D?br>__KERNEL_DS 内核数据D?br>__USER_CS 用户代码D?br>__USER_DS 用户数据D?/p>

#define start_thread(regs,new_eip,new_esp) do {\
 __asm__("movl %0,%%fs;movl %0,%%gs"::"r"(0)); \
 set_fs(USER_DS);
 regs->xds = __USER_DS; \
 regs->xes = __USER_DS; \
 regs->xss = __USER_DS; \
 regs->xcs = __USER_CS; \
 regs->eip = new_eip;   \
 regs->esp = new_esp;   \

}while(0)

通过q段宏可以看出,LINUX没用D式存储Q虽然它也走了这个流E?/p>

 

MMU的流E?MMU使用物理地址

式映射
从REG CR3拿PGD的地址
扑ֈ面目录Q线性地址中的?0位ؓ索引Q找到页面目录项Q从中拿?0位作为页面表的烦引,面表与4k字节边界寚wQCPU自动补充?2位ؓ0得到面表地址?/p>

然后拿线性地址的中?0位,得到面表中的烦引,拿到面表项Q页面表的?0位在低位补充12?Q再加上U性地址的低12位组成物理地址?/p>


mm_struct d相关的虚拟内?br>vm_area_struct 一D虚拟内存的抽象Q也可以理解为段
mm_struct中拥有vm_area_struct的指?br>在vm_area_struct多的时候用avl树来存储
mem_map_t  物理表
zone_struct 物理内存的区l构Qzone_struct把物理内存分成了几个部分
ZONE_DMA 0 供DMA使用
ZONE_NORMAL 普通?br>ZONE_HIGHMEN 高段内存Q内核映不?/p>

物理内存之间区的划分q不是强制的Q如果某一个区已经没有内存可用Q是可以d的区拿内存的

其实一直对内核的寻址有些疑问
不过刚刚g想通了
内核会做预映,把PGDW?68以后的都做映射Q也是1G的空?br>而这U映应该是满__pa()宏,即线性地址与物理地址是线性映的?br>所以最l__pa()宏被用作在内总码中显性的获得某个U性地址所对应的物理地址
而MMU负责把一个线性地址隐式的{成了物理地址Q而这已{换与内核代码无关?br>不知q样理解是否正确Q?br>

今天只看Cq里
待箋……

说v来把q么个东西放到首很不好意思,主要目的是希望有看到的h帮我指正一下我所认知的错误或者解惑。谢谢啦:)



Vincent 2011-03-15 17:47 发表评论
]]>
关于CppUnit的用?http://www.shnenglu.com/ccl0326/archive/2011/03/03/141026.htmlVincentVincentThu, 03 Mar 2011 01:56:00 GMThttp://www.shnenglu.com/ccl0326/archive/2011/03/03/141026.htmlhttp://www.shnenglu.com/ccl0326/comments/141026.htmlhttp://www.shnenglu.com/ccl0326/archive/2011/03/03/141026.html#Feedback11http://www.shnenglu.com/ccl0326/comments/commentRss/141026.htmlhttp://www.shnenglu.com/ccl0326/services/trackbacks/141026.html    首先Q我是初学者,从无使用CppUnit的经验?/p>

    目q展到后期,x高稳定性,在这个时候是否适合加入单元试呢?
    找了几篇文章看了看,g谈单元测试就不得不谈TDDQ但我看到的l大多数人的观点是舍TDD,留单元测?br>    好了Q暂且不谈TDDQ只谈单元测试,其中我看CBlog的观ҎQ用单元测试,来逐步重构代码Q其论点是真正适合单元试的代码,能够更符合SOLID原则?br>     q个看法是我目前比较认同的?br>     另外看到一U用方式是Q只对算法类和一些基c,q行单元试。对于这U用方式,我实不知使用CppUnitq种现成的较动化的单元测试的框架意义在哪?br>    最后一个问题就是,如果我认为的前一U观Ҏ正确的,那在我们的这个项目进行到的这个阶D,惌提高E_性去做单元测试,是需要大量重构的Q这样一个量会不会反而会影响E_性?


     Ƣ迎大家随意发表自己的观点哈Q我只是惛_了解一下。以便做己更适合的判断?/p>

Vincent 2011-03-03 09:56 发表评论
]]>
关于lua的coroutinehttp://www.shnenglu.com/ccl0326/archive/2011/02/15/140073.htmlVincentVincentTue, 15 Feb 2011 03:01:00 GMThttp://www.shnenglu.com/ccl0326/archive/2011/02/15/140073.htmlhttp://www.shnenglu.com/ccl0326/comments/140073.htmlhttp://www.shnenglu.com/ccl0326/archive/2011/02/15/140073.html#Feedback0http://www.shnenglu.com/ccl0326/comments/commentRss/140073.htmlhttp://www.shnenglu.com/ccl0326/services/trackbacks/140073.html虽然在用h下Q但是还是需要维护coroutine的状态,
也就是也会拥有自q上下文切换的开销
更像是用h下实现的线E,但抢占式的线E不论切换粒度再大或再小Q都会有不可预知的行为,所要做同步Q而coroutine是协作式的Q务,
p׃把自q权利交出去,同步问题自然不用考虑?br>自己记一?

Vincent 2011-02-15 11:01 发表评论
]]>
Lisp的本?The Nature of Lisp)(?http://www.shnenglu.com/ccl0326/archive/2011/02/15/140071.htmlVincentVincentTue, 15 Feb 2011 01:29:00 GMThttp://www.shnenglu.com/ccl0326/archive/2011/02/15/140071.htmlhttp://www.shnenglu.com/ccl0326/comments/140071.htmlhttp://www.shnenglu.com/ccl0326/archive/2011/02/15/140071.html#Feedback3http://www.shnenglu.com/ccl0326/comments/commentRss/140071.htmlhttp://www.shnenglu.com/ccl0326/services/trackbacks/140071.html
                             作?Slava Akhmechet
                             译?Alec Jang

              出处: http://www.defmacro.org/ramblings/lisp.html


?br>
最初在web的某些角落偶然看到有Lisp? 我那时已l是一个颇有经验的E序员?br>在我的历上, 掌握的语a范围相当q泛, 象C++, Java, C#L语言{等都不在话?
我觉得我差不多知道所有的有关~程语言的事情。对待编E语a的问题上, 我觉得自׃
太会遇到什么大问题。其实我大错牚w了?br>
我试着学了一下Lisp, l果马上撞了墙。我被那些范例代码吓坏了。我惛_多初ơ接?br>Lisp语言的h, 一定也有过cM的感受。Lisp的语法太ơ了。一个语a的发明h, 居然?br>肯用心弄Z套漂亮的语法, 那谁q会愿意学它。反? 我是确实实被那些难看的无数
的括h蒙了?br>
回过来之后, 我和LispC֌的那伙h交谈, 诉说我的沮心情。结? 立马有一大套
理论砸过? q套理论在LispC֌处处可见, 几成惯例。比如说: Lisp的括号只是表面现
? Lisp的代码和数据的表达方式没有差? 而且比XML语法高明许多, 所以有无穷的好
? Lisp有强大无比的元语a能力, E序员可以写我维护的代码; Lisp可以创造出?br>对特定应用的语言子集; Lisp的运行时和编译时没有明确的分? {等, {等, {等。这
么长的赞词虽然看v来相当动? 不过Ҏ毫无意义。没l我演示q些东西是如?br>应用? 因ؓq些东西一般来说只有在大型pȝ才会用到。我争辩? q些东西传统语言
一样办得到。在和别ZZC时之后, 我最l还是放弃了学Lisp的念头。ؓ什么要
p几个月的旉学习语法q么隄的语a? q种语言的概念这么晦? 又没什么好?br>的例子。也许这语言不是该我q样的h学的?br>
几个月来, 我承受着q些Lisp辩护士对我心늚重压。我一度陷入了困惑。我认识一些绝
聪明的? 我对他们相当敬, 我看C们对Lisp的赞达C宗教般的高度。这是
? Lisp中一定有某种秘的东西存? 我不能忍受自己对此的无知, 好奇心和求知Ʋ最
l不可遏制。我于是咬紧牙关埋头学习Lisp, l过几个月的旉费劲心力的练? l于,
我看C那无Ih的泉水的源头。在l过p换骨的磨l之? 在经q七重地q煎熬
之后, l于, 我明白了?br>
悟在突然之间来临。曾l许多次, 我听到别人引用雷蒙d(译者注: 论文<<大教堂和?br>?gt;>的作? 著名的黑客社区理论家)的话: "Lisp语言值得学习。当你学会Lisp之后, ?br>会拥有深ȝ体验。就你q_q不用Lisp~程, 它也会你成为更加优U的程序员"?br>q去, 我根本不懂这些话的含? 我也不相信这是真的。可是现在我懂得了。这些话蕴含
的真理远q超q我q去的想像。我内心体会CU神圣的情感, 一瞬间的顿? 几乎使我
对电脑科学的观念发生了根本的改变?br>
悟的那一? 我成了Lisp的崇拜者。我体验C宗教大师的感? 一定要把我的知识传
布开? 臛_要让10个迷q灵魂得到拯救。按照通常的办? 我把q些道理(是刚开
始别人砸q来的那一? 不过现在我明白了真实的含?告诉旁h。结果太令h失望?
只有数几个人在我坚持之? 发生了一点兴? 但是仅仅看了几眼Lisp代码, 他们退
却了。照q样的办? 也许Ҏq功夫能造就了几个Lispq? 但我觉得q样的结果太差强
人意? 我得想一套有更好的办法?br>
我深入地思考了q个问题。是不是Lisp有什么很艰深的东? 令得那么多老练的程序员?br>不能领会? 不是, 没有Ml对艰深的东ѝ因为我能弄? 我相信其他h也一定能。那
么问题出在那? 后来我终于找C{案。我的结论就? 凡是教h学高U概? 一定要
从他已经懂得的东西开始。如果学习过E很有趣, 学习的内容表辑־很恰? 新概念就?br>变得相当直观。这是我的{案。所谓元~程, 所谓数据和代码形式合一, 所谓自修改?br>? 所谓特定应用的子语a, 所有这些概忉|本就是同族概? 彼此互ؓ解释, 肯定讲
不明白。还是从实际的例子出发最有用?br>
我把我的x说给LispE序员听, 遭到了他们的反对?q些东西本n当然不可能用熟悉
的知识来解释, q些概念完全与众不同, 你不可能在别人已有的l验里找到类似的东西",
可是我认些都是遁词。他们又反问? "你自׃ؓ啥不试一?" 好吧, 我来试一下?br>q篇文章是我尝试的l果。我要用熟悉的直观的Ҏ来解释Lisp, 我希望有勇气的h?br>完它, 拿杯饮料, 深呼怸? 准备被搞得晕头{向。来? 愿你获得大能?br>
重新审视XML

千里之行始于下。让我们的第一步从XML开始。可是XML已经说得更多的了, q能有什?br>新意思可说呢? 有的。XML自n虽然谈谈不上有趣, 但是XML和Lisp的关pd相当有趣?br>XML和Lisp的概忉|着惊h的相g处。XML是我们通向理解Lisp的桥梁。好? 我们且把
XML当作z马包R让我们拿好手杖, 对XML的无人涉及的荒原地带作一番探险。我们要从一
个全新的视角来考察q个题目?br>
表面上看, XML是一U标准化语法, 它以适合人阅ȝ格式来表达Q意的层次化数?br>(hirearchical data)。象d?to-do list), |页, 病历, 汽R保险? 配置文g{?br>{? 都是XML用武的地斏V比如我们拿d表做例子:

<todo name="housework">
    <item priority="high">Clean the house.</item>
    <item priority="medium">Wash the dishes.</item>
    <item priority="medium">Buy more soap.</item>
</todo>

解析q段数据时会发生什么情? 解析之后的数据在内存中怎样表示? 昄, 用树来表C?br>q种层次化数据是很恰当的。说到底, XMLq种比较Ҏ阅读的数据格? 是树型l构
数据l过序列化之后的l果。Q何可以用树来表示的数? 同样可以用XML来表C? 反之
亦然。希望你能懂得这一? q对下面的内Ҏ光要?br>
再进一步。还有什么类型的数据也常用树来表C? 无疑列表(list)也是一U。上q编译课
? q模模糊p记得一点吧? 源代码在解析之后也是用树l构来存攄, M~译E序?br>会把源代码解析成一|象语法树, q样的表C法很恰? 因ؓ源代码就是层ơ结构的:
函数包含参数和代码块, 代码快包含表辑ּ和语? 语句包含变量和运符{等?br>
我们已经知道, M树结构都可以轻而易丄写成XML, 而Q何代码都会解析成? 因此,
M代码都可以{换成XML, 对不? 我D个例? L下面的函?

int add(int arg1, int arg2)
{
    return arg1+arg2;
}

能把q个函数变成对等的XML格式? 当然可以。我们可以用很多U方式做? 下面是其
中的一U? 十分?

<define-function return-type="int" name="add">
    <arguments>
        <argument type="int">arg1</argument>
        <argument type="int">arg2</argument>
    </arguments>
    <body>
        <return>
            <add value1="arg1" value2="arg2" />
        </return>
    </body>
</define>

q个例子非常? 用哪U语a来做都不会有太大问题。我们可以把ME序码{成XML,
也可以把XML转回到原来的E序码。我们可以写一个{换器, 把Java代码转成XML, 另一?br>转换器把XML转回到Java。一L道理, q种手段也可以用来对付C++(q样做跟发疯差不
多么。可是的有人在? 看看GCC-XML(http://www.gccxml.org)q道了)。进一步说,
凡是有相同语aҎ而语法不同的语言, 都可以把XML当作中介来互相{换代码。实际上
几乎所有的L语言都在一定程度上满q个条g。我们可以把XML作ؓ一U中间表C法,
在两U语a之间互相译码。比方说, 我们可以用Java2XML把Java代码转换成XML, 然后?br>XML2CPP再把XML转换成C++代码, q气好的? 是? 如果我们心避免使用那些C++?br>具备的JavaҎ的? 我们可以得到完好的C++E序。这办法怎么? 漂亮?

q一切充分说? 我们可以把XML作ؓ源代码的通用存储方式, 其实我们能够产生一整套
使用l一语法的程序语a, 也能写出转换? 把已有代码{换成XML格式。如果真的采U?br>q种办法, 各种语言的编译器q不着自己写语法解析了, 它们可以直接用XML的语法解
析来直接生成抽象语法树?br>
说到q里你该问了, 我们研究了这半天XML, q和Lisp有什么关pd? 毕竟XML出来之时,
Lisp早已l问世三十年了。这里我可以保证, 你马上就会明白。不q在l箋解释之前, ?br>们先做一个小的思维l习。看一下上面这个XML版本的add函数例子, 你怎样l它分类,
是代码还是数? 不用太多考虑都能明白, 把它分到哪一c都讲得通。它是XML, 它是?br>准格式的数据。我们也知道, 它可以通过内存中的树结构来生成(GCC-XML做的是q个?br>?。它保存在不可执行的文g中。我们可以把它解析成树节? 然后做Q意的转换。显
而易? 它是数据。不q且? 虽然它语法有炚w? 可它又确实实是一个add函数,
对吧?  一旦经q解? 它就可以拿给~译器编译执行。我们可以轻而易丑ֆ个XML
代码解释? q且直接q行它。或者我们也可以把它译成Java或C++代码, 然后再编译运
行。所以说, 它也是代码?br>
我们说到那里? 不错, 我们已经发现了一个有的关键之点。过去被认ؓ很难解的概念
已经非常直观非常单的昄出来。代码也是数? q且从来都是如此。这听v来疯疯癫
癫的, 实际上却是必然之事。我许诺q会以一U全新的方式来解释Lisp, 我要重申我的?br>诺。但是我们此刻还没有到预定的地方, 所以还是先l箋上边的讨论?br>
刚才我说q? 我们可以非常单地实现XML版的add函数解释? q听h好像不过是说?br>而已。谁真的会动手做一下呢? 未必有多h会认真对待这件事。随便说? q不打算?br>的去? q样的事情你在生zM恐怕也遇到吧。你明白我这栯的意思吧, 我说的有没有
打动? 有哇, 那好, 我们l箋?br>
重新审视Ant

我们现在已经来到了月亮背光的那一? 先别忙着d。再探烦一? 看看我们q能发现
什么东ѝ闭上眼? 想一?000q冬天的那个雨夜, 一个名叫James Duncan Davidson
的杰出的E序员正在研ITomcat的servlet容器。那? 他正心C存好刚修改过的文
? 然后执行make。结果冒Z一大堆错误, 昄有什么东西搞错了。经q仔l检? ?br>? N是因为tab前面加了个空D导致命令不能执行吗? 实如此。老是q样, 他真
的受够了。乌云背后的月亮l了他启C? 他创Z一个新的Java目, 然后写了一个简?br>但是十分有用的工? q个工具巧妙地利用了Java属性文件中的信息来构造工E? 现在
James可以写makefile的替代品, 它能起到相同的作? 而Ş式更加优? 也不用担心有
makefile那样可恨的空格问题。这个工兯够自动解释属性文? 然后采取正确的动作来
~译工程。真是简单而优?br>
(作者注: 我不认识James, James也不认识? q个故事是根据网上关于Ant历史的帖?br>虚构?

使用Ant构造Tomcat之后几个? 他越来越感到Java的属性文件不以表达复杂的构造指
令。文仉要检? 拯, ~译, 发到另外一台机? q行单元试。要是出? 发?br>件给相关人员, 要是成功, ql在可能高层的?volumn)上执行构造。追t到最?
卯回复到最初的水^上。确? Java的属性文件不够用? James需要更有弹性的解决
Ҏ。他不想自己写解析器(因ؓ他更希望有一个具有工业标准的Ҏ)。XML看v来是?br>不错的选择。他׃几天工夫把AntUL到XMLQ于是,一件伟大的工具诞生了?br>
Ant是怎样工作的?原理非常单。Ant把包含有构造命令的XML文g(代码还是算数据,
你自己想?Q交l一个JavaE序来解析每一个元素,实际情况比我说的q要单得多?br>一个简单的XML指o会导致具有相同名字的Javac装入,q执行其代码?br>
    <copy todir="../new/dir">
        <fileset dir="src_dir" />
    </copy>

q段文字的含义是把源目录复制到目标目录,Ant会找C?copy"d(实际上就是一?br>Javac?, 通过调用Java的方法来讄适当参数(todir和fileset)Q然后执行这个Q务?br>Ant带有一l核心类, 可以q户Q意扩? 只要遵守若干U定可以。Ant扑ֈq些c?
每当遇到XML元素有同L名字, 执行相应的代码。过E非常简单。Ant做到了我们前?br>所说的东西: 它是一个语a解释? 以XML作ؓ语法, 把XML元素转译为适当的Java指o?br>我们可以写一?add"d, 然后, 当发现XML中有add描述的时? 执行这个addd?br>׃Ant是非常流行的目, 前面展示的策略就昑־更ؓ明智。毕? q个工具每天差不
多有几千家公司在使用?br>
到目前ؓ? 我还没有说Ant在解析XML时所遇到困难。你也不用麻烦去它的|站上去扄
案了, 不会扑ֈ有h值的东西。至对我们q个论题来说是如此。我们还是l下一步讨
论吧。我们答案就在那里?br>
Z么是XML

有时候正的决策q完全Z深思熟虑。我不知道James选择XML是否Z深思熟虑。也
总仅是个下意识的决定。至从James在Ant|站上发表的文章看v? 他所说的理由?br>全是似是而非。他的主要理由是UL性和扩展? 在Ant案例? 我看不出q两条有什?br>帮助。用XML而不是Java代码, 到底有什么好? Z么不写一lJavac? 提供api来满
_本Q?拯目录, ~译{等), 然后在Java里直接调用这些代? q样做仍然可以保
证移植? 扩展性也是毫无疑问的。而且语法也更为熟? 看着眼。那Z么要?XML
? 有什么更好的理由?

有的。虽然我不确定James是否实意识C。在语义的可构造性方? XML的弹性是Java
望尘莫及的。我不想用高p的名词来吓唬你, 其中的道理相当简? 解释hq不?br>很多功夫。好, 做好预备动作, 我们马上p朝向悟的时d奋力一跃?br>
上面的那个copy的例? 用Java代码怎样实现? 我们可以q样?

    CopyTask copy = new CopyTask();
    Fileset fileset = new Fileset();

    fileset.setDir("src_dir");
    copy.setToDir("../new/dir");
    copy.setFileset(fileset);

    copy.excute();

q个代码看v来和XML的那个很怼, 只是E微长一炏V差别在那里? 差别在于XML构造了
一个特D的copy动词, 如果我们要用Java来写的话, 应该是这个样?

    copy("../new/dir");
    {
        fileset("src_dir");
    }

看到差别了吗? 以上代码(如果可以在Java中用的化), 是一个特D的copy符, 有点?br>for循环或者Java5中的foreach循环。如果我们有一个{换器, 可以把XML转换到Java, ?br>概就会得C面这D事实上不可以执行的代码。因为Java的技术规范是定死? 我们没有
办法在程序里改变它。我们可以增加包, 增加c? 增加Ҏ, 但是我们没办法增加算W?
而对于XML, 我们昄可以ȝ自己增加q样的东ѝ对于XML的语法树来说, 只要原意,
我们可以L增加M元素, 因此{于我们可以L增加符。如果你q不太明白的?
看下面这个例? 加入我们要给Java引入一个unless符:

    unless(someObject.canFly())
    {
        someObject.transportByGround():
    }

在上面的两个例子? 我们打算lJava语法扩展两个符, 成组拯文g符和条件算W?br>unless, 我们要想做到q一? 必M改Java~译器能够接受的抽象语法? 昄我们
无法用Java标准的功能来实现它。但是在XML中我们可以轻而易丑֜做到。我们的解析?br>Ҏ XML元素, 生成抽象语法? 由此生成符, 所? 我们可以L引入M符?br>
对于复杂的算W来? q样做的好处显而易见。比? 用特定的符来做出源? ~译
文g, 单元试, 发送邮件等d, x看有多么妙。对于特定的题目, 比如说构造Y
仉? q些符的用可以大q减低少代码的数量。增加代码的清晰E度和可重用性?br>解释性的XML可以很容易的辑ֈq个目标。XML是存储层ơ化数据的简单数据文? 而在
Java? ׃层次l构是定ȝ(你很快就会看? Lisp的情况与此截然不?, 我们没
法达Cq目标。也许这正是Ant的成功之处呢?br>
你可以注意一下最qJava和C#的变?其是C#3.0的技术规?, C#把常用的功能抽象?br>? 作ؓ符增加到C#中。C#新增加的query符是一个例子。它用的q是传统的作?
C#的设计者修Ҏ象语法树, 然后增加对应的实现。如果程序员自己也能修改抽象语法?br>该有多好! 那样我们可以构造用于特定问题的子语a(比如说就像Antq种用于构造项?br>的语a), 你能惛_别的例子? 再思考一下这个概c不q也不必思考太? 我们待会
q会回到q个题目。那时候就会更加清晰?br>
Lisp来近

我们先把符的事情放一? 考虑一下Ant设计局限之外的东西。我早先说过, Ant可以?br>q写JavacL扩展。Ant解析器会Ҏ名字来匹配XML元素和Javac? 一旦找到匹? 执
行相应Q务。ؓ什么不用Ant自己来扩展Ant? 毕竟核心d要包含很多传l语a的结?br>(例如"if"), 如果Ant自np提供构造Q务的能力(而不是依赖javac?, 我们可以得
到更高的UL性。我们将会依赖一l核心Q?如果你原? 也不妨把它称作标准库), ?br>不用有没有Java 环境了。这l核心Q务可以用M方式来实? 而其他Q务徏{在q?br>l核心Q务之? 那样的话, Ant׃成ؓ通用? 可扩展的, ZXML的编E语a。考虑
下面q种代码的可能?

    <task name="Test">
        <echo message="Hello World" />
    </task>
    <Test />

如果XML支持"task"的创? 上面q段代码׃输出"Hello World!". 实际? 我们可以
用Java写个"task"d, 然后用Ant-XML来扩展它。Ant可以在简单原语的基础上写出更?br>杂的原语, 像其他~程语言常用的作法一栗这也就是我们一开始提到的ZXML的编
E语a。这样做用处不大(你知道ؓ甚么?), 但是真的很酷?br>
再看一回我们刚才说的Taskd。祝Z呀, 你在看Lisp代码!!! 我说什? 一炚w不像
Lisp? 没关p? 我们再给它收拾一下?br>
比XML更好

前面一节说q? Ant自我扩展没什么大? 原因在于XML很烦琐。对于数据来? q个问题
q不太大, 但如果代码很烦琐的话, 光是打字上的ȝp以抵消它的好处。你写过Ant
的脚本吗? 我写q? 当脚本达C定复杂度的时? XML非常让h厌烦。想想看? Z
写结束标{? 每个词都得打两遍, 不发疯算好的!

Z解决q个问题, 我们应当化写法。须? XML仅仅是一U表辑ֱơ化数据的方式?br>我们q不是一定要使用括h能得到树的序列化l果。我们完全可以采用其他的格式?br>其中的一U?刚好是Lisp所采用?格式, 叫做s表达式。s表达式要做的和XML一? ?br>它的好处是写法更? 单的写法更适合代码输入。后面我会详l讲s表达式。这之前
我要清理一下XML的东ѝ考虑一下关于拷贝文件的例子:

    <copy toDir="../new/dir">
        <fileset dir="src_dir">
    </copy>

x看在内存里面, q段代码的解析树在内存会是什么样? 会有一?copy"节点, 其下
有一?"fileset"节点, 但是属性在哪里? 它怎样表达? 如果你以前用qXML, q且
弄不清楚该用元素q是该用属? 你不用感到孤? 别h一L涂着呢。没人真的搞得清
楚。这个选择与其说是Z技术的理由, q不如说是闭着眼瞎摸。从概念上来? 属性也
是一U元? M属性能做的, 元素一样做得到。XML引入属性的理由, 其实是Z?br>XML写法不那么冗ѝ比如我们看个例?

    <copy>
        <toDir>../new/dir</toDir>
        <fileset>
            <dir>src_dir</dir>
        </fileset>
    </copy>

两下比较, 内容的信息量完全一? 用属性可以减打字数量。如果XML没有属性的?
光是打字够把h搞疯掉?br>
说完了属性的问题, 我们再来看一看s表达式。之所以绕q么个弯, 是因为s表达式没有属
性的概念。因为s表达式非常简l? Ҏ没有必要引入属性。我们在把XML转换成s表达?br>的时? 心里应该Cq一炏V看个例? 上面的代码译成s表达式是q样?

    (copy
        (todir "../new/dir")
        (fileset (dir "src_dir")))

仔细看看q个例子, 差别在哪? 括h成了圆括? 每个元素原来是有一Ҏh?br>包围? 现在取消了后一?是带斜杠的那个)括号标记。表C元素的l束只需要一?)"
可以了。不? 差别是q些。这两种表达方式的{? 非常自然, 也非常简单。s?br>辑ּ打v字来, 也省事得多。第一ơ看s表达?Lisp)? 括号很烦人是? 现在我们?br>白了背后的道? 一下子变得容易多了。至? 比XML要好的多。用s表达式写代码, ?br>单是实用, 而且也很让h愉快。s表达式具有XML的一切好? q些好处是我们刚刚探讨过
的。现在我们看看更加Lisp风格的task例子:

    (task (name "Test")
        (echo (message "Hellow World!")))
    (Test)

用Lisp的行话来? s表达式称(list)。对于上面的例子, 如果我们写的时候不加换
? 用逗号来代替空? 那么q个表达式看h非常像一个元素列? 其中又嵌套着?br>他标记?br>
    (task, (name, "test"), (echo, (message, "Hello World!")))

XML自然也可以用q样的风格来写。当然上面这句ƈ不是一般意义上的元素表。它实际?br>是一个树。这和XML的作用是一L。称它ؓ列表, 希望你不会感到迷? 因ؓ嵌套表和
树实际上是一码事。Lisp的字面意思就是表处理(list processing), 其实也可以称为树
处理, q和处理XML节点没有什么不同?br>
l受q一番折以? 现在我们l于相当接近Lisp? Lisp的括L秘本质(像许多
Lisp狂热分子认ؓ?逐渐昄出来。现在我们l研I其他内宏V?br>
重新审视C语言的宏

Cq里, 对XML的讨Z大概都听累了, 我都讲篏了。我们先停一? 把树, s表达?
Antq些东西先放一? 我们来说说C的预处理器。一定有人问? 我们的话题和C有什?br>关系? 我们已经知道了很多关于元~程的事? 也探讨过专门写代码的代码。理解这问题
有一定难? 因ؓ相关讨论文章所使用的编E语a, 都是你们不熟悉的。但是如果只论概
늚? q对要单一些。我怿, 如果以C语言做例子来讨论元编E? 理解h一?br>会容易得多。好, 我们接着看?br>
一个问题是, Z么要用代码来写代码呢? 在实际的~程? 怎样做到q一点呢? 到底?br>~程是什么意? 你大概已l听说过q些问题的答? 但是q不懂得其中~由。ؓ了揭C?br>背后的真? 我们来看一下一个简单的数据库查询问题。这U题目我们都做过。比方说,
直接在程序码里到处写SQL语句来修改表(table)里的数据, 写多了就非常烦h。即便用
C#3.0的LINQ, 仍然不减其痛苦。写一个完整的SQL查询(管语法很优?来修Ҏ人的?br>址, 或者查找某人的名字, l对是g令程序员倍感乏味的事? 那么我们该怎样来解册
个问? {案是: 使用数据讉K层?

概念挺简? 其要Ҏ把数据访问的内容(臛_是那些比较琐的部分)抽象出来, 用类?br>映射数据库的? 然后用访问对象属性访问器(accessor)的办法来间接实现查询。这样就
极大地简化了开发工作量。我们用讉K对象的方?或者属性赋? q要视你选用的语a
而定)来代替写SQL查询语句。凡是用q这U方法的? 都知道这很节省时间。当? 如果
你要亲自写这样一个抽象层, 那可是要花非常多的时间的--你要写一l类来映表, 把属
性访问{换ؓSQL查询, q个zȝ当耗费_֊。用手工来做昄是很不明智的。但是一?br>你有了方案和模板, 实际上就没有多少东西需要思考的。你只需要按照同L模板一ơ又
一ơ重复编写相g码就可以了。事实上很多人已l发C更好的方? 有一些工具可?br>帮助你连接数据库, 抓取数据库结构定?schema), 按照预定义的或者用户定制的模板?br>自动~写代码?br>
如果你用q这U工? 你肯定会对它的神奇效果深为折服。往往只需要鼠标点L? ?br>可以q接到数据库, 产生数据讉K源码, 然后把文件加入到你的工程里面, 十几分钟的工
? 按照往常手工方式来作的? 也许需要数百个时人工(man-hours)才能完成。可?
如果你的数据库结构定义后来改变了怎么? 那样的话, 你只需把这个过E重复一遍就?br>以了。甚x一些工兯自动完成q项变动工作。你只要把它作ؓ工程构造的一部分, ?br>ơ编译工E的时? 数据库部分也会自动地重新构造。这真的太棒了。你要做的事情基?br>上减C0。如果数据库l构定义发生了改? q在~译时自动更C数据讉K层的代码,
那么E序中Q何用过时的旧代码的地方, 都会引发~译错误?br>
数据讉K层是个很好的例子, q样的例子还有好多。从GUIh代码, WEB代码, COM?br>CORBA存根, 以及MFC和ATL{等。在q些地方, 都是有好多相g码多ơ重复。既然这?br>代码有可能自动编? 而程序员旉又远q比CPU旉昂贵, 当然׃生了好多工具来自
动生成样板代码。这些工L本质是什么呢? 它们实际上就是制造程序的E序。它们有一
个神U的名字, 叫做元编E。所谓元~程的本? 是如此?br>
元编E本来可以用到无数多的地? 但实际上使用的次数却没有那么多。归根结? 我们
心里q是在盘? 假设重复代码用拷贝粘贴的? 大概要重?,7? 对于q样的工作量,
值得专门建立一套生成工具吗? 当然不值得。数据访问层和COM存根往往需要重用数百次,
甚至上千? 所以用工具生成是最好的办法。而那些仅仅是重复几次十几ơ的代码, 是没
有必要专门做工具的。不必要的时候也d发代码生成工? 那就昄q度估计了代码生
成的好处。当? 如果创徏q类工具_单的? q是应当量多用, 因ؓq样做必?br>会节省时间。现在来看一下有没有合理的办法来辑ֈq个目的?br>
现在, C预处理器要派上用Z。我们都用过C/C++的预处理? 我们用它执行单的~译
指o, 来生简单的代码变换(比方? 讄调试代码开?, 看一个例?

    #define triple(X) X+X+X

q一行的作用是什? q是一个简单的预编译指? 它把E序中的triple(X)替换UCؓ
X+X+X。例? 把所有的triple(5)都换?+5+5, 然后再交l编译器~译。这是一个简
单的代码生成的例子。要是C的预处理器再强大一? 要是能够允许q接数据? 要是?br>多一些其他简单的机制, 我们可以在我们E序的内部开发自q数据讉K层。下面这?br>例子, 是一个假想的对C宏的扩展:

    #get-db-schema("127.0.0.1")
    #iterate-through-tables
    #for-each-table
        class #table-name
            {
            };
    #end-for-each

我们q接数据库结构定? 遍历数据? 然后Ҏ个表创徏一个类, 只消几行代码完?br>了这个工作。这hơ编译工E的时? q些c都会根据数据库的定义同步更新。显而易
? 我们不费吹灰之力在E序内部建立了一个完整的数据讉K? Ҏ用不着M外部
工具。当然这U作法有一个缺? 那就是我们得学习一套新?~译时语a", 另一个缺?br>是Ҏ不存在这么一个高U版的C预处理器。需要做复杂代码生成的时? q个语言(?br>者注: q里指预处理指o, 即作者所说的"~译时语a")本n也一定会变得相当复杂。它
必须支持_多的库和语言l构。比如说我们惌生成的代码要依赖某些ftp服务器上?br>文g, 预处理器得支持ftp讉K, 仅仅因ؓq个d而不得不创造和学习一门新的语a,
真是有点让h恶心(事实上已l存在着有此能力的语a, q样做就更显荒谬)。我们不妨再
灉|一? Z么不直接?C/C++自己作ؓ自己的预处理语言?  q样子的? 我们?br>以发挥语a的强大能? 要学的新东西也只不过是几个简单的指示?, q些指示字用?br>区别~译时代码和q行时代码?br>
    <%
        cout<<"Enter a number: ";
        cin>>n;
    %>
    for(int i=0;i< <% n %>;i++)
    {
        cout<<"hello"<<endl;
    }

你明白了? ?lt;%?>标记之间的代码是在编译时q行? 标记之外的其他代码都是普?br>代码。编译程序时, pȝ会提CZ输入一个数, q个数在后面的@环中会用到。而for?br>环的代码会被~译。假定你在编译时输入5, for循环的代码将会是:

    for(int i=0;i<5; i++)
    {
        cout<<"hello"<<endl;
    }

又简单又有效? 也不需要另外的预处理语a。我们可以在~译时就充分发挥宿主语言(
此处是C/C++)的强大能? 我们可以很容易地在编译时q接数据? 建立数据讉K? ?br>像JSP或者ASP创徏|页那样。我们也用不着专门的窗口工h另外建立工程。我们可以在
代码中立卛_入必要的工具。我们也用不着虑建立q种工具是不是值得, 因ؓq太Ҏ
? 太简单了。这样子不知可以节省多少旉啊?br>
你好, Lisp

到此Mؓ? 我们所知的关于Lisp的指C可以ȝZ句话: Lisp是一个可执行的语法更
优美的XML, 但我们还没有说Lisp是怎样做到q一点的, 现在开始补上这个话题?

Lisp有丰富的内置数据cd, 其中的整数和字符串和其他语言没什么分别。像71或?br>"hello"q样的? 含义也和C++或者Javaq样的语a大体相同。真正有意思的三种cd?br>W号(symbol), 表和函数。这一章的剩余部分, 我都会用来介l这几种cd, q要介绍
Lisp环境是怎样~译和运行源码的。这个过E用Lisp的术语来说通常叫做求倹{通读q一
节内? 对于透彻理解元编E的真正潜力, 以及代码和数据的同一? 和面向领域语a?br>观念, 都极光要。万勿等闲视之。我会尽量讲得生动有一? 也希望你能获得一?br>启发。那? 我们先讲W号?br>
大体? W号相当于C++或Java语言中的标志W? 它的名字可以用来讉K变量?例如
currentTime, arrayCount, n, {等), 差别在于, Lisp中的W号更加基本。在C++?br>Java里面, 变量名只能用字母和下划线的组? 而Lisp的符号则非常有包Ҏ? 比如, ?br>?+)是一个合法的W号, 其他的像-, =, hello-world, *{等都可以是W号名。符?br>名的命名规则可以在网上查到。你可以l这些符号Q意赋? 我们q里先用伪码来说明这
一炏V假定函数set是给变量赋?像{号=在C++和Java里的作用), 下面是我们的例子:

    set(test, 5)            // W号test的gؓ5
    set(=, 5)               // W号=的gؓ5
    set(test, "hello")      // W号test的gؓ字符?hello"
    set(test, =)            // 此时W号=的gؓ5, 所以test的也?
    set(*, "hello")         // W号*的gؓ"hello"

好像有什么不对的地方? 假定我们?赋给整数或者字W串? 那做乘法时怎么? 不管
怎么? *L乘法呀? {案单极了。Lisp中函数的角色十分Ҏ, 函数也是一U数?br>cd, 像整数和字W串一? 因此可以把它赋值给W号。乘法函数Lisp的内|函? ?br>认赋l?, 你可以把其他函数赋值给*, 那样*׃代表乘法了。你也可以把q函数的值存
到另外的变量里。我们再用伪码来说明一?

    *(3,4)          // 3?, l果?2
    set(temp, *)    // ?的? 也就是乘法函? 赋值给temp
    set(*, 3)       // ?赋予*
    *(3,4)          // 错误的表辑ּ, *不再是乘? 而是数?
    temp(3,4)       // temp是乘法函? 所以此表达式的gؓ3?{于12
    set(*, temp)    // 再次把乘法函数赋?
    *(3,4)          // 3?{于12

再古怪一? 把减LDl加?

    set(+, -)       // 减号(-)是内|的减法函数
    +(5, 4)         // 加号(+)现在是代表减法函? l果??{于1

q只是D例子, 我还没有详细讲函数。Lisp中的函数是一U数据类? 和整? 字符?
W号{等一栗一个函数ƈ不必然有一个名? q和C++或者Java语言的情形很不相同?br>在这里函数自׃表自己。事实上它是一个指向代码块的指? 附带有一些其他信??br>如一l参数变?。只有在把函数赋予其他符h, 它才h了名? 像把一个数值或
字符串赋予变量一L道理。你可以用一个内|的专门用于创徏函数的函数来创徏函数,
然后把它赋值给W号fn, 用伪码来表示是:

    fn [a]
    {
        return *(a, 2);
    }

q段代码q回一个具有一个参数的函数, 函数的功能是计算参数?的结果。这个函数还
没有名字, 你可以把此函数赋值给别的W号:

    set(times-two, fn [a] {return *(a, 2)})

我们现在可以q样调用q个函数:

    time-two(5)         // q回10

我们先蟩q符号和函数, 讲一讲表。什么是? 你也许已l听q好多相关的说法。表, 一
a以蔽? 是把类似XML那样的数据块, 用s表达式来表示。表用一Ҏh? 表中?br>素以I格分隔, 表可以嵌套。例?q回我们用真正的Lisp语法, 注意用分可C注?:

    ()                      ; I
    (1)                     ; 含一个元素的?br>    (1 "test")              ; 两元素表, 一个元素是整数1, 另一个是字符?br>    (test "hello")          ; 两元素表, 一个元素是W号, 另一个是字符?br>    (test (1 2) "hello")    ; 三元素表, 一个符号test, 一个含有两个元???br>                            ; ? 最后一个元素是字符?br>
当Lisppȝ遇到q样的表? 它所做的, 和Ant处理XML数据所做的, 非常怼, 那就是试
图执行它们。其? Lisp源码是特定的一U表, 好比Ant源码是一U特定的XML一栗?br>Lisp执行表的序是这L, 表的W一个元素当作函? 其他元素当作函数的参数。如?br>其中某个参数也是? 那就按照同样的原则对q个表求? l果再传递给最初的函数作ؓ
参数。这是基本原则。我们看一下真正的代码:

    (* 3 4)                 ; 相当于前面列举过的伪?(3,4), 卌??
    (times-two 5)           ; q回10, times-two按照前面的定义是求参数的2?br>    (3 4)                   ; 错误, 3不是函数
    (time-two)              ; 错误, times-two要求一个参?br>    (times-two 3 4)         ; 错误, times-two只要求一个参?br>    (set + -)               ; 把减法函数赋予符?
    (+ 5 4)                 ; 依据上一句的l果, 此时+表示减法, 所以返?
    (* 3 (+ 2 2))           ; 2+2的结果是4, 再乘3, l果?2

上述的例子中, 所有的表都是当作代码来处理的。怎样把表当作数据来处理呢? 同样?
设想一? Ant是把XML数据当作自己的参数。在Lisp? 我们l表加一个前~'来表C数
据?br>
    (set test '(1 2))       ; test的gؓ两元素表
    (set test (1 2))        ; 错误, 1不是函数
    (set test '(* 3 4))     ; test的值是三元素表, 三个元素分别?, 3, 4

我们可以用一个内|的函数head来返回表的第一个元? tail函数来返回剩余元素组成的
表?br>
    (head '(* 3 4))         ; q回W号*
    (tail '(* 3 4))         ; q回?3 4)
    (head (tal '(* 3 4)))   ; q回3
    (head test)             ; q回*

你可以把Lisp的内|函数想像成Ant的Q务。差别在? 我们不用在另外的语言中扩?br>Lisp(虽然完全可以做得?, 我们可以用Lisp自己来扩展自? 像上面丄times-two
函数的例子。Lisp的内|函数集十分_, 只包含了十分必要的部分。剩下的函数都是?br>为标准库来实现的?br>
Lisp?br>
我们已经看到, 元编E在一个类似jsp的模板引擎方面的应用。我们通过单的字符串处
理来生成代码。但是我们可以做的更好。我们先提一个问? 怎样写一个工? 通过查找
目录l构中的源文件来自动生成Ant脚本?br>
用字W串处理的方式生成Ant脚本是一U简单的方式。当? q有一U更加抽? 表达?br>力更? 扩展性更好的方式, 是利用XML库在内存中直接生成XML节点, q样的话内存?br>的节点就可以自动序列化成为字W串。不仅如? 我们的工兯可以分析q些节点, 对已
有的XML文g做变换。通过直接处理XML节点。我们可以超字W串处理, 使用更高层次?br>概念, 因此我们的工作就会做的更快更好?br>
我们当然可以直接用Ant自n来处理XML变换和制作代码生成工兗或者我们也可以用Lisp
来做q项工作。正像我们以前所知的, 表是Lisp内置的数据结? Lisp含有大量的工h
快速有效的操作?head和tail是最单的两个)。而且, Lisp没有语义U束, 你可以构?br>M数据l构, 只要你原意?br>
Lisp通过?macro)来做元编E。我们写一l宏来把d列表(to-do list)转换Z用领
域语a?br>
回想一下上面to-do list的例? 其XML的数据格式是q样?

    <todo name = "housework">
        <item priority = "high">Clean the hose</item>
        <item priority = "medium">Wash the dishes</item>
        <item priority = "medium">Buy more soap</item>
    </todo>

相应的s表达式是q样?

    (todo "housework"
        (item (priority high) "Clean the house")
        (item (priority medium) "Wash the dishes")
        (item (priority medium) "Buy more soap"))

假设我们要写一个Q务表的管理程? 把Q务表数据存到一l文仉, 当程序启动时, ?br>文gdq些数据q显C给用户。在别的语言?比如说Java), q个d该怎么? 我们
会解析XML文g, 从中得出d表数? 然后写代码遍历XML? 再{换ؓJava的数据结?br>(老实? 在Java里解析XML真不是gL的事?, 最后再把数据展C给用户。现在如?br>用Lisp, 该怎么?

假定要用同样思\的化, 我们大概会用Lisp库来解析XML。XMLҎ们来说就是一个Lisp
的表(s表达?, 我们可以遍历q个? 然后把相x据提交给用户。可? 既然我们?br>Lisp, 根本没有必要再用XML格式保存数据, 直接用s表达式就好了, q样没有必要做
转换了。我们也用不着专门的解析库, Lisp可以直接在内存里处理s表达式。注? Lisp
~译器和.net~译器一? 对LispE序来说, 在运行时L随时可用的?br>
但是q有更好的办法。我们甚至不用写表达式来存储数据, 我们可以写宏, 把数据当作代
码来处理。那该怎么做呢? 真的单。回想一? Lisp的函数调用格?

    (function-name arg1 arg2 arg3)

其中每个参数都是s表达? 求g? 传递给函数。如果我们用(+ 4 5)来代替arg1,
那么, E序会先求出l果, 是9, 然后?传递给函数。宏的工作方式和函数cM。主?br>的差别是, 宏的参数在代入时不求倹{?br>
    (macro-name (+ 4 5))

q里, (+ 4 5)作ؓ一个表传递给? 然后宏就可以L处理q个? 当然也可以对它求
倹{宏的返回值是一个表, 然后有程序作Z码来执行。宏所占的位置, p替换?br>l果代码。我们可以定义一个宏把数据替换ؓL代码, 比方? 替换为显C数据给用户
的代码?br>
q和元编E? 以及我们要做的Q务表E序有什么关pd? 实际? ~译器会替我们工?
调用相应的宏。我们所要做? 仅仅是创Z个把数据转换为适当代码的宏?br>
例如, 上面曄过的C的求三次方的? 用Lisp来写是这样子:

    (defmacro triple (x)
        `(+ ~x ~x ~x))

(译注: 在Common Lisp? 此处的单引号应当是反单引? 意思是对表不求? 但可以对
表中某元素求? 记号~表示对元素x求? q个求D号在Common Lisp中应当是逗号?br>反单引号和单引号的区别是, 单引h识的? 其中的元素都不求倹{这里作者所用的?br>h自己发明的一ULisp方言Blaise, 和common lisp略有不同, 事实? 发明方言?br>lisp高手独有的乐? 很多狂热分子都热衯样做。比如Paul Graham发明了ARC, 许多
记号比传l的Lispz得? 昑־比较C)

单引L用处是禁止对表求倹{每ơ程序中出现triple的时?

    (triple 4)

都会被替换成:

    (+ 4 4 4)

我们可以ZQ务表E序写一个宏, 把Q务数据{换ؓ可执行码, 然后执行。假定我们的?br>出是在控制台:

    (defmacro item (priority note)
        `(block
            (print stdout tab "Prority: " ~(head (tail priority)) endl)
            (print stdout tab "Note: " ~note endl endl)))

我们创造了一个非常小的有限的语言来管理嵌在Lisp中的d表。这个语a只用来解决特
定领域的问题, 通常UC为DSLs(特定领域语言, 或专用领域语a)?br>
特定领域语言

本文谈到了两个特定领域语a, 一个是Ant, 处理软g构造。一个是没v名字? 用于?br>理Q务表。两者的差别在于, Ant是用XML, XML解析? 以及Java语言合在一h造出?br>的。而我们的q你语言则完全内嵌在Lisp? 只消几分钟就做出来了?br>
我们已经说过了DSL的好? q也是Ant用XML而不直接用Java的原因。如果用Lisp,
我们可以L创徏DSL, 只要我们需要。我们可以创建用于网站程序的DSL, 可以写多用户
游戏, 做固定收益N?fixed income trade), 解决蛋白质折叠问? 处理事务问题, {?br>{。我们可以把q些叠放在一? 造出一个语a, 专门解决Z|络的N易程? 既有|?br>l语a的优? 又有贸易语言的好处。每天我们都会收莯U方法带l我们的益处, q远
过Ant所能给予我们的?br>
用DSL解决问题, 做出的程序精, 易于l护, 富有Ҏ。在Java里面, 我们可以用类?br>处理问题。这两种Ҏ的差别在? Lisp使我们达C一个更高层ơ的抽象, 我们不再?br>语言解析器本w的限制, 比较一下用Java库直接写的构造脚本和用Ant写的构造脚本其?br>的差别。同L, 比较一下你以前所做的工作, 你就会明白Lisp带来的好处?br>
接下?br>
学习Lisp像战争中争夺山头。尽在电脑U学领域, Lisp已经是一门古老的语言, ?br>到现在仍然很有人真的明白该怎样l初学者讲授Lisp。尽Lisp老手们尽了很大努?
今天新手学习Lisp仍然是困N重。好在现在事情正在发生变? Lisp的资源正在迅速增
? 随着旉推移, Lisp会来受x?br>
Lisp使h越q_, 走到前沿。学会Lisp意味着你能扑ֈ更好的工? 因ؓ聪明的雇M
被你与众不同的洞察力所打动。学会Lisp也可能意味着明天你可能会被解? 因ؓ你L
, 如果公司所有Y仉用Lisp? 公司会如何卓越, 而这些话你的同事会听烦的?br>Lisp值得努力学习? 那些已经学会Lisp的h都说值得, 当然, q取决于你的判断?br>
你的看法?

q篇文章写写停停, 用了几个月才最l完成。如果你觉得有趣, 或者有什么问? 意见?br>, L我发邮gcoffeemug@gmail.com, 我会很高兴收C的反馈?


Vincent 2011-02-15 09:29 发表评论
]]>
互斥锁与条g变量的语?/title><link>http://www.shnenglu.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.shnenglu.com/ccl0326/archive/2010/12/16/136638.html</guid><wfw:comment>http://www.shnenglu.com/ccl0326/comments/136638.html</wfw:comment><comments>http://www.shnenglu.com/ccl0326/archive/2010/12/16/136638.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ccl0326/comments/commentRss/136638.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ccl0326/services/trackbacks/136638.html</trackback:ping><description><![CDATA[<p>互斥锁与条g变量的语?/p> <p><br>互斥锁,我要对一块共享数据操作,但是我怕同时你也操作,那就乱套了,所以我要加锁,q个时候我开始操作这块共享数据,而你q不了界区Q等我操作完了,把锁丢掉Q你可以拿到锁q去操作?/p> <p> </p> <p>条g变量Q我要看一块共享数据里某一个条件是否达成,我很兛_q个Q如果我用互斥锁Q不停的q入临界区看条g是否达成Q这直太悲剧了,q样一来,我醒的时候会占CPU资源Q但是却q不了什么时Q只是频J的看条件是否达成,而且q对别h来说也是一U损失,我每ơ加上锁Q别人就q不了界区q不了事了。好吧,轮询L痛苦的,q别h通知吧,于是条g变量出现了,我依旧要拿个锁,q了临界区,看到了共享数据,发现Q咦Q条件还不到Q于是我p用pthread_cond_wait(),先把锁丢了,好让别h可以d׃n数据做操作,然后呢?然后我就睡了Q直到特定的条g发生Q别Z改完了共享数据,l我发了个消息,我又重新拿到了锁Ql干q的事情?#8230;…</p> <p> </p> <img src ="http://www.shnenglu.com/ccl0326/aggbug/136638.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ccl0326/" target="_blank">Vincent</a> 2010-12-16 15:35 <a href="http://www.shnenglu.com/ccl0326/archive/2010/12/16/136638.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>U程内幕http://www.shnenglu.com/ccl0326/archive/2010/12/16/136635.htmlVincentVincentThu, 16 Dec 2010 06:37:00 GMThttp://www.shnenglu.com/ccl0326/archive/2010/12/16/136635.htmlhttp://www.shnenglu.com/ccl0326/comments/136635.htmlhttp://www.shnenglu.com/ccl0326/archive/2010/12/16/136635.html#Feedback0http://www.shnenglu.com/ccl0326/comments/commentRss/136635.htmlhttp://www.shnenglu.com/ccl0326/services/trackbacks/136635.html一.
在主U程中调?br>(1)pthread_create( &thread_a, NULL, thread_function, NULL);
(2)pthread_create( &thread_b, NULL, thread_function, NULL);
(3)pthread_create( &thread_c, NULL, thread_function, NULL);

 


在段2处,U程b可以认ؓU程a已经存在
但是在段2执行完以后,ȝEƈ不知道线Ea和线Eb谁先执行Qƈ不能在这里做U程a先于U程b执行的假?br>因ؓU程的时间片分配在这里是未知?/p>

 

?
myglobal=myglobal+1;
myglobal是全局变量,多个U程同时在做累加的工?br>是否应该为myglobal=myglobal+1;加锁呢?
肯定是应该加?br>首先我们q不知道myglobal=myglobal+1;又或?+ myglobal;能否被编译成一条汇~指?br>q如此++ myglobal被编译成了原子操?br>但考虑到多核处理器Q其原子操作可能在多CPU上同时处?br>其结果仍然是不可预估?/p>


以上内容转述自http://www.ibm.com/developerworks/cn/linux/thread/posix_thread2/index.html



Vincent 2010-12-16 14:37 发表评论
]]>
W??http://www.shnenglu.com/ccl0326/archive/2010/12/16/136612.htmlVincentVincentThu, 16 Dec 2010 03:39:00 GMThttp://www.shnenglu.com/ccl0326/archive/2010/12/16/136612.htmlhttp://www.shnenglu.com/ccl0326/comments/136612.htmlhttp://www.shnenglu.com/ccl0326/archive/2010/12/16/136612.html#Feedback0http://www.shnenglu.com/ccl0326/comments/commentRss/136612.htmlhttp://www.shnenglu.com/ccl0326/services/trackbacks/136612.htmlUNIX|络~程W记

做下W记Q以供不时之需:)

W?/span>4?/span>

 

 

socket(int family,               int type,                      int protocol);

非负描述?/span>-成功Q?/span>-1-出错

            协议?/span>                   套接口类?/span>                              协议cd帔R?/span>

            AF_INET  IPv4协议 SOCK_STREAM 字节?/span>           IPPROTO_TCP  TCP传输协议

            AF_INET6 IPv6协议    SOCK_DGRAM  数据?/span>              IPPROTO_UDP  UDP传输协议

           AF_LOCAL UNIX域协?/span>  SOCK_SEQPACKET有序分组          IPPROTO_SCTP SCTP传输协议

            AF_ROUTE 路由套接?/span>  SOCK_RAW    原始套接?/span>

            AF_KEY   密钥套接?/span>

 

 

 

protocol可以设ؓ0Q以选择所l定?/span>family?/span>typel合的系l缺省?/span>

 

 

connect(int sockfd,           const struct sockaddr *servaddr,    socklen_t addrlen);

         要连接的套接口描q字         指向描述q接地址的套接口地址的结构指?/span>     指向套接口地址的结构大?/span>        

0-成功Q?/span>-1-p|

 

 

ETIMEDOUT错误Q客L未收?/span>SYN分节的响?/span>

?/span>4.4BSD内核?/span>SYN分节会在首次发出后的6U,24U后再发?/span>SYN分节Q如?/span>75U后仍无响应Q返回错?/span>(TCPv2 p828)

 

ECONNERFUSED错误Q客L收到RST分节时就q回错误

其中产生RST的三个条件是Q?/span>

1.目的Cؓ某端口的SYN到达Q然而该端口上没有正在监听的服务?/span>

2.TCP惛_消一个已有连?/span>

3.TCP接受C个根本不存在的连接上的分?/span>(TCPv1 246-250)

 

EHOSTUNREACH?/span>ENETUNREACH错误

在发?/span>SYN分节的中间某个\由器引发了目的地不可辄ICMP错误Q客户主Z存该消息Q但仍然l箋发?/span>SYNQ直?/span>75U后Q如果仍未有回应Q则把错误消息返回给q程?/span>

以下情况也会产生此类错误

1.本地pȝ的{发表Q根本没有到达远地系l的路径

2.connect调用Ҏ不等待就q回(非阻?/span>?)

 

?/span>:connectp|则该套接口不再可用,必须关闭?/span>

 

 

bind(int sockfd,                  const struct sockaddr *myaddr,               socklen_t addrlen);

0-成功,-1p|

     要绑定的套接口描q字       指向描述要绑定的套接口地址的结构指?/span>         指向套接口地址的结构大?/span>

 

如果一?/span>TCP客户端或服务器未曾调?/span>bind捆绑一个端口,当调?/span>connect?/span>listenӞ内核会ؓ相应的套接口选择一个时端?/span>

q程可把特定IP地址捆绑到它的套接口上,q个特定IP必须是主机的|络接口之一。对?/span>TCP客户Q这限定了套接口只接受目的Cؓq个特定IP的客戯接?/span>

TCP客户通过不把IP捆绑到它的套接口上,内核会根据所用外出网l接口来选择?/span>IP地址Q而所用外出的接口则取决于到达服务器所需的\径?/span>(TCPv2 p737)

如果TCP服务器没有把IP地址捆绑到它的套接口上,内核把客户发送的SYN的宿IP地址作ؓ服务器的源地址?/span>(TCPv2 p943)

 

EADDRINUSE错误 地址已?/span>

 

 

 

listen(   int sockfd ,                         int backlog);

0-成功Q?/span>-1p|

      要{换成被动的套接口     排队的最大已q接个数

 

 

1.未完成连接队?/span>

处于SYN_RCVD状态的套接口队?/span>

2.已完成连接队?/span>

处于ESTABLISHED状态的套接口队?/span>

 

在队列满Ӟ服务器如果收?/span>SYN分节Q会忽略掉,因ؓ队列满是暂时性的Q忽略掉可以以期待客L的再ơ连接。而如果返?/span>RST分节Q会时客L攑ּq接?/span>

 

在三ơ握手完成之后,但在服务器调?/span>accept之前到达的数据,应有服务?/span>TCP排队Q最大数据量为相应的已连接套接口的接受缓冲区大小?br>


Int accept(        int sockfd,                  struct sockaddr* cliaddr,                             socklen_t* addrlen);

非负描述?/span>-成功,-1-p|

                            被动的监听套接口  对端的套接口地址的结?/span>         对端的套接口地址的结构大?/span>

 

 

Int close(int sockfd);

0-       成功,-1-p|

要关闭的套接?/span>

 

~省行ؓ是修改套接口标记为已关闭Q函C立即q回Q此时该套接口描q字已不能再pE用。?/span>TCP尝试发送已排队{待发送到对端的Q何数据,发送完毕后开始正常的TCPq接l止序列?/span>

 

SO_LINGER套接口选项可以改变此缺省行为?/span>



Vincent 2010-12-16 11:39 发表评论
]]>
懂得http://www.shnenglu.com/ccl0326/archive/2010/12/12/136236.htmlVincentVincentSun, 12 Dec 2010 13:55:00 GMThttp://www.shnenglu.com/ccl0326/archive/2010/12/12/136236.htmlhttp://www.shnenglu.com/ccl0326/comments/136236.htmlhttp://www.shnenglu.com/ccl0326/archive/2010/12/12/136236.html#Feedback0http://www.shnenglu.com/ccl0326/comments/commentRss/136236.htmlhttp://www.shnenglu.com/ccl0326/services/trackbacks/136236.htmlW一ơ周六周日在加班
W一ơ担赯?br>W一ơ怕自׃?br>W一ơ有那么一点理解到底什么是ȝ
q一天仅作纪?)



Vincent 2010-12-12 21:55 发表评论
]]>
游戏服务端程序员?/title><link>http://www.shnenglu.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.shnenglu.com/ccl0326/archive/2010/10/19/130461.html</guid><wfw:comment>http://www.shnenglu.com/ccl0326/comments/130461.html</wfw:comment><comments>http://www.shnenglu.com/ccl0326/archive/2010/10/19/130461.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ccl0326/comments/commentRss/130461.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ccl0326/services/trackbacks/130461.html</trackback:ping><description><![CDATA[号:16043631 已经250Z q差250<br>帮朋友宣传一?)<br> <img src ="http://www.shnenglu.com/ccl0326/aggbug/130461.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ccl0326/" target="_blank">Vincent</a> 2010-10-19 16:35 <a href="http://www.shnenglu.com/ccl0326/archive/2010/10/19/130461.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]《深度探索C++对象模型》读书笔记[二]http://www.shnenglu.com/ccl0326/archive/2010/10/14/129825.htmlVincentVincentThu, 14 Oct 2010 02:36:00 GMThttp://www.shnenglu.com/ccl0326/archive/2010/10/14/129825.htmlhttp://www.shnenglu.com/ccl0326/comments/129825.htmlhttp://www.shnenglu.com/ccl0326/archive/2010/10/14/129825.html#Feedback0http://www.shnenglu.com/ccl0326/comments/commentRss/129825.htmlhttp://www.shnenglu.com/ccl0326/services/trackbacks/129825.html2002-7-6

3.3 Data Member的存?br>1Q?nbsp;  不管什么情况,每一个static data member只有一个实体,攑֜E序的data segment之中Q每ơ程序取用static memberQ不是通过operator::q是member selection operatorQ都会被内部转化为对该唯一extern实体的直接参考操作。每一个static member的存取以及与class的关联不会导致Q何执行时间或I间上的额外负担。如果有两个classesQ每一个都声明了一个static member freeListQ那么当它们都放在程序的data segmentӞ׃D名称冲突Q编译器的解x法是使用name-manglingQ暗中对每一个static data member~码Q以获得一个独一无二的程序识别代码?/p>

2Q?nbsp;  有多个~译器,有多少Uname-mangling做法QQ何name-mangling做法都有两个要点Q?/p>

ü          一U算法,推导出独一无二的名Uͼ

ü          如果~译pȝ或者环境工具必d使用者交谈,那些独一无二的名U可被轻易推导回原先的名U?/p>

3Q?nbsp;  取一个static data member的地址Q会得到一个指向其数据cd的常量指针,而不是指向其class member的指针?/p>

4Q?nbsp;  nonstatic data members直接攑֜每一个class object之中Q除非经q显C的explicit或隐含的implicit class objectQ没有办法直接存取它们。只要程序员在一个member function中直接处理一个nonstatic data memberQ所谓implicit class object׃发生Q其实质是编译器会ؓq个member function增添一个const this指针Q而在函数体内通过q个this指针来存取nontatic data member?/p>

5Q?nbsp;  Ʋ对一个nonstatic data memberq行存取操作Q编译器需要把class object的v始地址加上data member的编译量offsetQ如地址&someObject.someMember{于&someobject + (&theClass::someMember – 1);指向data member的指针,其offset值L会被加上1Q这样可以ɾ~译pȝ区分Z个指向classW一个data member的指针和一个没有指向Q何data member的指针?/p>

6Q?nbsp;  每一个nonstatic data member的偏U量在编译时期即可获知,甚至如果member属于一个单一或多重承体pMbase class subobject也是一P因此其存取效率和一个C struct member或一个nonderived class的member的存取效率是一L。但是在虚拟l承的情况下另当别ZQ如果该nonstatic data member是一个virtual base class的memberQƈ且通过指针来存取的话,在编译时期就不会得知q个member真正的offset位置Q所以这个存取操作必dgq至执行期,l由一个额外的间接导引才能够解冟?/p>

2002-7-7

3.4 “l承”与Data Member
1Q?nbsp;  在C++l承模型中,一个derived class object所表现出来的东西,是其自己的members加上其base classes members的d。C++q未规定derived class members和base classes members的排列次序。不q,在大部分~译器上Q除virtual base class外,base class membersL先出现?/p>

2Q?nbsp;  一般而言Q具体承concrete inheritanceq不会增加空间或存取旉上的额外负担?/p>

3Q?nbsp;  把两个原本独立不相干的classes凑成一对type/subtypeQƈ带有l承关系Ҏ犯两个错误。一是可能会重复设计一些相同操作的函数Q一般而言Q选择某些函数做成inline函数Q是设计class的一个重要课题;二是把一个class分解为多层,有可能会Z表现class体系之抽象化Q因为编译器的边界调整而膨胀所需I间。其Ҏ原因是C++保证出现在derived class中的base class subobject有其完整原样性?/p>

4Q?nbsp;  C++最初问世时Q许多编译器把vptr攑֜class object的尾端,q样可以保留base class C struct的对象布局。此后,某些~译器开始把vptr攑֜class object的开始处Q这样会l多重承下通过指向class members之指针调用virtual function带来一些帮助,否则Q在执行期不仅必d妥从class objectL处开始量LoffsetQ而且必须备妥class vptr之间的offset?/p>

5Q?nbsp;  单一l承提供了一U自然多态的形态,是关于class体系中base type和derived type之间的{换。一般来_base class和derived class objects都是从相同的地址开始。但若将vptr攑֜class object的v始处Q如果base class没有virtual function而derived class有,那么单一l承的自然多态就会打破。此Ӟ把一个derived object转换为其basecd需要编译器的介入,用以调整地址。而在既是多重l承又是虚拟l承的情况下Q编译器的介入则更有必要?/p>

6Q?nbsp;  多重l承的复杂度在于derived class和其上一个base class乃至上上一个base class之间的非自然关系Q其主要问题发生在derived class objects和其W二或后l的base class objects之间的{换。对一个多重派生对象,其地址指定l最左端base class的指针,情况和单一l承相同Q而第二个或后l的base class的地址指定操作则需要修改地址Q加上或减去Q若是downcastQ介于中间的base class subobjects的大。C++q未要求多重l承时derived class object中各个base class subjectes的排列次序,目前各个~译器都是根据声明次序来排列它们?/p>

7Q?nbsp;  class内如果内含一个或多个virtual bass class subobjectsQ将被分割ؓ两部分:一个不变局部和一个共享局部。不变局部L拥有固定的offsetQ其数据用以指定׃n局部的位置Q可以直接存取;而共享局部表现的是virtual base class subobjectQ其位置会因为每ơ的z操作而变化,只可间接存取。各家编译器实现技术之间的差异在于间接存取的Ҏ不同?/p>

8Q?nbsp;  一般而言Qvirtual base class最有效的一U运用方式是Q一个没有Q何data member的抽象class?/p>

2002-7-14

3.5 对象成员的效?br>如果没有把优化开x开Q就很难猜测一个程序的效率表现Q因为程序代码潜在性的受到某些与编译器有关的东西的影响。程序员如果兛_效率Q应该实际测试,不要光凭推论或常识判断或假设。优化操作ƈ不一定L能够有效q行?/p>

2002-7-15

3.6 指向Data Members的指?br>指向data members的指针可用来详细调查class members的底层布局Q可用来军_vptr是放在class的v始处q是Q还可用来决定class中access sections的次序?/p>

取一个nonstatic data member的地址Q将会得到它在class的offsetQ而取一个static data member的地址或者取一个绑定于真正class objectw上的data member的地址Q将会得到该member在内存中的真正地址。这也正是someType someClass::*和someTye *潜在的区别?/p>

2002-7-16

Function语意?The Semantics of Function
C++支持三种cd的member functionsQstatic、nonstatic和virtualQ每一U类型的调用方式都不同?/p>

4.1 Members的各U调用方?br>1Q?nbsp;  C++的设计准则之一便是nonstatic member function臛_必须和一般的nonmember function有着相同的效率。编译器内部会将member函数实体转换为对{的nonmember函数实体Q其步骤为:

ü          改写函数原型signature以安插一个额外的参数this到member function中,使得class object可以调用该函数。其中,this是const指针Q若该函CؓconstQ则反映在this上面的结果是this指向的data也ؓconstQ?/p>

ü          每一个对nonstatic data member的存取操作改为经由this指针来存取;

ü          member function重新写成一个外部函敎ͼ对函数名U进行mangling处理Q?/p>

此后Q每一个函数调用操作也都必{换,用以提供相应的实参?/p>

2Q?nbsp;  关于虚拟函数的内部{换步骤:若normalize是一个virtual member functionQptr->normalize();会被内部转化?*ptr->vptr[t])(ptr); 事实上,vptr名称也会被mangledQ因为可能存在有多个vptrsQt是vitrual table slot的烦引|兌到normalize函数Q第二个ptr表示this指针?/p>

3Q?nbsp;  使用class scope operator明确调用一个vitual functionQ或l由一个class object调用一个vitual function其决议方式会和nontatic member function一P故virtual function的一个inline函数实体可被扩展开来,因而提供极大的效率利益?/p>

4Q?nbsp;  static member function的主要特征是没有this指针Q这D它不能直接存取其class中的nonstatic membersQ不能被声明为const、volatile或virtualQ也不需要经由class object才能调用。static member function会被提出于class声明之外Qƈl予一个经qmangled的适当名称。如果取一个static member function的地址Q得到的是其在内存中的地址Q其地址cdq不是一个指向class member function的指针,而是一个nonmember函数指针。static member function的一个意想不到的好处是可以成Z个callback函数Q也可以成功地应用在thread函数w上?/p>

2002-07-17

4.2 Virtual Member Functions虚拟成员函数
1Q?nbsp;  C++中,多态polymorphism表示以一个public base class指针或referencedZ个derived class object。识别一个class是否支持多态,唯一适当的方法试看它是否有Q何virtual function。只要class拥有一个virtual functionQ它需要一份额外的执行期型别判断信息?/p>

2Q?nbsp;  一个class只会有一个virtual tableQ其中内含对应class object中所有的active virtual functions的函数实体的地址。这些active virtual functions包括Q?/p>

ü          一个class定义的函数实体。它会改写overriding一个可能存在的base class virtual function?/p>

ü          l承自base class的函数实体。此时该class不改写base class virtual function?/p>

ü          一个pure_virtual_called()函数实体Q它既可以扮演pure virtual function的空间保卫者,也可以当作执行期异常处理函数。如果该函数被调用,通常的操作是l束E序?/p>

3Q?nbsp;  每一个virtual function都被指派一个固定不变的索引|该值在整个l承体系中保持与特定virtual function的关联。这样就可以在编译时期设定virtual function的调用?/p>

2002-7-20

4Q?nbsp;  多重l承下,一个上层basse classes数目为n的derived classQ它内含n-1个额外的virtual tables。其主要实体与最左端的base class׃nQ其中包含所有virtual functios的地址Qn-1个次要实体与其它base classes有关Q其中只包含出现在对应base class中virtual functions的地址?/p>

5Q?nbsp;  在多重承中支持virtual functionQ其复杂度围l在W二个及后base class上,以及执行期this指针调整上。第二(或后l)base class会媄响对virtual function支持?U情况:

ü          通过指向W二个base class的指针,调用derived class virtual functionQ?/p>

ü          通过指向derived class的指针,调用W二个base class中一个承而来的virtual functionQ?/p>

ü          允许virtual function函数的返回值类型有所变化Q可能是base typeQ也可能是publicly derived type?/p>

6Q?nbsp;  关于执行期this指针调整比较有效率的解决Ҏ是thunk。所谓thunk是一端assembly码,用来以适当的offset值来调整this指针q蟩到相应的virtual function。thunk技术允许virtual table slotl箋内含一个简单的指针Q此时多重承将不需要Q何空间上的额外负担!slots中的地址可以直接指向virtual functionQ也可以指向一个相关的thunk?/p>

4.3 函数的效?br>nonmember、static member和nonstatic member function在内部都会{化ؓ完全相同的Ş式,三者效率相同?/p>

2002-08-08

4.4 指向Member Function的指?br>对一个nonstatic member function取址Q得到的是该函数在内存中的地址Q而面对一个virtual functionQ得到的是一个烦引倹{这个值是不完整的Q必被l定于一个class object上,才能够通过它调用函数。指向member function的指针的声明语法Q以及指向member selectionq算W的指针Q其作用是作为this指针的空间保留者。因此,static member function的类型是函数指针Q而不是指向member function的指针?/p>

使用一个member function指针Q如果ƈ不用于virtual function、多重ѝvirtual base class{情늚话,其成本ƈ不比使用一个nonmember function指针要高?/p>

4.5 Inline Functions
关键词inline只是一请求。如果在某个层次上,函数的执行成本比一般的函数调用及返回机制所带来的负荷低Q那么该h被接受,~译器就用一个表辑ּ合理地将函数扩展开来。真正的inline函数扩展操作是在函数调用的那一点上。在inline扩展期间Q每一个Ş式参C被对应的实际参数所取代Qinline函数中的每一个局部变量都必须被放在函数调用的一个封闭区D中Qƈ拥有一个独一无二的名U。这会带来参数的求值操作以及时性对象的理?/p>

2002-08-11

构造、解构、拷贝语意学  Semantics of Construction, Destruction, and Copy
1Q?nbsp;  一般而言Qclass的data member应该被初始化Q而且只在constructor中或其它member functions中初始化Q其它Q何操作都破坏其装性质Q其维护和修改更加困难?/p>

2Q?nbsp;  可以定义q调用invoke一个pure virtual functionQ但它只能被静态调用,不能l由虚拟机制调用。每一个derived class destructor会被~译器加以扩展,静态调用每一个virtual base class以及上一层base class的destructor。因此,不管base class的virtual destructor是否声明为pureQ它必须被定义?/p>

5.1 无承情况下的对象构?br>C++ Standard要求~译器尽量gqnontrivial members的实际合成操作,直到真正遇到其用场所为止?/p>

5.2 l承体系下的对象构?br>一般而言Q承体pM~译器对constructor所作的扩充操作以及ơ序大约如下Q?/p>

ü          所有virtual base class constructors必须从左到右、从深到被调用Q如果class被列于member initialization list中,那么M明确指定的参数都必须传递过去,否则如果class有一个default constructorQ也应该调用它;class中的每一个virtual base class subobject的偏U量offset必须在执行期可被存取Q如果class object是最底层most-derived的classQ其constructors可能被调用,某些用以支持q个行ؓ的机制必被方进来?/p>

ü          以base class的声明次序调用上一层base class constructorsQ如果base class被列于member initialization list中,那么M明确指定的参数都必须传递过去,否则若它有default constructor或default memberwise copy constructorQ那么就调用它;如果base class是多重承下的第二或后的base classQ那么this指针必须有所调整?/p>

ü          如果class object有virtual table pointer(s)Q它Q们Q必被讑֮初|指向适当的virtual table(s)?/p>

ü          如果有一个member没有出现在member initialization list中,但它有default constructorQ调用之?/p>

ü          member initialization list中的data members的初始化操作以members的声明次序放qconstructor的函数本w?/p>

2002-8-18

5.3对象复制语意?Object Copy Semantics
1Q?nbsp;  只有在默认行为所D的语意不安全或者不正确以致发生别名化aliasing或者内存泄漏memory leakӞ才需要设计一个copy assignment operator。否则,E序反倒会执行得较慢?/p>

2Q?nbsp;  如果仅仅是ؓ了把NRV优化开x开而提供一个copy constructorQ那么就没有必要一定要提供一个copy assignment operator?/p>

3Q?nbsp;  copy assignment operator有一个非正交情况Q那是它缺乏一个^行于member initialization list的member assignment list。调用base class的copy assignment operatorCZQ?/p>

Point::operator = (p3d); ?(*(Point*)this) = p3d; ?(Point &)(*this) = p3d;

4Q?nbsp;  事实上,copy assignment operator在虚拟承情况下行ؓ不佳Q需要小心设计和说明。许多编译器甚至q不试取得正确的语意,它们在每一个中间的copy assignment operator中调用每一个base class instanceQ于是造成virtual base copy assignment operator的多个实体被调用。徏议尽可能不要允许一个virtual base class的拷贝操作,q不要在Mvirtual base class中声明data member?/p>

5.5解构语意?Semantics of Destruction
如果class没有定义destructorQ那么只有在其内带的member object或base class拥有destructorӞ~译器才会自动合成出一个destructor。一个由E序员定义的destructor被扩展的方式cMconstructors被扩展的方式Q只是顺序相反:

ü          destructor的函数本体首先被执行Q?/p>

ü          如果class拥有member class objectsQ而后者拥有destructorsQ那么它们将以声明的相反序而调用;

ü          如果object内带一个vptrQ则现在被重新设定以指向适当base class之virtual tableQ?/p>

ü          如果有Q何直接的nonvirtual base classes拥有destructorQ它们将以声明的相反序而调用;

ü          如果有Q何virtual base classes拥有destructorQ而前面讨论的q个class是most-derived classQ那么它们会以原先构造顺序的相反序被调用?/p>

2002-8-19

执行期语意学 Runtime Semantics
6.1对象的构造和解构
1Q?nbsp;  一般而言Qconstructor和destructor的安插都如你所预期。但如果一个区D|函数中有一个以上的d点,情况׃复杂一些,destructor会放在每一个离开点之前。通常Q我们要求将object可能放在用它的那个程序区附近Q这样做可以节省不必要的对象产生和销毁操作?/p>

2Q?nbsp;  C++E序中所有的global objects都被攄在程序的data segment中,如果不明指定初|object所配置的内存内容将?QCq不自动讑֮初|。如果global object有constructor和destructor的话Q我们说它需要静态的初始化和内存释放操作?/p>

2002-8-20

3Q?nbsp;  virtual base class的subobject在每个derived class中的位置可能会变动,不能在编译时期确定。以一个derived class的pointer或reference来存取virtual base class subobjectQ是一Unonconstant expressionQ必d执行期方可评估求倹{?/p>

4Q?nbsp;  使用静态初始化的object有一些缺炏V其一Q无法放入try区段QQ何throw操作必将触发exception handling library的默认函数terminate()Q其二,E序员必Mؓ控制“需要跨模块做静态初始化”objects的依赖顺序而生的复杂度付Z仗徏议根本就不要使用那些需要静态初始化的global objects?/p>

5Q?nbsp;  新的C++标准要求~译单位中的static local class objects必须在相应函数第一ơ被调用时才被构造,而且必须以相反的ơ序销毁。由于这些objects是在需要时才被构造,因此~译时期无法预期光合和序。ؓ支持新标准,可能要对被生出来的static local class objects保持一个执行期链表?/p>

2003-8-1

6Q?nbsp;  对于对象数组定义Q晚q的~译器一般会提供两个函数Q分别用于处理没有virtual base class的classQ以及内带virtual base class的class Q它们通常被称为vec_new、vec_vnew。前者类型通常为:

void* vec_new(                                                   // 初始化程序员未提供初值的q箋元素

           void *array,                                               // 数组起始地址若ؓ0Q则动态分?/p>

           size_t elem_size,                                       // 每一个class object的大?/p>

           int elem_count,                                         // 数组中的元素数目

           void (*constructor) (void *),                    // class的default constructor指针

           void (*destructor) (void *, char)               // class的destructor指针Q以0填入

); 如果E序员提供带有默认参数值的default constructorQ编译器要做Ҏ处理Q以传入默认参数|

对应销毁数l的两个函数分别为vec_delete、vec_vdelete。前者类型通常为:

void* vec_delete(

           void *array,                                               // 数组起始地址

           size_t elem_size,                                       // 每一个class object的大?/p>

           int elem_count,                                         // 数组中的元素数目

           void (*destructor) (void *, char)               // class的destructor指针

);

6.2 new和deleteq算W?br>         注意区分operator new和new operatorQ前者负责分配内存;后者先调用前者分配内存,然后调用constructor以实施初始化?/p>

?/p>

《深度探索C++对象模型》读书笔?

 

本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/xjtuse_mal/archive/2007/03/01/1517809.aspx



Vincent 2010-10-14 10:36 发表评论
]]>
þоƷ| avþþƷ| Ļþۺ | þۺϾɫۺϾ99| þۺϺݺۺϾþۺ88 | ƷۺϾþ | þѸƵ| 99þù뾫Ʒ| þþƷԭ| AV˾þԭ| þþþһëþþ| þþþþAŷAV| պľþ| AVþ| 91Ʒ91þþþþ| Ʒþþþþ֣ݹ˾ | þerƷѹۿ8| һþaþþƷۺҹҹ| ٸ޾þþþþ| þ99ƷСѼ| ƷþþþAV| þùƷ99| ˶ݺɫۺϾþ| ޹˾ƷŮ˾þþ | ۺϾƷ㽶þ| þ㽶97Ʒ| ޳˾Ʒþ| þùŷպƷ| Ըߺþþþþþþ| ƷþþþþþҹƬ | þݺҹҹ2020 | þùɫAVѿ| Ӱһþþþó˾Ʒۺ | Ļձ޾þþ| ޾Ʒþþþþþþþþþ| ҹƷþ2021 | žžƷ99þþ㽶| þAٸ۲ӰԺ| þˬˬAV| þƵվ| þþùҺ|