<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Programming Life with Music</title>
	<atom:link href="http://jackaldire.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://jackaldire.com</link>
	<description>JackalDire &#039;s Blog</description>
	<lastBuildDate>Thu, 11 Mar 2010 01:50:13 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/><atom:link rel="hub" href="http://superfeedr.com/hubbub"/>		<item>
		<title>惠普实习生笔试总结</title>
		<link>http://jackaldire.com/201003/hp-written-exam-summary/</link>
		<comments>http://jackaldire.com/201003/hp-written-exam-summary/#comments</comments>
		<pubDate>Thu, 11 Mar 2010 01:24:29 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[日记]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[笔试]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=95</guid>
		<description><![CDATA[Like The Wind &#8211; Guitar Version, from 爱情白皮书OST by S.E.N.S 
老早投了一个HP的实习生，主要是HP UX下的开发、测试和运维，周二通知我周三笔试&#8230;今天下午去国贸HP总部笔了一场，感觉和有道的笔试大不相同。
一出国贸地铁站，就看到马路对面的HP大厦，比清华科技园的任何一座楼都华丽。走进大厦，到处都是西装革履皮鞋锃亮的中年白领，年龄普遍30以上，与清华科技园那边二十来岁、一身便装的工程师形成鲜明对比。
到了14层，一个估计快40的工程师接我进去，发给我一份题就开始做。笔试题总共有张A4纸，正反一共8面，大约100题，2个小时，刚拿到题被题量吓到了，浏览了一下发现题目都非常基础，但是面面据到。从基础知识、C语言、C++、操作系统原理、操作系统应用、编译原理、开发环境使用、数据库无所不有。

第一大题是类似填空，大约有30小题
前面几道题是列举题，考知识面：

列举1个你了解的C++编译器及版本（gcc 4.4, icc 9.0, vc 2010, watcom c++ 1.9, etc）；
列举1个你了解的关系型数据库（Oracle, DB2, SyBase, MySQL, etc）；
列举1个你了解的版本控制软件（CVS, SVN, VSS, GIT, Bazaar, Mecurial, etc）；
分别列举1个CISC和RISC处理器（CISC: x86; RISC: MIPS, SPARK, ARM, PowerPC）；
列举1个Unix操作系统发行版（Solaris, HP-UX, Novell Netware, etc）；
列举1个你了解的shell（bash, zsh, ksh, csh, etc）；

其他题目都没有什么新奇的，有不少unix相关的题，都轻松拿下，只有一道题突然想不起来了，问“nohup命令有什么作用？”，回来才想起来，nohup使得程序忽略hangup信号，使得在某个虚拟终端下的程序即使终端关闭也不会停止运行。
最后一小题正好是我寒假在某个blog看到过：“比较分析以函数返回值和异常两种处理错误的方式”，我写的是：用异常处理错误使得正常逻辑代码和错误处理代码分离，相对函数返回值方法代码可读性较高、程序局部性较好；而函数返回值适合于小型系统以及底层系统代码，相对开销比较低。
第二大题是选择题，和第一大题基本类似
总共也有30小题左右，印象只有一道关于UML的题是瞎蒙的，虽然看过一点UML，但是基本没有实际应用过，早就忘干了。也都是很基础的题目，比如编译过程的几个阶段，一些基础的C/C++语法题目，只要是学计算机的并且稍微听了点课的人都可以做出来。
第三大题是一下语言相关的题
一道编程题，要求用C++模板写一个泛型的max函数，假定对象提供了]]></description>
			<content:encoded><![CDATA[<div>Like The Wind &#8211; Guitar Version, from <em>爱情白皮书OST</em> by S.E.N.S </div>
<p>老早投了一个HP的实习生，主要是HP UX下的开发、测试和运维，周二通知我周三笔试&#8230;今天下午去国贸HP总部笔了一场，感觉和有道的笔试大不相同。</p>
<p>一出国贸地铁站，就看到马路对面的HP大厦，比清华科技园的任何一座楼都华丽。走进大厦，到处都是西装革履皮鞋锃亮的中年白领，年龄普遍30以上，与清华科技园那边二十来岁、一身便装的工程师形成鲜明对比。</p>
<p>到了14层，一个估计快40的工程师接我进去，发给我一份题就开始做。笔试题总共有张A4纸，正反一共8面，大约100题，2个小时，刚拿到题被题量吓到了，浏览了一下发现题目都非常基础，但是面面据到。从基础知识、C语言、C++、操作系统原理、操作系统应用、编译原理、开发环境使用、数据库无所不有。</p>
<p><span id="more-95"></span></p>
<h2>第一大题是类似填空，大约有30小题</h2>
<p>前面几道题是列举题，考知识面：</p>
<ul>
<li>列举1个你了解的C++编译器及版本（gcc 4.4, icc 9.0, vc 2010, watcom c++ 1.9, etc）；</li>
<li>列举1个你了解的关系型数据库（Oracle, DB2, SyBase, MySQL, etc）；</li>
<li>列举1个你了解的版本控制软件（CVS, SVN, VSS, GIT, Bazaar, Mecurial, etc）；</li>
<li>分别列举1个CISC和RISC处理器（CISC: x86; RISC: MIPS, SPARK, ARM, PowerPC）；</li>
<li>列举1个Unix操作系统发行版（Solaris, HP-UX, Novell Netware, etc）；</li>
<li>列举1个你了解的shell（bash, zsh, ksh, csh, etc）；</li>
</ul>
<p>其他题目都没有什么新奇的，有不少unix相关的题，都轻松拿下，只有一道题突然想不起来了，问“nohup命令有什么作用？”，回来才想起来，nohup使得程序忽略hangup信号，使得在某个虚拟终端下的程序即使终端关闭也不会停止运行。</p>
<p>最后一小题正好是我寒假在某个blog看到过：“比较分析以函数返回值和异常两种处理错误的方式”，我写的是：用异常处理错误使得正常逻辑代码和错误处理代码分离，相对函数返回值方法代码可读性较高、程序局部性较好；而函数返回值适合于小型系统以及底层系统代码，相对开销比较低。</p>
<h2>第二大题是选择题，和第一大题基本类似</h2>
<p>总共也有30小题左右，印象只有一道关于UML的题是瞎蒙的，虽然看过一点UML，但是基本没有实际应用过，早就忘干了。也都是很基础的题目，比如编译过程的几个阶段，一些基础的C/C++语法题目，只要是学计算机的并且稍微听了点课的人都可以做出来。</p>
<h2>第三大题是一下语言相关的题</h2>
<p>一道编程题，要求用C++模板写一个泛型的max函数，假定对象提供了<操作符。加上函数头一共两行，唯一要注意的是要把参数写成常引用（const T &#038;），返回值最好也写成const（原因详见Effecctive C++），细节决定成败。</p>
<p>还有几道改错题，前几道是语法错误，比如说 void register(const char * s, int a);看了半天发现原来rigister是个关键字，诸如此类。最后一道题，给出一个语法正确但是逻辑有问题的程序，并给出了几组test case，要求写出每组test case的输出，指出那个test case的输出有错，并定位bug，然后fix bug。程序一共30行左右，是一个计算日期的程序，出去类的声明和函数头，核心代码也就10行，无非就是除法和求余的时候off by one了，通过测试用例可以很快定位bug。</p>
<h2>第四大题是一个连线题</h2>
<p>考的是unix的基本命令，左边是命令，右边是功能。有pwd、vi、chown、chmod、w、man等十几个命令。</p>
<h2>最后一道大题出自cut命令的man手册页</h2>
<p>题目给出了cut命令的man手册页的全部内容，要求翻译指定的一段，并且用cut命令完成一个指定的功能，也不是太难。</p>
<p>花了不到一个半小时全不做完，感觉不错。做完后和带我进来的那个工程师聊了一会，他问了一下我的情况，并且告诉我大概一周出结果。我问了他实习的工作，大概分成三种：开发、测试和运维，都是在HP-UX系统上。</p>
<p>总得来说觉得这次笔试感觉挺顺利，题目虽然很多、覆盖面很广，但是都很基础，非常适合我这种什么都懂一点但都不精通的人。</p>
<p>笔试如果过了，面试估计得下下周了。</p>
<p>到了抉择的时候了，自己到底是去做互联网相关的应用开发，还是去类似HP这样的企业级解决方案提供商做系统开发呢，犹豫不定啊。</p>

	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li>No Related Post</li>
	</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/201003/hp-written-exam-summary/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="http://storage.live.com/items/9151E070FED682BB!174?filename=03%20-%20Crawling%20" length="5923270" type="audio/mpeg" />
<enclosure url="http://storage.live.com/items/9151E070FED682BB!175?filename=4.Like%20the%20Wind%20~%20Guitar%20Version%20.mp3" length="3780949" type="audio/mpeg" />
		</item>
		<item>
		<title>有道实习生面试总结</title>
		<link>http://jackaldire.com/201003/youdao-intern-interview-summary/</link>
		<comments>http://jackaldire.com/201003/youdao-intern-interview-summary/#comments</comments>
		<pubDate>Tue, 09 Mar 2010 01:58:02 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[面试]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=93</guid>
		<description><![CDATA[Crawling(LP) &#8211; Piano, from Piano Instruments 
昨天去面了有道研发实习生，总得来说感觉不是很好。总结一下是很必要的。
面试下午三点开始，总共持续了一个小时，纯粹的技术面，没有问任何非技术问题。
一开始问了一些开发经验的问题，问我主要主要使用的开发语言是什么，会不会java，有没有windows开发经验等等。还有一个尖锐的问题就是问我现在在上大三，时间上如何保证&#8230;.我又不好意思直接说天天翘课，就说对通信相关的课没什么兴趣。

下面的问题是答得最失败的问题，题目是：“DNS是用tcp协议还是udp协议？为什么？”
这个问题从直觉上感觉就是udp，但不知道如何从什么角度分析原理。于是乎扯了一堆dns服务器层次结构、如何处理dns请求。回来查了一下，发现问题并没有考虑全面：“dns协议客户在查询时用udp，在服务器见传送域名信息时用tcp协议”。想了一下，查询时用udp协议大概是为了降低服务器负载，若采用tcp协议，服务器要增加三次握手和错误重发负担，一旦出现网络阻塞，还会导致大量半开连接。另一方面，服务器之间的域名信息交换则要求较高的可靠性，宜用tcp协议。
这道题目的经验就是，HR问一个问题后，不要张口就说，需要先进行全面的思考，组织好语言逻辑后，再开始回答
下面是一道编程题：给定一个入栈序列，判断一个出栈序列是否合法。比如入栈序列是12345，54321是合法的出栈序列之一。
因为说的是编程题，所以没在算法上考虑太长时间，写了个搜索。还是和笔试的问题一样，代码比较混乱，前后花了近20分钟。回来后找到一篇论文，介绍了一种线性的方法，过几天准备单独写一篇blog分析这个问题。
最后是一道简单的算法题：给定一个数组{ai}，求max{ai&#124;i]]></description>
			<content:encoded><![CDATA[<div>Crawling(LP) &#8211; Piano, from <em>Piano Instruments</em> </div>
<p>昨天去面了有道研发实习生，总得来说感觉不是很好。总结一下是很必要的。</p>
<p>面试下午三点开始，总共持续了一个小时，纯粹的技术面，没有问任何非技术问题。</p>
<p>一开始问了一些开发经验的问题，问我主要主要使用的开发语言是什么，会不会java，有没有windows开发经验等等。还有一个尖锐的问题就是问我现在在上大三，时间上如何保证&#8230;.我又不好意思直接说天天翘课，就说对通信相关的课没什么兴趣。</p>
<p><span id="more-93"></span></p>
<p>下面的问题是答得最失败的问题，题目是：“DNS是用tcp协议还是udp协议？为什么？”</p>
<p>这个问题从直觉上感觉就是udp，但不知道如何从什么角度分析原理。于是乎扯了一堆dns服务器层次结构、如何处理dns请求。回来查了一下，发现问题并没有考虑全面：“dns协议客户在查询时用udp，在服务器见传送域名信息时用tcp协议”。想了一下，查询时用udp协议大概是为了降低服务器负载，若采用tcp协议，服务器要增加三次握手和错误重发负担，一旦出现网络阻塞，还会导致大量半开连接。另一方面，服务器之间的域名信息交换则要求较高的可靠性，宜用tcp协议。</p>
<p>这道题目的经验就是，HR问一个问题后，不要张口就说，需要先进行全面的思考，组织好语言逻辑后，再开始回答</p>
<p>下面是一道编程题：给定一个入栈序列，判断一个出栈序列是否合法。比如入栈序列是12345，54321是合法的出栈序列之一。</p>
<p>因为说的是编程题，所以没在算法上考虑太长时间，写了个搜索。还是和笔试的问题一样，代码比较混乱，前后花了近20分钟。回来后找到一篇论文，介绍了一种线性的方法，过几天准备单独写一篇blog分析这个问题。</p>
<p>最后是一道简单的算法题：给定一个数组{ai}，求max{ai|i<j}。</p>
<p>这道题还是很简单的，画个图就分析一下OK了。O(n)的解法是：从数组的左端开始扫描，维护一个当前最大值max，和当前的最大差值maxd，因为最大差值肯定是由当前的最大值减去当前的最小值得到的，所以计算一下当前最大值和这一位的差值，并且更新当前最大差值就行了。</p>
<h2>总结</h2>
<ol>
<li> HR提问时，尤其是一些和自己经历相关的经历，一定要主动展开，不要HR问一句回答一句。比如这次HR问到我大二写的那个类RAR的文件压缩器，我只是简单的说了一下是用MFC写的文件压缩器，而没有展开说<br />

	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li><a href="http://jackaldire.com/201003/youdao-written-exam-summary/" title="有道实习生笔试总结 (2010年03月6日)">有道实习生笔试总结</a> (6)</li>
	<li><a href="http://jackaldire.com/200908/quick-sort-analysis/" title="快速排序详细分析 (2009年08月27日)">快速排序详细分析</a> (2)</li>
	<li><a href="http://jackaldire.com/200908/reverse-token/" title="[备忘]倒置字符串中的单词 (2009年08月18日)">[备忘]倒置字符串中的单词</a> (0)</li>
	<li><a href="http://jackaldire.com/200911/string-matching-with-wildcard/" title="[一道面试题]含有*的字符串匹配问题 (2009年11月25日)">[一道面试题]含有*的字符串匹配问题</a> (1)</li>
	<li><a href="http://jackaldire.com/200905/smart-way-to-solve-fibonacci/" title="Fibonacci数的巧妙求法 (2009年05月6日)">Fibonacci数的巧妙求法</a> (2)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/201003/youdao-intern-interview-summary/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="http://storage.live.com/items/9151E070FED682BB!174?filename=03%20-%20Crawling%20" length="5923270" type="audio/mpeg" />
		</item>
		<item>
		<title>有道实习生笔试总结</title>
		<link>http://jackaldire.com/201003/youdao-written-exam-summary/</link>
		<comments>http://jackaldire.com/201003/youdao-written-exam-summary/#comments</comments>
		<pubDate>Fri, 05 Mar 2010 16:54:12 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[日记]]></category>
		<category><![CDATA[实习]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[编程]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=91</guid>
		<description><![CDATA[Wo Ai Nee- Arabesque, from Hana &#038; Alice OST  by 岩井俊二
首先要感谢Ant漂亮的简历模板。
周三去参加了有道研发实习生的笔试。虽然很简单，但毕竟是人生的第一次笔试，想了想还是写点东西总结下。
题目总的来说很基础，而且不多，一个半小时的时间非常充足。
第一大题是一些基础概念的简答题，每题限制50字或者100字以内。一共5小题，有一题想不起来了：


tcp和udp的区别
process和thread的区别
Ajax的概念和应用场景
介绍一下最近三个月听说的互联网产品

题目都比较基础。庆幸去年暑假做了一点Firefox插件的开发（感谢晓松学长），Ajax那题目也算答出来了。互联网产品那题写了Google buzz（这个是人都会写）、sougou云输入法、goo.gl的短网址服务（一时想不出来了，拿这个凑数），虽然过年的时候花了那么多时间在网上，到要写点东西的时候还是捉襟见肘，以后要养成记笔记的习惯。

第二大题要写出5种排序算法，并一句话简述原理和时间复杂度。我一高兴写了很多种，心想自己擅长的方面总要发挥一下嘛。

最后是两道编程题
第一题是给出一个图（不一定是连通图）的边表，求这个图是否含有环。
这到题的题目中的数据结构描述很不清楚，我花了大概10分钟才看懂。把边表转换成邻接矩阵以后就是一个简单的DFS，大二学数据结构的时候自己也编过。但是由于缺乏笔试经验，平时也不怎么在纸上写程序，笔跟着思路走，难免这少一句那少一行，卷面变得相当混乱。以后遇到编程题，一定要在草稿纸上搭出框架，然后在写道卷面上。
第二题是一道算法题，题目是：给出一个数字（10,000～100,000,000），把这个数字拆分成4段，怎样使得4段的乘积最小求出4段的最小乘积。比如12345拆分成1*2*3*45=270, 10000=1*00*0*0=0。
其实这到题从直觉上觉得是一道动态规划，但是深知自己是个DP茫，推了一会没啥思路就放弃了，写了个暴力搜索了事（总不能空着吧）。出了考场给同学打电话，同学大概花了半分钟就想出来了。其实确实是个很简单的DP，该找个时间练练DP了&#8230;.状态转移方程如下：
dp[i][j] = min{ dp[i-1][j-k]} * num[j-k+1][j], (k in [1, j-i+1]) }
i是当前分段数，j是当前扫描到的字符串位置，num[i][j]是从第i个数字到第j个数字组成的数。
其实还有个很简单的优化，就是只要包含数字‘0’，那么最小乘积必然是0，单独把这个‘0’分成一段就行了。

总共花了大概一个小时，提前交了卷敢回去上下午的课。

PS：下午刚接到电话，笔试过了，下周一面试，得好好准备一下，人生第一次面试就要来了。

	Related Post
	
	有道实习生面试总结 (0)
	快速排序详细分析 (2)
	[备忘]倒置字符串中的单词 (0)
	[一道面试题]含有*的字符串匹配问题 (1)
	Fibonacci数的巧妙求法 (2)


]]></description>
			<content:encoded><![CDATA[<div>Wo Ai Nee- Arabesque, from <em><a href="http://www.xiami.com/album/167929">Hana &#038; Alice OST </a></em> by 岩井俊二</div>
<p>首先要感谢Ant漂亮的简历模板。</p>
<p>周三去参加了有道研发实习生的笔试。虽然很简单，但毕竟是人生的第一次笔试，想了想还是写点东西总结下。</p>
<p>题目总的来说很基础，而且不多，一个半小时的时间非常充足。</p>
<p>第一大题是一些基础概念的简答题，每题限制50字或者100字以内。一共5小题，有一题想不起来了：</p>
<p><span id="more-91"></span></p>
<ol>
<li>tcp和udp的区别</li>
<li>process和thread的区别</li>
<li>Ajax的概念和应用场景</li>
<li>介绍一下最近三个月听说的互联网产品</li>
</ol>
<p>题目都比较基础。庆幸去年暑假做了一点Firefox插件的开发（感谢晓松学长），Ajax那题目也算答出来了。互联网产品那题写了Google buzz（这个是人都会写）、sougou云输入法、goo.gl的短网址服务（一时想不出来了，拿这个凑数），虽然过年的时候花了那么多时间在网上，到要写点东西的时候还是捉襟见肘，以后要养成记笔记的习惯。</p>
</p>
<p>第二大题要写出5种排序算法，并一句话简述原理和时间复杂度。我一高兴写了很多种，心想自己擅长的方面总要发挥一下嘛。</p>
</p>
<p>最后是两道编程题</p>
<p>第一题是给出一个图（不一定是连通图）的边表，求这个图是否含有环。<br />
这到题的题目中的数据结构描述很不清楚，我花了大概10分钟才看懂。把边表转换成邻接矩阵以后就是一个简单的DFS，大二学数据结构的时候自己也编过。但是由于缺乏笔试经验，平时也不怎么在纸上写程序，笔跟着思路走，难免这少一句那少一行，卷面变得相当混乱。以后遇到编程题，一定要在草稿纸上搭出框架，然后在写道卷面上。</p>
<p>第二题是一道算法题，题目是：给出一个数字（10,000～100,000,000），把这个数字拆分成4段，<del datetime="2010-03-07T03:49:06+00:00">怎样使得4段的乘积最小</del>求出4段的最小乘积。比如12345拆分成1*2*3*45=270, 10000=1*00*0*0=0。</p>
<p>其实这到题从直觉上觉得是一道动态规划，但是深知自己是个DP茫，推了一会没啥思路就放弃了，写了个暴力搜索了事（总不能空着吧）。出了考场给同学打电话，同学大概花了半分钟就想出来了。其实确实是个很简单的DP，该找个时间练练DP了&#8230;.状态转移方程如下：</p>
<p>dp[i][j] = min{ dp[i-1][j-k]} * num[j-k+1][j], (k in [1, j-i+1]) }</p>
<p>i是当前分段数，j是当前扫描到的字符串位置，num[i][j]是从第i个数字到第j个数字组成的数。</p>
<p>其实还有个很简单的优化，就是只要包含数字‘0’，那么最小乘积必然是0，单独把这个‘0’分成一段就行了。</p>
</p>
<p>总共花了大概一个小时，提前交了卷敢回去上下午的课。</p>
</p>
<p>PS：下午刚接到电话，笔试过了，下周一面试，得好好准备一下，人生第一次面试就要来了。</p>

	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li><a href="http://jackaldire.com/201003/youdao-intern-interview-summary/" title="有道实习生面试总结 (2010年03月9日)">有道实习生面试总结</a> (0)</li>
	<li><a href="http://jackaldire.com/200908/quick-sort-analysis/" title="快速排序详细分析 (2009年08月27日)">快速排序详细分析</a> (2)</li>
	<li><a href="http://jackaldire.com/200908/reverse-token/" title="[备忘]倒置字符串中的单词 (2009年08月18日)">[备忘]倒置字符串中的单词</a> (0)</li>
	<li><a href="http://jackaldire.com/200911/string-matching-with-wildcard/" title="[一道面试题]含有*的字符串匹配问题 (2009年11月25日)">[一道面试题]含有*的字符串匹配问题</a> (1)</li>
	<li><a href="http://jackaldire.com/200905/smart-way-to-solve-fibonacci/" title="Fibonacci数的巧妙求法 (2009年05月6日)">Fibonacci数的巧妙求法</a> (2)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/201003/youdao-written-exam-summary/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
<enclosure url="http://storage.live.com/items/9151E070FED682BB!168?filename=13%20Wo%20Ai%20Nee-%20Arabesque.mp3" length="7338112" type="audio/mpeg" />
		</item>
		<item>
		<title>自己动手写Linux Shell（三） —— 支持IO重定向</title>
		<link>http://jackaldire.com/201001/write-your-own-shell-3-io-redirection/</link>
		<comments>http://jackaldire.com/201001/write-your-own-shell-3-io-redirection/#comments</comments>
		<pubDate>Sun, 31 Jan 2010 14:57:56 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[Linux与开源]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Shell]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=86</guid>
		<description><![CDATA[A Winter Story &#8212; from Love Letter OST
1.IO重定向功能分析
IO重定向也是Shell的基本功能之一，这篇文章http://www.ibm.com/developerworks/cn/linux/l-iotips/比较全面地介绍了Linux Shell的IO重定向功能。总结一下，IO重定向的大致格式是这样：

cmd  &#91;src &#124; &#38;&#93;     &#40;&#62; &#124; &#60; &#124; &#62;&#62; &#41;      &#40;&#38; num&#124;-&#41;  &#124; dst

貌似写得有点复杂（我不是故意的）。解释一下，整个重定向语句由三部分组成：

第一部分（[src&#124;&#038;]）：src代表被重定向的文件描述符，一般是0（stdin）、1（stdout）和2（stderr），此外还可以是‘＆’，代表2和3，也就是把stdout和stderr同时重定向。这一部分通常是省略的，‘>’和‘>>’隐含了0，‘ &#124; < &#124; >>）：三种操作符。‘’将src的输出重定向到目标文件（若文件不存在则创建，否则现有文件被截断(truncation)）；‘>>’将src的输出附加到（pending）目标文件尾部（若文件不存在则创建）。
第三部分（ (&#038; num&#124;-)  &#124; dst ）：第三部分有两种形式，最常见的就是一个文件名，代表重定向的目标。此外还可以是&#8217;&#038;'加一个数字，代表将src重定向到数字代表的文件描述符上。另外‘&#038;’后还可以接‘-’，代表关闭src。比如：

$ cmd 2&#62;&#38;1     将标准错误重定向到标准输出
$ cmd 1&#62;&#38;-    [...]]]></description>
			<content:encoded><![CDATA[<div>A Winter Story &#8212; from <em>Love Letter OST</em></div>
<h2>1.IO重定向功能分析</h2>
<p>IO重定向也是Shell的基本功能之一，这篇文章http://www.ibm.com/developerworks/cn/linux/l-iotips/比较全面地介绍了Linux Shell的IO重定向功能。总结一下，IO重定向的大致格式是这样：</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">cmd  <span style="color: #7a0874; font-weight: bold;">&#91;</span>src <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #000000; font-weight: bold;">&amp;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>     <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #000000; font-weight: bold;">&lt;</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #000000; font-weight: bold;">&gt;&gt;</span> <span style="color: #7a0874; font-weight: bold;">&#41;</span>      <span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #000000; font-weight: bold;">&amp;</span> num<span style="color: #000000; font-weight: bold;">|</span>-<span style="color: #7a0874; font-weight: bold;">&#41;</span>  <span style="color: #000000; font-weight: bold;">|</span> dst</pre></div></div>

<p>貌似写得有点复杂（我不是故意的）。解释一下，整个重定向语句由三部分组成：</p>
<p><span id="more-86"></span></p>
<p>第一部分（[src|&#038;]）：src代表被重定向的文件描述符，一般是0（stdin）、1（stdout）和2（stderr），此外还可以是‘＆’，代表2和3，也就是把stdout和stderr同时重定向。这一部分通常是省略的，‘>’和‘>>’隐含了0，‘<’隐含了1，也就是说</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ cmd <span style="color: #000000; font-weight: bold;">&lt;</span><span style="color: #c20cb9; font-weight: bold;">file</span> 等价于
$ cmd <span style="color: #000000;">0</span><span style="color: #000000; font-weight: bold;">&lt;</span><span style="color: #c20cb9; font-weight: bold;">file</span>
$ cmd <span style="color: #000000; font-weight: bold;">&gt;</span><span style="color: #c20cb9; font-weight: bold;">file</span> 等价于
$ cmd <span style="color: #000000;">1</span><span style="color: #000000; font-weight: bold;">&gt;</span><span style="color: #c20cb9; font-weight: bold;">file</span></pre></div></div>

<p>第二部分（> | < | >>）：三种操作符。‘<’将src的输入重定向到目标文件；‘>’将src的输出重定向到目标文件（若文件不存在则创建，否则现有文件被截断(truncation)）；‘>>’将src的输出附加到（pending）目标文件尾部（若文件不存在则创建）。</p>
<p>第三部分（ (&#038; num|-)  | dst ）：第三部分有两种形式，最常见的就是一个文件名，代表重定向的目标。此外还可以是&#8217;&#038;'加一个数字，代表将src重定向到数字代表的文件描述符上。另外‘&#038;’后还可以接‘-’，代表关闭src。比如：</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ cmd <span style="color: #000000;">2</span><span style="color: #000000; font-weight: bold;">&gt;&amp;</span><span style="color: #000000;">1</span>     将标准错误重定向到标准输出
$ cmd <span style="color: #000000;">1</span><span style="color: #000000; font-weight: bold;">&gt;&amp;</span>-      关闭标准输出
$ cmd <span style="color: #000000;">0</span><span style="color: #000000; font-weight: bold;">&lt;&amp;</span>-      关闭标准输入</pre></div></div>

</p>
<h2>2.IO重定向功能实现</h2>
<p>Linux的IO重定向实际上是通过dup函数和close函数配合实现。close关闭一个文件描述符，而dup复制一个文件描述符，并且新产生的文件描述符是当前可用文件描述符的最小值。</p>
<p>通过先将要重定向的fd关闭，在将重定向的目标文件的描述符dup就实现了重定向（因为012三个描述符默认都是打开的，关闭任何一个后其都将成为当前可用的最小文件描述符）。</p>
<p>为了支持重定向命令的分析，实现里新添加了一个结构，记录了每个重定向的必须信息，然后在子进程fork以后根据记录信息进行重定向操作。</p>
<p>测试的时候发现了一个有趣的问题。比如：</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ <span style="color: #c20cb9; font-weight: bold;">ls</span> <span style="color: #000000; font-weight: bold;">&gt;</span>f1 <span style="color: #000000; font-weight: bold;">&gt;</span>f2
$ <span style="color: #c20cb9; font-weight: bold;">cat</span> <span style="color: #000000; font-weight: bold;">&gt;</span>f1 <span style="color: #000000; font-weight: bold;">&gt;</span>f2</pre></div></div>

<p>这两条命令在bash里的结果是后面的重定向覆盖了前面的重定向，这和我的实现是一样的，也是最简单的。而在zsh里，第一条命令的结果是f1 f2文件有相同的内容，也就是标准输出同时被重定向到f1和f2两个文件里了；第二条命令的效果等同于先把f1、f2两个文件现连接起来再重定向到cat的输入。这是相当神奇的，到现在也没找到实现方法，看来得找时间研究一下zsh的代码了。</p>
<p>其实实现重定向最麻烦的地方是命令分析，也就是字符串处理。parse_command函数俨然已经100行了，两个switch-case加无数个if-else组成的大自动机，不加注释恐怕就是write-only了（感觉加了注释还是write-only）&#8230;</p>
<p>打开重定向文件那里有一点问题，一大堆flags或来或去，最傻的是忘了给创建的文件设置mode，结果只能用root权限查看&#8230;</p>
<p><strong>源代码 : <a href='http://jackaldire.com/wordpress/wp-content/uploads/2010/01/jdsh_v3.h'>jdsh_v3.h</a> <a href='http://jackaldire.com/wordpress/wp-content/uploads/2010/01/jdsh_v3.c'>jdsh_v3.c</a>（为了让乱七八糟的代码稍微可读一点，单独分了个头文件出来）</strong></p>
<p>剩下的任务就是管道了，有必要把代码好好整理一下了。</p>

	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li><a href="http://jackaldire.com/201001/write-your-own-shell-2-background-task/" title="自己动手写Linux Shell（二） —— 支持后台执行 (2010年01月31日)">自己动手写Linux Shell（二） —— 支持后台执行</a> (2)</li>
	<li><a href="http://jackaldire.com/201001/write-your-own-shell-1-a-simple-command-interpreter/" title="自己动手写Linux Shell（一） —— 简单的命令解释器 (2010年01月29日)">自己动手写Linux Shell（一） —— 简单的命令解释器</a> (2)</li>
	<li><a href="http://jackaldire.com/200905/linux-and-i/" title="我和Linux (2009年05月14日)">我和Linux</a> (0)</li>
	<li><a href="http://jackaldire.com/200906/archlinux-tor-config/" title="在Archlinux上的安装Tor (2009年06月8日)">在Archlinux上的安装Tor</a> (6)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/201001/write-your-own-shell-3-io-redirection/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
<enclosure url="http://storage.live.com/items/9151E070FED682BB!167?filename=11%20A%20WINTER%20STORY.mp3" length="3256268" type="audio/mpeg" />
		</item>
		<item>
		<title>自己动手写Linux Shell（二） —— 支持后台执行</title>
		<link>http://jackaldire.com/201001/write-your-own-shell-2-background-task/</link>
		<comments>http://jackaldire.com/201001/write-your-own-shell-2-background-task/#comments</comments>
		<pubDate>Sat, 30 Jan 2010 18:09:53 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[Linux与开源]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Shell]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=82</guid>
		<description><![CDATA[在写完一个最简单的命令解释器以后，我给自己的shell起了个名字——jdsh（JackalDire Shell）。
本来以为加入后台执行不是什么难事，但是认真想了一下要处理好一个字符‘＆’不是一般的麻烦，各种问题接踵而至，于是决定先找点轻松的活干。
1.加入shell内建命令

每个shell都有自己的内建命令，最常见的有“cd”、“pwd”、“exit”等等。没有“cd“的shell是没法干活的。
其实很简单，标准的做法建个hash表把所有内建命令加进去，parse command以后先查一下表就行了，然后可以通过像Linux Syscall的实现方法一样，用一个字符串连接宏（##）找到对应的处理函数。但是由于只准备写寥寥几个命令，偷懒直接 if else if了（= =!）。
2.打印命令提示符Prompt
每个命令行shell都有命令提示符，告诉用户现在可以敲命令。大多数的linux shell的prompt是可以定制的。我采用了bash的默认格式“[username@hostname cwd]$ ”。实现prompt里的cwd（current working directory）时候发现了一个问题：基本上所有linux shell里都用了&#8217;~'代替$HOME。于是做个个字符串替换，使得home文件夹在显示的时候都显示为&#8217;~'（命令里的路径还不支持&#8217;~'）
3.处理SIGINT、SIGQUIT和SIGTSTP信号
SIGINT信号对应按下ctrl+c，shell对该信号的处理方式是（观察zsh/bash得到，没有查文档）：若shell当前没有前台任务，则输入一个空行，放弃当前正在输入的命令；若当前有前台任务，则SIGINT信号由前台程序捕捉。
SIGQUIT信号对应按下ctrl+\，shell对该信号的处理方式是（观察zsh/bash得到，没有查文档）：若shell当前没有前台任务，则忽略该信号，放弃当前正在输入的命令；若当前有前台任务，则SIGQUIT信号由前台程序捕捉。
SIGQUIT信号对应按下ctrl+z，shell对该信号的处理方式是（观察zsh/bash得到，没有查文档）：若shell当前没有前台任务，则忽略该信号，放弃当前正在输入的命令；若当前有前台任务，则SIGQUIT信号由前台程序捕捉。
PS:对这APUE里的信号表看了一遍，好像没有其他要特殊处理的信号了。
其实很好处理，用signal函数或者sigaction都可以（偷懒就用signal了）。在shell的主进程里改变SIGINT的信号处理函数、忽略SIGQUIT和SIGTSPT信号就可以。
有一个问题需要注意：必须在fork的子进程里恢复对这几个信号的默认处理方式。因为fork产生子进程和父进程有相同的信号处理函数signal handler。解决方法就是fork以后在子进程里手动恢复修改过的信号。另一种方法是不用fork，改用clone系统调用，通过clone flags里的CLONE_SIGHAND参数使得复制出来的进程不保留父进程的信号处理函数。

回到主题上&#8230;..
4.后台运行程序
其实单纯的实现后台运行很简单，把前面程序里的wait去掉使shell主进程不等待子进程结束就OK了。问题在于，shell是通过一个&#8217;&#038;'字符决定后台执行，如何才能从命令里提取出这个代表后台执行的&#8217;&#038;'字符？
看起来似乎一个字符串查找就能解决，其实并非如此：如果这&#8217;&#038;'是在一个路径名里呢(Linux中只有一个字符不能做文件名：&#8217;/')。看一下别的shell很快就会找到解决方法：转义字符。如果想把有特殊含义的字符当作一个普通字符处理，那么就要在前面加上转移字符&#8217;\'（比如空格、&#8217;\'、&#8217;&#038;'、&#8217;~'、&#8217;-'等等）。
要支持转移字符，我原来用strsep实现的优美的parse command函数就得全部扔掉重写。没办法，换成了fgetc和丑陋的switch case。
有了转义字符，就可以处理含有特殊字符的文件名了^ ^
另外，我观察到bash和zsh在后台程序运行结束后都会显示一条提示信息显示pid和命令，于是我也想把这个功能加进去。思路很简单，就是改变SIGCHLD信号的handler，捕捉到SIGCHLD后输出进程信息就可以了。默认SIGCHLD信号是通过wait和waitpid函数捕捉。signal能改变SIGCHLD信号的处理函数，但是无法获得足够的信息（pid、退出代码等），而sigaction支持复杂的信号处理函数，通过向信号处理函数传递siginfo结构，提供更充足的信息（还是sigaction强大）。
差不多就这样了&#8230;代码量翻了一倍，直接贴出来有点恐怖，所以扔到附件了～（BUG很多，就不一一列举了，sign～）
源代码：jdsh_v2.c

	Related Post
	
	自己动手写Linux Shell（三） —— 支持IO重定向 (1)
	自己动手写Linux Shell（一） —— 简单的命令解释器 (2)
	我和Linux (0)
	在Archlinux上的安装Tor (6)


]]></description>
			<content:encoded><![CDATA[<p>在写完一个最简单的命令解释器以后，我给自己的shell起了个名字——jdsh（JackalDire Shell）。</p>
<p>本来以为加入后台执行不是什么难事，但是认真想了一下要处理好一个字符‘＆’不是一般的麻烦，各种问题接踵而至，于是决定先找点轻松的活干。</p>
<h2>1.加入shell内建命令</h2>
<p><span id="more-82"></span></p>
<p>每个shell都有自己的内建命令，最常见的有“cd”、“pwd”、“exit”等等。没有“cd“的shell是没法干活的。</p>
<p>其实很简单，标准的做法建个hash表把所有内建命令加进去，parse command以后先查一下表就行了，然后可以通过像Linux Syscall的实现方法一样，用一个字符串连接宏（##）找到对应的处理函数。但是由于只准备写寥寥几个命令，偷懒直接 if else if了（= =!）。</p>
<h2>2.打印命令提示符Prompt</h2>
<p>每个命令行shell都有命令提示符，告诉用户现在可以敲命令。大多数的linux shell的prompt是可以定制的。我采用了bash的默认格式“[username@hostname cwd]$ ”。实现prompt里的cwd（current working directory）时候发现了一个问题：基本上所有linux shell里都用了&#8217;~'代替$HOME。于是做个个字符串替换，使得home文件夹在显示的时候都显示为&#8217;~'（命令里的路径还不支持&#8217;~'）</p>
<h2>3.处理SIGINT、SIGQUIT和SIGTSTP信号</h2>
<p>SIGINT信号对应按下ctrl+c，shell对该信号的处理方式是（观察zsh/bash得到，没有查文档）：若shell当前没有前台任务，则输入一个空行，放弃当前正在输入的命令；若当前有前台任务，则SIGINT信号由前台程序捕捉。</p>
<p>SIGQUIT信号对应按下ctrl+\，shell对该信号的处理方式是（观察zsh/bash得到，没有查文档）：若shell当前没有前台任务，则忽略该信号，放弃当前正在输入的命令；若当前有前台任务，则SIGQUIT信号由前台程序捕捉。</p>
<p>SIGQUIT信号对应按下ctrl+z，shell对该信号的处理方式是（观察zsh/bash得到，没有查文档）：若shell当前没有前台任务，则忽略该信号，放弃当前正在输入的命令；若当前有前台任务，则SIGQUIT信号由前台程序捕捉。<br />
PS:对这APUE里的信号表看了一遍，好像没有其他要特殊处理的信号了。</p>
<p>其实很好处理，用signal函数或者sigaction都可以（偷懒就用signal了）。在shell的主进程里改变SIGINT的信号处理函数、忽略SIGQUIT和SIGTSPT信号就可以。</p>
<p>有一个问题需要注意：必须在fork的子进程里恢复对这几个信号的默认处理方式。因为fork产生子进程和父进程有相同的信号处理函数signal handler。解决方法就是fork以后在子进程里手动恢复修改过的信号。另一种方法是不用fork，改用clone系统调用，通过clone flags里的CLONE_SIGHAND参数使得复制出来的进程不保留父进程的信号处理函数。</p>
</p>
<p>回到主题上&#8230;..</p>
<h2>4.后台运行程序</h2>
<p>其实单纯的实现后台运行很简单，把前面程序里的wait去掉使shell主进程不等待子进程结束就OK了。问题在于，shell是通过一个&#8217;&#038;'字符决定后台执行，如何才能从命令里提取出这个代表后台执行的&#8217;&#038;'字符？</p>
<p>看起来似乎一个字符串查找就能解决，其实并非如此：如果这&#8217;&#038;'是在一个路径名里呢(Linux中只有一个字符不能做文件名：&#8217;/')。看一下别的shell很快就会找到解决方法：<strong>转义字符</strong>。如果想把有特殊含义的字符当作一个普通字符处理，那么就要在前面加上转移字符&#8217;\'（比如空格、&#8217;\'、&#8217;&#038;'、&#8217;~'、&#8217;-'等等）。</p>
<p>要支持转移字符，我原来用strsep实现的优美的parse command函数就得全部扔掉重写。没办法，换成了fgetc和丑陋的switch case。</p>
<p>有了转义字符，就可以处理含有特殊字符的文件名了^ ^</p>
<p>另外，我观察到bash和zsh在后台程序运行结束后都会显示一条提示信息显示pid和命令，于是我也想把这个功能加进去。思路很简单，就是改变SIGCHLD信号的handler，捕捉到SIGCHLD后输出进程信息就可以了。默认SIGCHLD信号是通过wait和waitpid函数捕捉。signal能改变SIGCHLD信号的处理函数，但是无法获得足够的信息（pid、退出代码等），而sigaction支持复杂的信号处理函数，通过向信号处理函数传递siginfo结构，提供更充足的信息（还是sigaction强大）。</p>
<p>差不多就这样了&#8230;代码量翻了一倍，直接贴出来有点恐怖，所以扔到附件了～（BUG很多，就不一一列举了，sign～）</p>
<p>源代码：<a href='http://jackaldire.com/wordpress/wp-content/uploads/2010/01/jdsh_v21.c'>jdsh_v2.c</a></p>

	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li><a href="http://jackaldire.com/201001/write-your-own-shell-3-io-redirection/" title="自己动手写Linux Shell（三） —— 支持IO重定向 (2010年01月31日)">自己动手写Linux Shell（三） —— 支持IO重定向</a> (1)</li>
	<li><a href="http://jackaldire.com/201001/write-your-own-shell-1-a-simple-command-interpreter/" title="自己动手写Linux Shell（一） —— 简单的命令解释器 (2010年01月29日)">自己动手写Linux Shell（一） —— 简单的命令解释器</a> (2)</li>
	<li><a href="http://jackaldire.com/200905/linux-and-i/" title="我和Linux (2009年05月14日)">我和Linux</a> (0)</li>
	<li><a href="http://jackaldire.com/200906/archlinux-tor-config/" title="在Archlinux上的安装Tor (2009年06月8日)">在Archlinux上的安装Tor</a> (6)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/201001/write-your-own-shell-2-background-task/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>自己动手写Linux Shell（一） —— 简单的命令解释器</title>
		<link>http://jackaldire.com/201001/write-your-own-shell-1-a-simple-command-interpreter/</link>
		<comments>http://jackaldire.com/201001/write-your-own-shell-1-a-simple-command-interpreter/#comments</comments>
		<pubDate>Fri, 29 Jan 2010 10:32:37 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[Linux与开源]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Shell]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=80</guid>
		<description><![CDATA[ 晓之车 ~piano version~ &#8212; 梶浦由记 

寒假在做Linux Kernel Project这本书上的习题，第二章的练习是写一个简单的shell，看了一下要求觉得这个练习很有价值，涉及到很多Linux C Programming的知识，所以准备认真地做一下。
最终的目标如下:
命令解释执行
支持后台执行(&#038;)
支持输入输出重定向(, >>)
支持管道IPC
内建命令cd, pwd, exit等

可见写一个shell并不是一件简单的事，从简单的一步一步做起吧，手头有APUE，一边做一边查。

一个简单的命令解释器
命令解释执行是shell最基本的功能，实现的方法很简单：从标准输入流中读入命令，然后exec一下就行了。但是还有很多琐碎的地方需要处理：
1.命令行参数传递
首先需要将输入的命令字符串按空格打断（strsep实在是太方便了），然后将打断的字符串构建成一个char*数组，通过execv的第二个参数传递给程序。
注：man exec可以得到关于exec函数族的详细说明。需要说明的是execlp和execvp会在PATH环境变量中的目录搜索可执行程序，而其他的exec函数族函数不会，如果不使用这两个函数，则需要自己编写代码搜索PATH环境变量。
2.使用fork建立子进程
直接在当前进程里exec的话，exec执行的程序结束后，整个程序也就结束了，因为exec直接将原来的进程上下文替换。所以需要fork一个新进程来执行命令，而父进程阻塞直到子进程结束后继续执行，这个可以通过wait函数实现。
3.处理命令的返回值
大多数的shell在命令程序返回非零值（异常退出）会打印出其返回值。而子进程的返回值可以在父进程里通过wait函数的第一个参数得到。然后通过一组宏可以方便地确定子进程的返回状态。这部分内容在APUE里有详细说明（8.6节），下面代码里的pr_exit函数基本上就是从APUE上抄过来的。
4.检查各个函数的返回值
Linux C Programming的一个原则就是在所有可能fail的地方加入检查代码。绝大多数C库函数和Linux系统函数都以负数返回值表示出错，并且通过C库的全局变量errno可以获得错误号，从而得到错误原因，并输出到标准错误流。由于整个过程动作固定，就用一个CHKERR宏来完成了。
下面是源代码
CODE BELOW ARE UNDER GPLV3 LISENCE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/* By JackalDire, Jan 29 2010 
 * Tested on Linux Kernel 2.6.32, gcc 4.4.3 */
#include &#60;unistd.h&#62;
#include &#60;sys/wait.h&#62;
&#160;
#include &#60;stdio.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;string.h&#62;
#include &#60;errno.h&#62;
&#160;
#define LINE_MAX 8192
#define ARG_MAX 1024
#define ARG_NR_MAX 32
&#160;
#define CHKERR(ret, msg) if (ret &#60; 0) [...]]]></description>
			<content:encoded><![CDATA[<div> 晓之车 ~piano version~ &#8212; 梶浦由记 </div>
<p></p>
<p>寒假在做Linux Kernel Project这本书上的习题，第二章的练习是写一个简单的shell，看了一下要求觉得这个练习很有价值，涉及到很多Linux C Programming的知识，所以准备认真地做一下。</p>
<ol><strong>最终的目标如下:</strong></p>
<li>命令解释执行</li>
<li>支持后台执行(&#038;)</li>
<li>支持输入输出重定向(<, >, >>)</li>
<li>支持管道IPC</li>
<li>内建命令cd, pwd, exit等</li>
</ol>
<p>可见写一个shell并不是一件简单的事，从简单的一步一步做起吧，手头有APUE，一边做一边查。</p>
<p><span id="more-80"></span></p>
<h2>一个简单的命令解释器</h2>
<p>命令解释执行是shell最基本的功能，实现的方法很简单：从标准输入流中读入命令，然后exec一下就行了。但是还有很多琐碎的地方需要处理：</p>
<h3>1.命令行参数传递</h3>
<p>首先需要将输入的命令字符串按空格打断（strsep实在是太方便了），然后将打断的字符串构建成一个char*数组，通过execv的第二个参数传递给程序。</p>
<p>注：man exec可以得到关于exec函数族的详细说明。需要说明的是execlp和execvp会在PATH环境变量中的目录搜索可执行程序，而其他的exec函数族函数不会，如果不使用这两个函数，则需要自己编写代码搜索PATH环境变量。</p>
<h3>2.使用fork建立子进程</h3>
<p>直接在当前进程里exec的话，exec执行的程序结束后，整个程序也就结束了，因为exec直接将原来的进程上下文替换。所以需要fork一个新进程来执行命令，而父进程阻塞直到子进程结束后继续执行，这个可以通过wait函数实现。</p>
<h3>3.处理命令的返回值</h3>
<p>大多数的shell在命令程序返回非零值（异常退出）会打印出其返回值。而子进程的返回值可以在父进程里通过wait函数的第一个参数得到。然后通过一组宏可以方便地确定子进程的返回状态。这部分内容在APUE里有详细说明（8.6节），下面代码里的pr_exit函数基本上就是从APUE上抄过来的。</p>
<h3>4.检查各个函数的返回值</h3>
<p>Linux C Programming的一个原则就是在所有可能fail的地方加入检查代码。绝大多数C库函数和Linux系统函数都以负数返回值表示出错，并且通过C库的全局变量errno可以获得错误号，从而得到错误原因，并输出到标准错误流。由于整个过程动作固定，就用一个CHKERR宏来完成了。
<p>下面是源代码</p>
<p><strong>CODE BELOW ARE UNDER <a href="http://www.gnu.org/licenses/gpl-3.0-standalone.html">GPLV3 LISENCE</a></strong></p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* By JackalDire, Jan 29 2010 
 * Tested on Linux Kernel 2.6.32, gcc 4.4.3 */</span>
<span style="color: #339933;">#include &lt;unistd.h&gt;</span>
<span style="color: #339933;">#include &lt;sys/wait.h&gt;</span>
&nbsp;
<span style="color: #339933;">#include &lt;stdio.h&gt;</span>
<span style="color: #339933;">#include &lt;stdlib.h&gt;</span>
<span style="color: #339933;">#include &lt;string.h&gt;</span>
<span style="color: #339933;">#include &lt;errno.h&gt;</span>
&nbsp;
<span style="color: #339933;">#define LINE_MAX 8192</span>
<span style="color: #339933;">#define ARG_MAX 1024</span>
<span style="color: #339933;">#define ARG_NR_MAX 32</span>
&nbsp;
<span style="color: #339933;">#define CHKERR(ret, msg) if (ret &lt; 0) {\
        fprintf(stderr, &quot;ERROR : \&quot;%s\&quot;, %s\n&quot;, \
                msg, strerror(errno)); \
        exit(-1);   \
    } </span>
&nbsp;
<span style="color: #993333;">char</span> <span style="color: #339933;">*</span> args<span style="color: #009900;">&#91;</span>ARG_NR_MAX <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">extern</span> <span style="color: #993333;">char</span> <span style="color: #339933;">**</span> environ<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">char</span> line<span style="color: #009900;">&#91;</span>LINE_MAX <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">void</span> parse_command<span style="color: #009900;">&#40;</span><span style="color: #993333;">char</span> <span style="color: #339933;">*</span> cmd<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">char</span> <span style="color: #339933;">*</span> res<span style="color: #339933;">;</span>
    size_t cnt <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    <span style="color: #808080; font-style: italic;">/* tokenize the command string by space */</span>
    <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>res <span style="color: #339933;">=</span> strsep<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>cmd<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot; &quot;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000066;">printf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;%s<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span> res<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        args<span style="color: #009900;">&#91;</span>cnt<span style="color: #339933;">++</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> strdup<span style="color: #009900;">&#40;</span>res<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    args<span style="color: #009900;">&#91;</span>cnt<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #993333;">void</span> pr_exit<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span> name<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> status<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>WIFEXITED<span style="color: #009900;">&#40;</span>status<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #666666; font-style: italic;">// exit normally</span>
        <span style="color: #b1b100;">return</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">else</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>WIFSIGNALED<span style="color: #009900;">&#40;</span>status<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
        fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;%s exit abnormally, signal %d caught%s.<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span>
                name<span style="color: #339933;">,</span> WTERMSIG<span style="color: #009900;">&#40;</span>status<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
<span style="color: #339933;">#ifdef WCOREDUMP</span>
            WCOREDUMP<span style="color: #009900;">&#40;</span>status<span style="color: #009900;">&#41;</span> <span style="color: #339933;">?</span> <span style="color: #ff0000;">&quot; (core file generated)&quot;</span> <span style="color: #339933;">:</span> <span style="color: #ff0000;">&quot;&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #339933;">#else</span>
            <span style="color: #ff0000;">&quot;&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #339933;">#endif</span>
    <span style="color: #b1b100;">else</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>WIFSTOPPED<span style="color: #009900;">&#40;</span>status<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
        fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;child stopped, signal %d caught.&quot;</span><span style="color: #339933;">,</span>
                WSTOPSIG<span style="color: #009900;">&#40;</span>status<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> argc<span style="color: #339933;">,</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span> argv<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">char</span> c<span style="color: #339933;">;</span>
    size_t idx<span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> r<span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> status<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        idx <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
        bzero<span style="color: #009900;">&#40;</span>line<span style="color: #339933;">,</span> LINE_MAX <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        c <span style="color: #339933;">=</span> fgetc<span style="color: #009900;">&#40;</span>stdin<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span>c <span style="color: #339933;">&amp;&amp;</span> c <span style="color: #339933;">!=</span> <span style="color: #ff0000;">'<span style="color: #000099; font-weight: bold;">\n</span>'</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            line<span style="color: #009900;">&#91;</span>idx<span style="color: #339933;">++</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> c<span style="color: #339933;">;</span>
            c <span style="color: #339933;">=</span> fgetc<span style="color: #009900;">&#40;</span>stdin<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        parse_command<span style="color: #009900;">&#40;</span>line<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        r <span style="color: #339933;">=</span> fork<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> 
        CHKERR<span style="color: #009900;">&#40;</span>r<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;fork&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>r <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
            r <span style="color: #339933;">=</span> execvp<span style="color: #009900;">&#40;</span>args<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> args<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #666666; font-style: italic;">//printf(&quot;ret : %d\n&quot;, r);</span>
            CHKERR<span style="color: #009900;">&#40;</span>r<span style="color: #339933;">,</span> args<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
            wait<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>status<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            pr_exit<span style="color: #009900;">&#40;</span>args<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> status<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>一个简单的命令解释器就这样完成了，下面的工作就是添加后台执行功能，休息一会^ ^</p>

	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li><a href="http://jackaldire.com/201001/write-your-own-shell-2-background-task/" title="自己动手写Linux Shell（二） —— 支持后台执行 (2010年01月31日)">自己动手写Linux Shell（二） —— 支持后台执行</a> (2)</li>
	<li><a href="http://jackaldire.com/201001/write-your-own-shell-3-io-redirection/" title="自己动手写Linux Shell（三） —— 支持IO重定向 (2010年01月31日)">自己动手写Linux Shell（三） —— 支持IO重定向</a> (1)</li>
	<li><a href="http://jackaldire.com/200905/linux-and-i/" title="我和Linux (2009年05月14日)">我和Linux</a> (0)</li>
	<li><a href="http://jackaldire.com/200906/archlinux-tor-config/" title="在Archlinux上的安装Tor (2009年06月8日)">在Archlinux上的安装Tor</a> (6)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/201001/write-your-own-shell-1-a-simple-command-interpreter/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
<enclosure url="http://www.86comic.com/music/song/2007/9/14/86comic0914131614181.mp3" length="293170" type="audio/mpeg" />
		</item>
		<item>
		<title>[消遣]Love Functions</title>
		<link>http://jackaldire.com/201001/love-function/</link>
		<comments>http://jackaldire.com/201001/love-function/#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:51:46 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[未分类]]></category>
		<category><![CDATA[fun]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=79</guid>
		<description><![CDATA[Version 1 (ZZ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
result love&#40;boy, girl&#41; 
&#123; 
    if &#40; boy.有房&#40;&#41; and boy.有车&#40;&#41; &#41; &#123; 
        boy.set&#40;nothing&#41;; 
        return girl.嫁给&#40;boy&#41;; 
    &#125; else if &#40;girl.愿意等&#40;&#41;&#41; &#123; 
        while&#40; ! [...]]]></description>
			<content:encoded><![CDATA[<h2>Version 1 (ZZ)</h2>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;">result love<span style="color: #008000;">&#40;</span>boy, girl<span style="color: #008000;">&#41;</span> 
<span style="color: #008000;">&#123;</span> 
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span> boy.有房<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> and boy.有车<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
        boy.<span style="color: #007788;">set</span><span style="color: #008000;">&#40;</span>nothing<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
        <span style="color: #0000ff;">return</span> girl.嫁给<span style="color: #008000;">&#40;</span>boy<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
    <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>girl.愿意等<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
        <span style="color: #0000ff;">while</span><span style="color: #008000;">&#40;</span> <span style="color: #000040;">!</span> <span style="color: #008000;">&#40;</span>boy.赚钱 ＞ <span style="color:#800080;">1e6</span> and girl.感情 ＞ <span style="color: #0000dd;">8</span> <span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
            <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span>day<span style="color: #000080;">=</span><span style="color: #0000dd;">1</span><span style="color: #008080;">;</span> day ＜<span style="color: #000080;">=</span><span style="color: #0000dd;">365</span><span style="color: #008080;">;</span> day<span style="color: #000040;">++</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
                <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span> day <span style="color: #000080;">==</span> 情人节 <span style="color: #008000;">&#41;</span> 
                    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span> boy.<span style="color: #007788;">givegirl</span><span style="color: #008000;">&#40;</span>玫瑰<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#41;</span> girl.感情<span style="color: #000040;">++</span><span style="color: #008080;">;</span> 
                    <span style="color: #0000ff;">else</span> girl.感情<span style="color: #000040;">--</span><span style="color: #008080;">;</span> 
                <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span> day <span style="color: #000080;">==</span> girl.生日<span style="color: #008000;">&#41;</span> 
                    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span> boy.<span style="color: #007788;">givegirl</span><span style="color: #008000;">&#40;</span>玫瑰<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#41;</span> 
                        girl.感情<span style="color: #000040;">++</span><span style="color: #008080;">;</span> 
                <span style="color: #0000ff;">else</span> girl.感情<span style="color: #000040;">--</span><span style="color: #008080;">;</span> 
                boy.拼命赚钱<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
                <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span> girl.耐心 <span style="color: #000080;">==</span> <span style="color: #0000dd;">0</span> <span style="color: #000040;">&amp;&amp;</span> girl.有其它追求者<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
                    girl.<span style="color: #0000ff;">goto</span><span style="color: #008000;">&#40;</span>nother_boy<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
                    boy.郁闷中<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
                    boy.天天到BYR灌水<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
                    <span style="color: #0000ff;">return</span> 没有结果<span style="color: #008080;">;</span> 
                <span style="color: #008000;">&#125;</span> 
            <span style="color: #008000;">&#125;</span> 
        <span style="color: #008000;">&#125;</span>
        <span style="color: #0000ff;">try</span> 
        <span style="color: #008000;">&#123;</span> 
            girl.要男友买房<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
            girl.要男友买车<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
            girl.每天逛专卖店<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
            <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span> boy.有房<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">&amp;&amp;</span> boy.有车<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
                girl.去澳洲旅游<span style="color: #008000;">&#40;</span>boy<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
                girl.到英国威斯敏斯结婚<span style="color: #008000;">&#40;</span>boy<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
                girl.嫁给<span style="color: #008000;">&#40;</span>boy<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
                boy.没日没夜挣钱<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
            <span style="color: #008000;">&#125;</span> 
        <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">catch</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
            girl.<span style="color: #007788;">byebye</span><span style="color: #008000;">&#40;</span>boy<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
            boy.郁闷中<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
            boy.天天到天涯灌水<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
            <span style="color: #0000ff;">return</span> girl.<span style="color: #007788;">broadcast</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;这个男人真小气&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
        <span style="color: #008000;">&#125;</span> 
        <span style="color: #0000ff;">return</span> girl.每天逛专卖店<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
    <span style="color: #008000;">&#125;</span> 
    <span style="color: #0000ff;">return</span> girl.<span style="color: #0000ff;">goto</span><span style="color: #008000;">&#40;</span>another_boy<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p><span id="more-79"></span></p>
<h2>Version 2 (By wks)</h2>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">void</span> love<span style="color: #008000;">&#40;</span>Man <span style="color: #000040;">&amp;</span>m, Woman <span style="color: #000040;">&amp;</span>w<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
     <span style="color: #0000ff;">try</span> <span style="color: #008000;">&#123;</span> 
         <span style="color: #0000ff;">while</span><span style="color: #008000;">&#40;</span>w.<span style="color: #007788;">isFuckable</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
         doSomething<span style="color: #008000;">&#40;</span>things<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">rand</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #000040;">%</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">sizeof</span><span style="color: #008000;">&#40;</span>things<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #000040;">/</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">sizeof</span><span style="color: #008000;">&#40;</span>things<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
         wait<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
        <span style="color: #008000;">&#125;</span> 
    <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">catch</span> <span style="color: #008000;">&#40;</span>CanWaitNoLongerException e<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
    <span style="color: #008000;">&#125;</span> 
    m.<span style="color: #007788;">fuck</span><span style="color: #008000;">&#40;</span>w<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<h2>Version 3 (By me)</h2>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">void</span> Man<span style="color: #008080;">::</span><span style="color: #007788;">love</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
    <span style="color: #0000ff;">try</span> <span style="color: #008000;">&#123;</span> 
        <span style="color: #0000ff;">while</span><span style="color: #008000;">&#40;</span>m.<span style="color: #007788;">alive</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">&amp;&amp;</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>m.<span style="color: #007788;">hasHouse</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">||</span> <span style="color: #000040;">!</span>m.<span style="color: #007788;">hasCar</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
            m.<span style="color: #007788;">makeMoney</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>  <span style="color: #666666;">// good luck if this is not a infinite loop... </span>
        <span style="color: #008000;">&#125;</span> 
        Woman w <span style="color: #000040;">*</span><span style="color: #008080;">;</span> 
        <span style="color: #0000ff;">while</span><span style="color: #008000;">&#40;</span>w <span style="color: #000080;">=</span> findMM<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>  <span style="color: #666666;">// good luck if this do not block forever... </span>
        <span style="color: #008000;">&#123;</span> 
            <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>w<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>isLove<span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span><span style="color: #0000dd;">this</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">&amp;&amp;</span> w<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>getParents<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>.<span style="color: #007788;">isLike</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span><span style="color: #0000dd;">this</span><span style="color: #008000;">&#41;</span> 
                <span style="color: #000040;">&amp;&amp;</span> w<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>getParents<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>.<span style="color: #007788;">getParents</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>.<span style="color: #007788;">isLike</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span><span style="color: #0000dd;">this</span><span style="color: #008000;">&#41;</span> 
                <span style="color: #000040;">&amp;&amp;</span> ...<span style="color: #008000;">&#41;</span>    
                marry<span style="color: #008000;">&#40;</span>w<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
            <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">continue</span><span style="color: #008080;">;</span> 
        <span style="color: #008000;">&#125;</span> 
    <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">catch</span> <span style="color: #008000;">&#40;</span>房子又涨价了Exception  <span style="color: #000040;">&amp;</span> e<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
        letItBe<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
    <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">catch</span> <span style="color: #008000;">&#40;</span>遇到宋思明了Exception  <span style="color: #000040;">&amp;</span> e<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>  
        letItBe<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
    <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">catch</span> <span style="color: #008000;">&#40;</span>被炒鱿鱼了Exception <span style="color: #000040;">&amp;</span> e<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
        letItBe<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
    <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">catch</span> <span style="color: #008000;">&#40;</span>家命难违Exception <span style="color: #000040;">&amp;</span> e<span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> 
        letItBe<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
    ...... 
    <span style="color: #008000;">&#125;</span> finally <span style="color: #008000;">&#123;</span> 
        mayGodSaveMe<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
    <span style="color: #008000;">&#125;</span> 
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>


	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li>No Related Post</li>
	</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/201001/love-function/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>[C++]一个由C-Style类型转换引发的血案</title>
		<link>http://jackaldire.com/201001/c-style-cast-issues/</link>
		<comments>http://jackaldire.com/201001/c-style-cast-issues/#comments</comments>
		<pubDate>Mon, 11 Jan 2010 13:13:21 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C++]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=78</guid>
		<description><![CDATA[没事搜了一下自己域名，无意间发现老早提交的open directory申请居然通过了&#8230;想到一个多月没写东西，大惭&#8230;
为了尽快阻止这个连续N天没有日志的记录，先找一篇凑数&#8230;
先上开胃小菜 Appetizer
有人在byr论坛C++版上问了这样一道C++面试题：

class A &#123;
    public: void fun&#40;&#41;    &#123;    &#125;
&#125;;
&#160;
class B: public A &#123;
    public: virtual void fun&#40;&#41; &#123;    &#125;
&#125;;
&#160;
class C: public B &#123;
    public: void fun&#40;&#41;&#123;&#125;
&#125;;
&#160;
class D: virtual public A &#123;
public:
    void fun&#40;&#41;&#123;&#125;
&#125;;
&#160;
int [...]]]></description>
			<content:encoded><![CDATA[<p>没事搜了一下自己域名，无意间发现老早提交的<a href="http://www.dmoz.org/World/Chinese_Simplified/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E4%BA%92%E8%81%94%E7%BD%91%E7%BB%9C/%E7%BD%91%E7%BB%9C%E8%B5%84%E6%BA%90/%E5%8D%9A%E5%AE%A2/%E6%8A%80%E6%9C%AF/">open directory</a>申请居然通过了&#8230;想到一个多月没写东西，大惭&#8230;</p>
<p>为了尽快阻止这个连续N天没有日志的记录，先找一篇凑数&#8230;</p>
<h2>先上开胃小菜 Appetizer</h2>
<p>有人在<a href="http://forum.byr.edu.cn/wForum/disparticle.php?boardName=CPP&#038;ID=34447&#038;pos=49&#038;page=1">byr论坛C++版</a>上问了这样一道C++面试题：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">class</span> A <span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">public</span><span style="color: #008080;">:</span> <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>    <span style="color: #008000;">&#123;</span>    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">class</span> B<span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> A <span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">public</span><span style="color: #008080;">:</span> <span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">class</span> C<span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> B <span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">public</span><span style="color: #008080;">:</span> <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span><span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">class</span> D<span style="color: #008080;">:</span> <span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">public</span> A <span style="color: #008000;">&#123;</span>
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>
    <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span><span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #008000;">&#41;</span>  <span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">void</span> <span style="color: #000040;">*</span>p<span style="color: #008080;">;</span>
    <span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span>A<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #0000ff;">NULL</span><span style="color: #008000;">&#41;</span><span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span>C<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #0000ff;">NULL</span><span style="color: #008000;">&#41;</span><span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <span style="color: #666666;">// why fault here?</span>
    <span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span>D<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #0000ff;">NULL</span><span style="color: #008000;">&#41;</span><span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>为什么在((C*)NULL)->fun()这句挂了呢？</p>
<p><span id="more-78"></span><br />
原因很简单：B的fun是虚函数, 而C继承B, 所以通过C对象指针调用fun的时候触发基类virtual方法的多态特性, 需要查通过vptr查询虚表, 但是对NULL强制类型转换并不会设置vptr（vptr的设置是在构造函数中完成的），编译器就把地址0（NULL）中的内容当vptr，一解引用就segment fault了。</p>
<p>其实一开始看((A*)NULL)->fun();这句也觉得别扭，没有构造对象就直接调用非static的成员方法，居然还没有运行错误。想了一想，实际上在编译期，编译器都会通过一个<a href="http://en.wikipedia.org/wiki/Name_mangling">name mangling机制</a>将类的成员函数转换成一个具有唯一名字的非成员函数。程序开始运行时，成员函数和非成员函数一样都被载入到内存。所以只要在调用的成员函数不用到需要由类构造函数构造的成分（比如成员变量和vptr），这种通过指针调用成员函数就不会出错。</p>
<p>其实这个问题并不复杂，但是jmpesp老兄在回帖中写了一道更恶心的题目。</p>
<h2>主菜来了 Main Dishes</h2>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">class</span> A <span style="color: #008000;">&#123;</span>  
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>  
    <span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">float</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;A&quot;</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span>  
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>  
&nbsp;
<span style="color: #0000ff;">class</span> B <span style="color: #008000;">&#123;</span>  
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>  
    <span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;B&quot;</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span>  
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>  
&nbsp;
<span style="color: #0000ff;">class</span> C<span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> B, <span style="color: #0000ff;">public</span> A <span style="color: #008000;">&#123;</span>  
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>  
    <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">float</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;C float&quot;</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span><span style="color: #008000;">&#41;</span>   <span style="color: #008000;">&#123;</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;C int&quot;</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span> 
&nbsp;
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>       
    C<span style="color: #000040;">*</span> p <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> C<span style="color: #008080;">;</span>
    <span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span>B<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#40;</span>A<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span>p<span style="color: #008000;">&#41;</span><span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>fun<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000dd;">delete</span> p<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>问该段代码的输出是什么？</p>
<p>自己编译运行了一下输出是：C float&#8230;</p>
<p>首先输出C是确定的，因为无论怎么转型， p都是指向一个C对象，而A、B中的fun都是虚函数，所以调用的肯定是C类方法。输出float看起来比较诡异，原因肯定在这个(B*)(A*)c-style转型上。</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;">     <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #008000;">&#40;</span>A<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span>p <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
     <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #008000;">&#40;</span>B<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span>p <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
     <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #008000;">&#40;</span>B<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#40;</span>A<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span>p <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span></pre></div></div>

<p>上面的代码得到以下输出：<br />
00382E30<br />
00382E34<br />
00382E30</p>
<p>可见(B*)(A*)p和(A*)p是相同的。</p>
<p>分析如下：</p>
<p>C继承了A和B，所以一个C对象里包含了一个A对象和B对象。由上面的输出知道(A*)p使得p偏移到了C对象中的A对象，是个static_cast。 接下来(B*)再对指向A对象的p进行转型，这时候问题就出现了，B对象并不包含A对象，编译器也不知道p指向的A对象实际上是包含在一个C对象里，所以 (B*)这次转换，只是改变了p的静态类型，并没有改变p指向的位置，是个reinterpret_cast也就是说p还是指向一个A对象。所以通过p调用fun(1)的时候，查的是A的虚表，先查到virtual void A::fun(float)，再到void C::fun(float)</p>
<p>这个问题的关键就在(B*)(A*)p这两个C-Style的函数式转型(functional cast)上:<br />
(A*)p是从派生类C到基类A的转换，有可能是个向从派生类到基类的static_cast，也有可能是个简单的reinterpret_cast, 从运行结果上看(A*)是个static_cast，而由(A*)到(B*)的转换不可能是static_cast（static_cast (static_cast(p)))是编译不过的），所以只能是reinterpret_cast</p>
<p>这里又出现了一个问题，为什么(A*)p是个static_cast而不是reinterpret_cast？至少这与我的直觉不服，在ISO C++标准5.4节找到如下定义(见图):</p>
<p>也就是说一个C-style的强制类型转换如果可以解释成多个列表里的C++ style转型，取在列表里位置最前面的一个。static_cast在reinterpret_cast前，所以得到了下面的结果：</p>
<p style="text-align: center;"><strong>(B*)(A*)p 等价于 reinterpret_cast(static_cast(p))</strong></p>
<h2><strong>由这道题目说明（Conclusion）：</strong></h2>
<ol>
<li><strong>千万不要用C-style对对象指针进行转型，请用C++-style的static_cast、reinterpret_cast和dynamic_cast，以免造成意料之外的错误。</strong></li>
<li><strong>C++标准是个好东西，它胜过任何技术手册，一旦对语言特性有困惑，查标准是最好的解决方法。</strong></li>
</ol>

	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li><a href="http://jackaldire.com/200906/cost-of-class-inherit/" title="类继承的开销 ——《Inside the C++ Object Model》笔记二 (2009年06月20日)">类继承的开销 ——《Inside the C++ Object Model》笔记二</a> (0)</li>
	<li><a href="http://jackaldire.com/200905/note-constructor/" title="构造函数 ——《Inside the C++ Object Model》笔记一 (2009年05月15日)">构造函数 ——《Inside the C++ Object Model》笔记一</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/201001/c-style-cast-issues/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>[一道面试题]含有*的字符串匹配问题</title>
		<link>http://jackaldire.com/200911/string-matching-with-wildcard/</link>
		<comments>http://jackaldire.com/200911/string-matching-with-wildcard/#comments</comments>
		<pubDate>Wed, 25 Nov 2009 01:13:43 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[面试题]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=76</guid>
		<description><![CDATA[Question
字符串1：只含有英文字母
字符串2：含有英文字母和*，其中符号*表示匹配任意字符0或者多次，即正则表达式里面的含义。
现在给定这样的两个串，要求判断是否匹配？
bool isMatch ( const char *str1, const char *str2)
例如：str1 = &#8220;hello&#8221;, str2 = &#8220;he*o&#8221;，则二者匹配，返回true,str1 = &#8220;hello&#8221;, str2 = &#8220;he*l&#8221;，则不匹配，返回false。

Solution
关键是如何处理*，首先想到的就是回溯，在纸上画了一下得到如下算法
设输入是两个字符串 s, t, 其中t可能包含* 
1.当*t不是*的时候, 就像普通的串匹配一样, 移动s和t 
2.当*t是*的时候,  假设*t后面第一个不是*的字符是x,  若x是null, 直接匹配成功,  否则在s中找到当前位置后所有字符x的位置, 这时候问题转化成了t中x后的串和s中当前位置以后所有以x为开始的串的匹配问题, 递归调用即可, 其中任意一个匹配成功, 则原串匹配成功, 若都没有匹配成功则匹配失败.
3.当*s和*t其中一个是null时 跳出循环, 若此时 *t  == &#8216;*&#8217;, 则++t 知道 t != &#8216;*&#8217;,  这时若 *t == 0 则代表匹配成功, [...]]]></description>
			<content:encoded><![CDATA[<h2>Question</h2>
<p>字符串1：只含有英文字母<br />
字符串2：含有英文字母和*，其中符号*表示匹配任意字符0或者多次，即正则表达式里面的含义。</p>
<p>现在给定这样的两个串，要求判断是否匹配？<br />
bool isMatch ( const char *str1, const char *str2)</p>
<p>例如：str1 = &#8220;hello&#8221;, str2 = &#8220;he*o&#8221;，则二者匹配，返回true,str1 = &#8220;hello&#8221;, str2 = &#8220;he*l&#8221;，则不匹配，返回false。</p>
<p><span id="more-76"></span></p>
<h2>Solution</h2>
<p>关键是如何处理*，首先想到的就是回溯，在纸上画了一下得到如下算法</p>
<p>设输入是两个字符串 s, t, 其中t可能包含* <.p><br />
1.当*t不是*的时候, 就像普通的串匹配一样, 移动s和t <br />
2.当*t是*的时候,  假设*t后面第一个不是*的字符是x,  若x是null, 直接匹配成功,  否则在s中找到当前位置后所有字符x的位置, 这时候问题转化成了t中x后的串和s中当前位置以后所有以x为开始的串的匹配问题, 递归调用即可, 其中任意一个匹配成功, 则原串匹配成功, 若都没有匹配成功则匹配失败.</p>
<p>3.当*s和*t其中一个是null时 跳出循环, 若此时 *t  == &#8216;*&#8217;, 则++t 知道 t != &#8216;*&#8217;,  这时若 *t == 0 则代表匹配成功, 否则匹配失败。</p>
<p>代码如下:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#include &lt;cstring&gt;</span>
<span style="color: #339900;">#include &lt;iostream&gt;</span>
&nbsp;
<span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span> std<span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">bool</span> is_match<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> s, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> t<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>s <span style="color: #000040;">&amp;&amp;</span> <span style="color: #000040;">*</span>t<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000040;">!</span><span style="color: #000080;">=</span> <span style="color: #FF0000;">'*'</span> <span style="color: #000040;">&amp;&amp;</span> <span style="color: #000040;">*</span>s <span style="color: #000080;">==</span> <span style="color: #000040;">*</span>t<span style="color: #008000;">&#41;</span>
            <span style="color: #000040;">++</span>s, <span style="color: #000040;">++</span>t<span style="color: #008080;">;</span>
        <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000040;">!</span><span style="color: #000080;">=</span> <span style="color: #FF0000;">'*'</span> <span style="color: #000040;">&amp;&amp;</span> <span style="color: #000040;">*</span>s <span style="color: #000040;">!</span><span style="color: #000080;">=</span> <span style="color: #000040;">*</span>t<span style="color: #008000;">&#41;</span>
            <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000080;">==</span> <span style="color: #FF0000;">'*'</span><span style="color: #008000;">&#41;</span>
        <span style="color: #008000;">&#123;</span>
            <span style="color: #0000ff;">do</span> <span style="color: #000040;">++</span>t<span style="color: #008080;">;</span> <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000080;">==</span> <span style="color: #FF0000;">'*'</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
            <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000080;">==</span> <span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
            <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span> <span style="color: #008080;">;</span> <span style="color: #000040;">*</span>s<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>s<span style="color: #008000;">&#41;</span>
                <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>s <span style="color: #000080;">==</span> <span style="color: #000040;">*</span>t <span style="color: #000040;">&amp;&amp;</span> is_match<span style="color: #008000;">&#40;</span>s<span style="color: #000040;">+</span><span style="color: #0000dd;">1</span>, t<span style="color: #000040;">+</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
                    <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
            <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span>
        <span style="color: #008000;">&#125;</span>
    <span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000080;">==</span> <span style="color: #FF0000;">'*'</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">++</span>t<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>s <span style="color: #000080;">==</span> <span style="color: #000040;">*</span>t<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> argc, <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> argv<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>is_match<span style="color: #008000;">&#40;</span>argv<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#93;</span>, argv<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">2</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
        <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;match&quot;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">else</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;not match&quot;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<h2>改进</h2>
<p>上面的暴力算法如果遇到“aaaaaaaaaaaaaaaa”，“a*a*a*a*a*a*a*a*a*a*a*a”这样的输入，会达到2^n的复杂度，是无法接受的。注意到，递归搜索是有很多重复的状态，自然就想到了记忆化搜索，时间空间复杂度均为O(n^2)，代码如下：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#include &lt;iostream&gt;</span>
<span style="color: #339900;">#include &lt;cstring&gt;</span>
<span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span> std<span style="color: #008080;">;</span>
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAX_LEN <span style="color: #000080;">=</span> <span style="color: #0000dd;">1024</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">int</span> dp<span style="color: #008000;">&#91;</span>MAX_LEN<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#91;</span>MAX_LEN<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">bool</span> is_match<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> s, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> t<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>dp<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&gt;=</span> <span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">return</span> dp<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
    <span style="color: #0000ff;">int</span> <span style="color: #000040;">&amp;</span>ans <span style="color: #000080;">=</span> dp<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
&nbsp;
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>s<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span> <span style="color: #000040;">&amp;&amp;</span> t<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
        ans <span style="color: #000080;">=</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>s<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span> <span style="color: #000040;">||</span> <span style="color: #000040;">!</span>t<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>t<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">==</span> <span style="color: #FF0000;">'*'</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
            ans <span style="color: #000080;">=</span> is_match<span style="color: #008000;">&#40;</span>p, q <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span>, s, t<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #008000;">&#125;</span>
    <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">else</span> <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>t<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">==</span> <span style="color: #FF0000;">'*'</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
            ans <span style="color: #000080;">=</span> is_match<span style="color: #008000;">&#40;</span>p <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span>, q, s, t<span style="color: #008000;">&#41;</span>
                <span style="color: #000040;">||</span> is_match<span style="color: #008000;">&#40;</span>p, q <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span>, s, t<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>s<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span> <span style="color: #000080;">==</span> t<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
            ans <span style="color: #000080;">=</span> is_match<span style="color: #008000;">&#40;</span>p <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span>, q <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span>, s, t<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #008000;">&#125;</span>
    <span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">return</span> ans<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">bool</span> is_match<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> s, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> t<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000dd;">memset</span><span style="color: #008000;">&#40;</span>dp, <span style="color: #000040;">-</span><span style="color: #0000dd;">1</span>, <span style="color: #0000dd;">sizeof</span><span style="color: #008000;">&#40;</span>dp<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> is_match<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">0</span>, s, t<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>  
&nbsp;
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> argc, <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> argv<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>is_match<span style="color: #008000;">&#40;</span>argv<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#93;</span>, argv<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">2</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
        <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;match&quot;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">else</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;not match&quot;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>


	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li><a href="http://jackaldire.com/201003/youdao-intern-interview-summary/" title="有道实习生面试总结 (2010年03月9日)">有道实习生面试总结</a> (0)</li>
	<li><a href="http://jackaldire.com/201003/youdao-written-exam-summary/" title="有道实习生笔试总结 (2010年03月6日)">有道实习生笔试总结</a> (6)</li>
	<li><a href="http://jackaldire.com/200908/quick-sort-analysis/" title="快速排序详细分析 (2009年08月27日)">快速排序详细分析</a> (2)</li>
	<li><a href="http://jackaldire.com/200908/reverse-token/" title="[备忘]倒置字符串中的单词 (2009年08月18日)">[备忘]倒置字符串中的单词</a> (0)</li>
	<li><a href="http://jackaldire.com/200905/smart-way-to-solve-fibonacci/" title="Fibonacci数的巧妙求法 (2009年05月6日)">Fibonacci数的巧妙求法</a> (2)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/200911/string-matching-with-wildcard/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>快速排序详细分析</title>
		<link>http://jackaldire.com/200908/quick-sort-analysis/</link>
		<comments>http://jackaldire.com/200908/quick-sort-analysis/#comments</comments>
		<pubDate>Thu, 27 Aug 2009 09:15:37 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[算法]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=73</guid>
		<description><![CDATA[快速排序详细分析
注：REF[n]为参考资料，列于文章结尾。
看了编程珠玑Programming Perls第11章关于快速排序的讨论，发现自己长年用库函数，已经忘了快排怎么写。于是整理下思路和资料，把至今所了解的快排的方方面面记录与此。

纲要

算法描述
时间复杂度分析
具体实现细节

划分

选取枢纽元

固定位置
随机选取
三数取中

分割

单向扫描
双向扫描
Hoare的双向扫描
改进的双向扫描
双向扫描的其他问题


分治

尾递归


参考文献

一、算法描述（Algorithm Description）
快速排序由C.A.R.Hoare于1962年提出，算法相当简单精炼，基本策略是随机分治。
首先选取一个枢纽元（pivot），然后将数据划分成左右两部分，左边的大于（或等于）枢纽元，右边的小于(或等于枢纽元)，最后递归处理左右两部分。
分治算法一般分成三个部分：分解、解决以及合并。快排是就地排序，所以就不需要合并了。只需要划分（partition）和解决（递归）两个步骤。因为划分的结果决定递归的位置，所以Partition是整个算法的核心。
对数组S排序的形式化的描述如下（REF[1]）:

如果S中的元素个数是0或1，则返回
取S中任意一元素v，称之为枢纽元
将S-{v}（S中其余元素），划分成两个不相交的集合：S1={x∈S-{v}&#124;x=v}
返回{quicksort(S1) , v , quicksort(S2)}

二、时间复杂度分析（Time Complexity）
快速排序最佳运行时间O(nlogn)，最坏运行时间Ｏ(N^2)，随机化以后期望运行时间O(nlogn)，关于这些任何一本算法数据结构书上都有证明，就不写在这了，一下两点很重要：

选取枢纽元的不同, 决定了快排算法时间复杂度的数量级；
划分方法的划分方法总是O(n), 所以其具体实现的不同只影响算法时间复杂度的系数。

所以诉时间复杂度的分析都是围绕枢纽元的位置展开讨论的。
三、具体实现细节（Details of Implementaion）
1、划分(Partirion)
为了方便讨论，将Partition从QuickSort函数里提出来，就像算法导论里一样。实际实现时我更倾向于合并在一起，就一个函数，减少了函数调用次数。（REF[2]）

1
2
3
4
5
6
7
8
9
void QuickSort&#40;T A&#91;&#93;, int p, int q&#41;
&#123;
    if &#40;p &#60; q&#41;
    &#123;
        int q = Partition&#40;A, p, q&#41;;
        QuickSort&#40;A, p, q-1&#41;;
    [...]]]></description>
			<content:encoded><![CDATA[<p>快速排序详细分析</p>
<p>注：REF[n]为参考资料，列于文章结尾。</p>
<p>看了编程珠玑Programming Perls第11章关于快速排序的讨论，发现自己长年用库函数，已经忘了快排怎么写。于是整理下思路和资料，把至今所了解的快排的方方面面记录与此。</p>
<p><span id="more-73"></span></p>
<h2>纲要</h2>
<ol>
<li>算法描述</li>
<li>时间复杂度分析</li>
<li>具体实现细节</li>
<ol style="border:0px">
<li>划分</li>
<ol style="border:0px">
<li>选取枢纽元</li>
<ol style="border:0px">
<li>固定位置</li>
<li>随机选取</li>
<li>三数取中</li>
</ol>
<li>分割</li>
<ol style="border:0px">
<li>单向扫描</li>
<li>双向扫描</li>
<li>Hoare的双向扫描</li>
<li>改进的双向扫描</li>
<li>双向扫描的其他问题</li>
</ol>
</ol>
<li>分治</li>
<ol  style="border:0px">
<li>尾递归</li>
</ol>
</ol>
<li>参考文献</li>
</ol>
<h2>一、算法描述（Algorithm Description）</h2>
<p>快速排序由C.A.R.Hoare于1962年提出，算法相当简单精炼，基本策略是随机分治。<br />
首先选取一个枢纽元（pivot），然后将数据划分成左右两部分，左边的大于（或等于）枢纽元，右边的小于(或等于枢纽元)，最后递归处理左右两部分。<br />
分治算法一般分成三个部分：分解、解决以及合并。快排是就地排序，所以就不需要合并了。只需要划分（partition）和解决（递归）两个步骤。因为划分的结果决定递归的位置，所以Partition是整个算法的核心。</p>
<p>对数组S排序的形式化的描述如下（REF[1]）:</p>
<ol>
<li>如果S中的元素个数是0或1，则返回</li>
<li>取S中任意一元素v，称之为枢纽元</li>
<li>将S-{v}（S中其余元素），划分成两个不相交的集合：S1={x∈S-{v}|x<=v} 和 S2={x∈S-{v}|x>=v}</li>
<li>返回{quicksort(S1) , v , quicksort(S2)}</li>
</ol>
<h2>二、时间复杂度分析（Time Complexity）</h2>
<p>快速排序最佳运行时间O(nlogn)，最坏运行时间Ｏ(N^2)，随机化以后期望运行时间O(nlogn)，关于这些任何一本算法数据结构书上都有证明，就不写在这了，一下两点很重要：</p>
<ol>
<li>选取枢纽元的不同, 决定了快排算法时间复杂度的数量级；</li>
<li>划分方法的划分方法总是O(n), 所以其具体实现的不同只影响算法时间复杂度的系数。</li>
</ol>
<p>所以诉时间复杂度的分析都是围绕枢纽元的位置展开讨论的。</p>
<h2>三、具体实现细节（Details of Implementaion）</h2>
<h3>1、划分(Partirion)</h3>
<p>为了方便讨论，将Partition从QuickSort函数里提出来，就像算法导论里一样。实际实现时我更倾向于合并在一起，就一个函数，减少了函数调用次数。（REF[2]）</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">void</span> QuickSort<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>p <span style="color: #000080;">&lt;</span> q<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">int</span> q <span style="color: #000080;">=</span> Partition<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        QuickSort<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #000040;">-</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        QuickSort<span style="color: #008000;">&#40;</span>A, q<span style="color: #000040;">+</span><span style="color: #0000dd;">1</span>, r<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>划分又分成两个步骤：<b>选取枢纽元</b>和<b>按枢纽元将数组分成左右两部分</b></p>
<h4>a.选取枢纽元（Pivot Selection）</h4>
<h5>固定位置</h5>
<p>同样是为了方便，将选取枢纽元单独提出来成一个函数：select_pivot(T A[], int p, int q)，该函数从A[p...q]中选取一个枢纽元并返回，且枢纽元放置在左端（A[p]的位置）。</p>
<p>对于完全随机的数据，枢纽元的选取不是很重要，往往直接取左端的元素作为枢纽元。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> select_pivot<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">return</span> A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>但是实际应用中，数据往往是部分有序的，如果仍用两端的元素最为枢纽元，则会产生很不好的划分，使算法退化成O(n^2)。所以要采用一些手段避免这种情况，我知道的有“随机选取法”和“三数取中法”。</p>
<h5>随机选取</h5>
<p>顾名思义就是从A[p...q]中随机选择一个枢纽元，这个用库函数可以很容易实现</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> select_pivot_random<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> randInt<span style="color: #008000;">&#40;</span>p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>其中randInt(p, q)随机返回[p, q]中的一个数，C/C++里可由stdlib.h中的rand函数模拟。</p>
<h5>三数取中</h5>
<p>即取三个元素的中间数作为枢纽元，一般是取左端、右断和中间三个数，也可以随机选取。（REF[1]）</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> select_pivot_median3<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> m <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span>p <span style="color: #000040;">+</span> q<span style="color: #008000;">&#41;</span><span style="color: #000040;">/</span><span style="color: #0000dd;">2</span><span style="color: #008080;">;</span>
    <span style="color: #ff0000; font-style: italic;">/* swap to ensure A[m] &lt;= A[p] &lt;= A[q] */</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> A<span style="color: #008000;">&#91;</span>m<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>m<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> A<span style="color: #008000;">&#91;</span>m<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>m<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<h4>b.按枢纽元将数组分成左右两部分</h4>
<p>虽然说分割方法只影响算法时间复杂度的系数，但是一个好系数也是比较重要的。这也就是为什么实际应用中宁愿选择可能退化成O(n^2)的快速排序，也不用稳定的堆排序（堆排序交换次数太多，导致系数很大）。</p>
<p>常见的分割方法有三种：</p>
<h5>单向扫描</h5>
<p>单向扫描代码非常简单，只有短短的几行，思路也比较清晰。该算法由N.Lomuto提出，算法导论上也采用了这种算法。对于数组A[p...q]，该算法用一个循环扫描整个区间，并维护一个标志m，使得循环不变量（loop invariant）A[p＋1...m] < A[p] &#038;&#038; A[m+1, i-1] >= x[l]始终成立。（REF[2],REF[3]）</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> partition<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> x <span style="color: #000080;">=</span> select_pivot<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">int</span> m <span style="color: #000080;">=</span> p, j<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> j <span style="color: #000080;">=</span> p<span style="color: #000040;">+</span><span style="color: #0000dd;">1</span><span style="color: #008080;">;</span> j <span style="color: #000080;">&lt;=</span> q<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>j<span style="color: #008000;">&#41;</span>
        <span style="color: #ff0000; font-style: italic;">/* invariant : A[p＋1...m] &lt; A[p] &amp;&amp; A[m+1, i-1] &gt;= x[q] */</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;=</span> x<span style="color: #008000;">&#41;</span>
            swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span><span style="color: #000040;">++</span>m<span style="color: #008000;">&#93;</span>, j<span style="color: #008000;">&#41;</span> 
    swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>m<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
    <span style="color: #ff0000; font-style: italic;">/* A[p...m-1] &lt; A[m] &lt;= A[m+1...u] */</span>
    <span style="color: #0000ff;">return</span> m<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>顺便废话几句，在看国外的书的时候，发现老外在分析和测试算法尤其是循环时，非常重视不变量（invariant）的使用。确立一个不变量，在循环开始之前和结束之后检查这个不变量，是一个很好的保持算法正确性的手段。</p>
<p>事实上第一种算法需要的交换次数比较多，而且如果采用选取左端元素作为枢纽元的方法，该算法在输入数组中元素全部相同时退化成O(n^2)。第二种方法可以避免这个问题。</p>
<h5>双向扫描</h5>
<p>双向扫描用两个标志i、j，分别初始化成数组的两端。主循环里嵌套两个内循环：第一个内循环i从左向右移过小于枢纽元的元素，遇到大元素时停止；第二个循环j从右向左移过大于枢纽元的元素，遇到小元素时停止。然后主循环检查i、j是否相交并交换A[i]、A[j]。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> partition<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> x <span style="color: #000080;">=</span> select_pivot<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> p, j <span style="color: #000080;">=</span> q <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span> <span style="color: #008080;">;</span> <span style="color: #008080;">;</span> <span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">do</span> <span style="color: #000040;">++</span>i<span style="color: #008080;">;</span> <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>i <span style="color: #000080;">&lt;=</span> q <span style="color: #000040;">&amp;&amp;</span> A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> x<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">do</span> <span style="color: #000040;">--</span>j<span style="color: #008080;">;</span> <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&gt;</span> x<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>i <span style="color: #000080;">&gt;</span> j<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
        swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
    swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> j<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>双向扫描可以正常处理所有元素相同的情况，而且交换次数比单向扫描要少。</p>
<h5>Hoare的双向扫描</h5>
<p>这种方法是Hoare在62年最初提出快速排序采用的方法，与前面的双向扫描基本相同，但是更难理解，手算了几组数据才搞明白：（REF[2]）</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> partition<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> x <span style="color: #000080;">=</span> select_pivot<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> p <span style="color: #000040;">-</span> <span style="color: #0000dd;">1</span>, j <span style="color: #000080;">=</span> q <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span> <span style="color: #008080;">;</span> <span style="color: #008080;">;</span> <span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">do</span> <span style="color: #000040;">--</span>j<span style="color: #008080;">;</span> <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&gt;</span> x<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">do</span> <span style="color: #000040;">++</span>i<span style="color: #008080;">;</span> <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> x<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>i <span style="color: #000080;">&lt;</span> j<span style="color: #008000;">&#41;</span> swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span>
        <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">return</span> j<span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>需要注意的是，返回值j并不是枢纽元的位置，但是仍然保证了A[p..j] <= A[j+1...q]。这种方法在效率上于双向扫描差别甚微，只是代码相对更为紧凑，并且用A[p]做哨兵元素减少了内层循环的一个if测试。</p>
<p>http://www.see2say.com/channel/music/player.aspx?v_album_id=9804</p>
<h5>改进的双向扫描</h5>
<p>枢纽元保存在一个临时变量中，这样左端的位置可视为空闲。j从右向左扫描，直到A[j]小于等于枢纽元,检查i、j是否相交并将A[j]赋给空闲位置A[i],这时A[j]变成空闲位置；i从左向右扫描，直到A[i]大于等于枢纽元,检查i、j是否相交并将A[i]赋给空闲位置A[j]，然后A[i]变成空闲位置。重复上述过程，最后直到i、j相交跳出循环。最后把枢纽元放到空闲位置上。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> partition<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> x <span style="color: #000080;">=</span> select_pivot<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> p, j <span style="color: #000080;">=</span> q<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span> <span style="color: #008080;">;</span> <span style="color: #008080;">;</span> <span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>i <span style="color: #000080;">&lt;</span> j <span style="color: #000040;">&amp;&amp;</span> A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&gt;</span> x<span style="color: #008000;">&#41;</span> <span style="color: #000040;">--</span>j<span style="color: #008080;">;</span>
        A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>i <span style="color: #000080;">&lt;</span> j <span style="color: #000040;">&amp;&amp;</span> A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> x<span style="color: #008000;">&#41;</span> <span style="color: #000040;">++</span>i<span style="color: #008080;">;</span>
        A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
    A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> x<span style="color: #008080;">;</span>  <span style="color: #666666;">// i == j</span>
    <span style="color: #0000ff;">return</span> i<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>这种类似迭代的方法，每次只需一次赋值，减少了内存读写次数，而前面几种的方法一次交换需要三次赋值操作。由于没有哨兵元素，不得不在内层循环里判断i、j是否相交，实际上反而增加了很多内存读取操作。但是由于循环计数器往往被放在寄存器了，而如果待排数组很大，访问其元素会频繁的cache miss，所以用计数器的访问次数换取待排数组的访存是值得的。</p>
<h5>关于双向扫描的几个问题</h5>
<p>1.内层循环中的while测试是用“严格大于/小于”还是&#8221;大于等于/小于等于&#8221;。</p>
<p>一般的想法是用大于等于/小于等于，忽略与枢纽元相同的元素，这样可以减少不必要的交换，因为这些元素无论放在哪一边都是一样的。但是如果遇到所有元素都一样的情况，这种方法每次都会产生最坏的划分，也就是一边1个元素，令一边n－1个元素，使得时间复杂度变成O(N^2)。而如果用严格大于/小于，虽然两边指针每此只挪动1位，但是它们会在正中间相遇，产生一个最好的划分，时间复杂度为log(2,n)。</p>
<p>另一个因素是，如果将枢纽元放在数组两端，用严格大于/小于就可以将枢纽元作为一个哨兵元素，从而减少内层循环的一个测试。<br />
由以上两点，内层循环中的while测试一般用“严格大于/小于”。</p>
<p>2.对于小数组特殊处理</p>
<p>按照上面的方法，递归会持续到分区只有一个元素。而事实上，当分割到一定大小后，继续分割的效率比插入排序要差。由统计方法得到的数值是50左右(REF[3])，也有采用20的（REF[1]）, 这样原先的QuickSort就可以写成这样</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">void</span> QuickSort<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>q <span style="color: #000040;">-</span> p <span style="color: #000080;">&gt;</span> cutoff<span style="color: #008000;">&#41;</span> <span style="color: #666666;">//cutoff is constant </span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">int</span> q <span style="color: #000080;">=</span> Partition<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        QuickSort<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #000040;">-</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        QuickSort<span style="color: #008000;">&#40;</span>A, q<span style="color: #000040;">+</span><span style="color: #0000dd;">1</span>, r<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">else</span>
        InsertionSort<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <span style="color: #666666;">//user insertion sort for small arrays</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<h2>二、分治</h2>
<p>分治这里看起来没什么可说的，就是一枢纽元为中心，左右递归，实际上也有一些技巧。</p>
<h3>1.尾递归（Tail recursion）</h3>
<p>快排算法和大多数分治排序算法一样，都有两次递归调用。但是快排与归并排序不同，归并的递归则在函数一开始， 快排的递归在函数尾部，这就使得快排代码可以实施尾递归优化。第一次递归以后，变量p就没有用处了， 也就是说第二次递归可以用迭代控制结构代替。虽然这种优化一般是有编译器实施，但是也可以人为的模拟：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">void</span> QuickSort<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>p <span style="color: #000080;">&lt;</span> q<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">int</span> m <span style="color: #000080;">=</span> Partition<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        QuickSort<span style="color: #008000;">&#40;</span>A, p, m<span style="color: #000040;">-</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        p <span style="color: #000080;">=</span> m <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>采用这种方法可以缩减堆栈深度，由原来的O(n)缩减为O(logn)。</p>
<h2>三、参考文献：</h2>
<li>[1]Mark Allen Weiss. <em>Data Structures and Algorithms Analysis in C++</em>. Pearson Education, Third Edition, 2006.</li>
<li>[2]Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein. <em>Introduction to Algorithms</em>. MIT Press, 2001, Second Edtion, 2001.</li>
<li>[3]Jon Bently. <em>Programming Pearls</em>. Addison Wesley, Second Edition, 2000.</li>

	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li><a href="http://jackaldire.com/201003/youdao-intern-interview-summary/" title="有道实习生面试总结 (2010年03月9日)">有道实习生面试总结</a> (0)</li>
	<li><a href="http://jackaldire.com/201003/youdao-written-exam-summary/" title="有道实习生笔试总结 (2010年03月6日)">有道实习生笔试总结</a> (6)</li>
	<li><a href="http://jackaldire.com/200908/reverse-token/" title="[备忘]倒置字符串中的单词 (2009年08月18日)">[备忘]倒置字符串中的单词</a> (0)</li>
	<li><a href="http://jackaldire.com/200911/string-matching-with-wildcard/" title="[一道面试题]含有*的字符串匹配问题 (2009年11月25日)">[一道面试题]含有*的字符串匹配问题</a> (1)</li>
	<li><a href="http://jackaldire.com/200905/smart-way-to-solve-fibonacci/" title="Fibonacci数的巧妙求法 (2009年05月6日)">Fibonacci数的巧妙求法</a> (2)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/200908/quick-sort-analysis/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
