﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-hex108-随笔分类-Program</title><link>http://www.cppblog.com/hex108/category/14173.html</link><description>懂历史 ==&gt; 知未来
</description><language>zh-cn</language><lastBuildDate>Thu, 14 Jul 2011 07:27:11 GMT</lastBuildDate><pubDate>Thu, 14 Jul 2011 07:27:11 GMT</pubDate><ttl>60</ttl><item><title>gdb,strace那些不常用的功能</title><link>http://www.cppblog.com/hex108/archive/2011/05/17/146600.html</link><dc:creator>hex108</dc:creator><author>hex108</author><pubDate>Tue, 17 May 2011 13:14:00 GMT</pubDate><guid>http://www.cppblog.com/hex108/archive/2011/05/17/146600.html</guid><wfw:comment>http://www.cppblog.com/hex108/comments/146600.html</wfw:comment><comments>http://www.cppblog.com/hex108/archive/2011/05/17/146600.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/hex108/comments/commentRss/146600.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/hex108/services/trackbacks/146600.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 主要想介绍一下.gdbinit文件。<br><br>gdb运行时会首先加载 ~/.gdbinit文件<br>例如：我在debug时，每次都需要进行handle SIGBUS noprint pass来处理SIGBUS信号，这种情况就可以把它写入 .gdbinit文件。<br>在.gdbinit里也可以定义函<br>  eg:  在.gdbinit里定义print_regs <br>     def print_regs<br>        i r eax ebx ecx edx<br>     end<br>  (gdb) print_regs<br>  eax            0xbffff4a4    -1073744732<br>  ebx            0x28bff4    2670580<br>  ecx            0x902c5562    -1876142750<br>  edx            0x1    1&nbsp;&nbsp;<a href='http://www.cppblog.com/hex108/archive/2011/05/17/146600.html'>阅读全文</a><img src ="http://www.cppblog.com/hex108/aggbug/146600.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hex108/" target="_blank">hex108</a> 2011-05-17 21:14 <a href="http://www.cppblog.com/hex108/archive/2011/05/17/146600.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Debug</title><link>http://www.cppblog.com/hex108/archive/2011/05/17/146592.html</link><dc:creator>hex108</dc:creator><author>hex108</author><pubDate>Tue, 17 May 2011 12:26:00 GMT</pubDate><guid>http://www.cppblog.com/hex108/archive/2011/05/17/146592.html</guid><wfw:comment>http://www.cppblog.com/hex108/comments/146592.html</wfw:comment><comments>http://www.cppblog.com/hex108/archive/2011/05/17/146592.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/hex108/comments/commentRss/146592.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/hex108/services/trackbacks/146592.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp; debug可以帮助熟悉系统，可是时间长了会很疲卷，特别是机械的调试，如果还要面对杂乱的代码，更是雪上加霜。所以要学着从debug中钻探快乐，在系统的调试过程中发挥想象，尝试不同的debug方法。</p> <p>&nbsp;&nbsp;&nbsp; 最近看了《软件调试实战》，结合自己的经历，总结了一下：</p> <p>&nbsp;&nbsp;&nbsp; 1. 与测试用例相关</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a. 如果不能达到&#8220;测试先行&#8221;，至少应该在写完代码后有相对完整的测试用例。对于正确性的保证和以后重构代码都是有好处的。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b. 每次添加新功能或修复了一个bug时，都应该增加测试用例！A历经千辛万苦终于fix 了一个bug，很久很久以后，B觉得这段代码需要改改，于是改了改，后来的结果还是改了，而且顺利提交到了库里（因为A当时遇到的bug 并没有出现！）</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c. 回归测试</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 修改代码后进行回归测试。每次提交一个版本后自动进行回归测试，保证库里的代码的正确性。  </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d. 简化测试用例  </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 好处：可以排除不起作用的因素；减少测试用例的运行时间；最重要的是，使用测试用例更容易调试（谁愿意处理那些填充了数百或数千项的数据容器呢？）  </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 方法如： 如果测试例子比较好改，可以将其改小；将输入集改小  </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e. 完成代码，清理后重新运行所有测试用例。  </p><p>&nbsp;&nbsp;&nbsp; 2. 关于程序的编译  </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a. 重视编译期间的warning，最好把warning数减为0. 不要忽略编译器警告，即使它们可能是无害的。  </p><blockquote> <p>eg：  </p><p>int add(int a,int b){  </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return a +b ;  </p><p>}  </p><p>结果头文件里声明成了 extern int add(long a,int b)  </p><p>会调试死人啊，调程序的时候一看程序定义是对的啊，怎么传的参数一下就变了；  </p><p>b. 如果出现莫名其妙的错误  </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果是用Makefile组织工程时，考虑make clean，有可能修改数据结构或头文件后改变了一些东西，但是由于一些未知原因该文件并未重新编译。如果函数是C函数，有可能调用者和被 调用者的参数的成员和类型不同。如果一个类方法，则访问任何类成员 都将发生错误，因为这两个类的内存而已几乎是完全不同的。这可能导致Segmentation falut,或是很久之后才能检测到的内存破坏。  </p><p>3. 关于链接  </p><p>a. 链接器的基本工作原理  </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编译器或汇编程序将源代码转换为机器代码，并输出对象谁的。对象文件中包含符号（函数或变量），这些符号有的在本模块定义的，有的在其他模块定义的，链接器就在链接对象文件时把这些未定义的符号与定义它的模块对应起来。  </p><p>b. 链接顺序  </p><p>&nbsp;&nbsp;&nbsp;&nbsp; 有库和归档文件时 链接算法是不一样的。&nbsp;&nbsp;&nbsp;&nbsp; </p><p>&nbsp;&nbsp;&nbsp;&nbsp; 链接器参数顺序很重要，对于编译单元（如对象文件和库）和搜索路径来说都是如此。  </p><p>c. C++中使用C代码时，用extern c{} 把C代码包装一下。  </p><p>&nbsp;&nbsp;&nbsp;&nbsp; 关于 c++符号和名称改编：C++允许重载函数，为了生成C++代码元素的唯一符号，编译器使一种称为名称改编（name mangling）的技术，它将对象的准确规格说明（如会员名空间和函数参数的个数及类型）编码到符号中。（可以用c++filt解析出来~ eg: c++filt _Z9factoriali的结果为factorial(int)）  </p><p>d. 环境变量  </p><p>&nbsp;&nbsp; LD_LIBRARY_PATH会影响动态加载的库，用LDD可以看到程序依赖哪个动态库  </p><p>4. <font color="#ff0000">自动化测试 </font> </p><p>&nbsp;&nbsp; 让一切自动化起来。如果重复的做一件事，就很有必要考虑自动化了。  </p><p>5. 关于那些怪异的错误  </p><p>&nbsp;&nbsp;&nbsp; 在一些显而易见有内存问题的情况下，如：间歇故障和无法解释的随机行为，这时考虑使用内存调试器了！  </p><p>&nbsp;&nbsp;&nbsp; 如valgrind，很好用，也很简单。  </p><p>&nbsp;&nbsp;&nbsp; valgrind &#8211;tool=massif your_program 进行内存剖析（检测内存分配情况，优化内存使用）  </p><p>&nbsp;&nbsp;&nbsp; valgrind &#8211;tool=memcheck your_program 进行内存检查（检测无效的写访问，检测对未初始化的内存的读取操作，检测内存泄露等）  </p><p>&nbsp;&nbsp;&nbsp; valgrind &#8211;tool=helgrind your_program 查找竞争条件，可以用来辅助调试多线程程序  </p><p>&nbsp;&nbsp;&nbsp; valgrid &#8211;-db-attac=yes的功能很好用，可以将内存高度器和源代码测试器（如gdb）结合起来，这样就可以即时查看当时的变量的值，很好用！  </p><p>6. 静态检查器  </p><p>&nbsp;&nbsp; 作为常规软件构建过程中的一部分运行，用于查找一些可通过静态源代码分析发现的特定bug。  </p><p>7. 关于运行时剖析工具  </p><p>&nbsp;&nbsp;&nbsp;&nbsp; 不要编写自己的运行时剖析时工具：自己霞友云朋一的剖析 工具通常使用系统调用time()或ctime()来测量时间。这些系统调用的问题是开销很高，而且准确度低。另处在剖析期间要收集大量数据，可能会影响程序本身的行为。  </p><p>8. 环境变量  </p><p>&nbsp; 如程序的行为可能 依赖于当前工作目录。在linux上，目录被注册到环境变量CWD上。这个bug碰到过，还导致了死锁。  </p><p>9. 读取恰当的错误消息  </p><p>&nbsp; 某个地方出错时，满屏都是错误消息时，应该重点关注哪些消息？  </p><p>&nbsp; Answer: 首先出现的那些消息！因为后面的消息有可能是前面导致的。这和编译出错时的情景一致：编译错误有很多，我们肯定会直觉地去寻找第一个出错的 地方，谁知道是不是少了个括号导致后面一连串的错误。  </p><p>10. bug不会自动消失  </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果某个版本有bug，update后，bug消失了，&#8220;真好！&#8221;，一定要弄清楚bug出现的原因是什么。以前遇到过一个bug，增加一条printf语句后，bug消失了！最后发现问题是数组越界了，而修改源代码会导致代码段，数据段的布局等改变，所以会导致偶尔对。（这种情况可以求助于内存调试工具或者静态检查的工具）  </p><p>11. 学习使用gcc, gdb,strace 等工具。（熟悉以后可以再挖掘挖掘，可能有惊喜）  </p><p>12. cvs/svn commit之前一定要diff一下，看做了哪些修改，以避免不小心删掉一些东西后，然后&#8221;被提交&#8221;了。  </p><p>最后，最强大的工具不在计算机中，而是调试者的判断力和分析技巧。</p></blockquote> <p>&nbsp;&nbsp; 参考资料：  </p><p>&nbsp;&nbsp; 1. 《软件调试实战》：<a href="http://book.douban.com/subject/4231293/">http://book.douban.com/subject/4231293/</a></p><img src ="http://www.cppblog.com/hex108/aggbug/146592.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hex108/" target="_blank">hex108</a> 2011-05-17 20:26 <a href="http://www.cppblog.com/hex108/archive/2011/05/17/146592.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>shell编程 : Remember that the shell spends a lot of its life substituting text</title><link>http://www.cppblog.com/hex108/archive/2011/04/23/144812.html</link><dc:creator>hex108</dc:creator><author>hex108</author><pubDate>Fri, 22 Apr 2011 16:23:00 GMT</pubDate><guid>http://www.cppblog.com/hex108/archive/2011/04/23/144812.html</guid><wfw:comment>http://www.cppblog.com/hex108/comments/144812.html</wfw:comment><comments>http://www.cppblog.com/hex108/archive/2011/04/23/144812.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/hex108/comments/commentRss/144812.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/hex108/services/trackbacks/144812.html</trackback:ping><description><![CDATA[<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对shell不熟，偶尔会现一些我无法理解的现象。此时该进行debug了，可选的方法有:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a. echo变量的值&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b. shell &#8211;x <br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 此外，<font color="#ff0000">Remember that the shell spends a lot of its life substituting text</font>.（<a href="http://linuxcommand.org/wss0100.php">http://linuxcommand.org/wss0100.php</a>）例如，对于下面的程序：</div> <blockquote><pre class="csharpcode"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#104;&#101;&#120;&#49;&#48;&#56;&#64;&#71;&#101;&#110;&#116;&#111;&#111;">hex108@Gentoo</a> ~ $ cat test.sh 
#!/bin/sh
var=
<span class="kwrd">if</span> [ $var = <span class="str">"y"</span> ] ;then
    echo <span class="str">"yes"</span>
fi</pre></blockquote>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if语句里的var变量经替换后变为 if [ = "y" ]，些时当然会出错。<br />
<blockquote><pre class="csharpcode">hex108@Gentoo ~ $ ./test.sh 
./test.sh: line 3: [: =: unary <span class="kwrd">operator</span> expected</pre></blockquote>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ps:现在写脚本的时候倾向于使用perl,而较少使用shell ，因为对于经常使用的脚本，可能会经常需要对它不停地进行改进，慢慢的，程序越来越大，该考虑重构了，&nbsp;&nbsp; 此时才会发现perl(python等&#8220;真正的&#8221;脚本语言)比shell相对来说更好重构。</p><img src ="http://www.cppblog.com/hex108/aggbug/144812.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hex108/" target="_blank">hex108</a> 2011-04-23 00:23 <a href="http://www.cppblog.com/hex108/archive/2011/04/23/144812.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>理解C指针： 一个内存地址对应着一个值</title><link>http://www.cppblog.com/hex108/archive/2010/08/21/124234.html</link><dc:creator>hex108</dc:creator><author>hex108</author><pubDate>Sat, 21 Aug 2010 15:20:00 GMT</pubDate><guid>http://www.cppblog.com/hex108/archive/2010/08/21/124234.html</guid><wfw:comment>http://www.cppblog.com/hex108/comments/124234.html</wfw:comment><comments>http://www.cppblog.com/hex108/archive/2010/08/21/124234.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/hex108/comments/commentRss/124234.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/hex108/services/trackbacks/124234.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 一个内存地址存着一个对应的值，这是比较容易理解的。<br /><br />&nbsp;&nbsp;&nbsp; 如果程序员必须清楚地知道某块内存存着什么内容和某个内容存在哪个内存地址里了，那他们的负担可想而知。<br />&nbsp;&nbsp;&nbsp; 汇编语法对&#8220;一个内存地址存着一个对应的数&#8221;，作了简单的&#8220;抽象&#8221;：<font color="#cc0000">把内存地址用变量名代替</font>了，对内存地址的取值和赋值方式不变。<br />&nbsp;&nbsp;&nbsp; c语言对此进行了进一步的抽象：变量 &lt;==&gt; （一个内存地址，对应的值）（这里忽略类型等信息）。<br /><br />&nbsp;&nbsp;&nbsp; 把C语言中的基本类型(int,long,float等),指针，数组等还原为（一个内存地址，对应的值）后，就能更清淅地理解它们了。<br /><br />&nbsp;&nbsp;&nbsp; 内存就相当于(addr,val)的大hash表,c语句的语义基本就是改变hash值。<br /><br />&nbsp;&nbsp;&nbsp; 为了下文的方便，特定义如下语义（遵循C的标准语义）： <br /><br /><hr size="2" width="100%" />&nbsp;&nbsp;&nbsp; var&nbsp; &lt;==&gt;&nbsp; (addr, val)&nbsp; (var为一个变量名，addr为var在内存中的首地址,val为var 的值)<br />
&nbsp;&nbsp;&nbsp; &amp;var &lt;==&gt; addr<br />
&nbsp;&nbsp;&nbsp; var&nbsp; &lt;==&gt; var作为左值出现（即等式左边）时，var等价于 addr;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var作为右值出现（即等式左边）时，var等价于 val;<br />
&nbsp;&nbsp;&nbsp; *var &lt;==&gt; val<br /><hr size="2" width="100%" /><br />&nbsp;&nbsp;&nbsp; 注：符号"&lt;==&gt;" 右边出的等式 x = y(x是一个内存地址，y是一个值); 表示将内存地址为x的内容置为值y，如addr = 3表示置内存addr里的值为3<br /><br /><br />&nbsp;&nbsp;&nbsp; 现在利用上面的语义解释一下这些例子：<br />&nbsp;&nbsp;&nbsp; int i = 3; <br />&nbsp;&nbsp;&nbsp; 假设 i的内存地址为 0x8049320 ,那么这句话的语义是0x8049320 = 3，经过i = 3后，i为(0x8049320,3)<br /><br />&nbsp;&nbsp;&nbsp; int b = i;<br />&nbsp;&nbsp;&nbsp; 假设 b的内存地址为 0x8049324 ,那么这句话的语义是0x8049324 = i对应的val = 3,此时b为(0x8049324,3)<br /><br />&nbsp;&nbsp;&nbsp; int *p = &amp;b<br />&nbsp;&nbsp;&nbsp; 指针p也是一个变量，int **p,int *p[8],在这些申明中p都只是一个指针变量，它和其他的变量的不同之处在于它的大小是定的，它的类型信息只是编译器用来进行类型检查和其他一些作用的(如果没有类型检查，你可以用任何的方式对一个变量进行操作如int i; ****i = 3)。假设p的地址为0x8049328,则根据p = &amp;b的语义p.addr = b.addr，p为(0x8049328,0x8049324)<br /><br />&nbsp;&nbsp;&nbsp; *p = 5;<br />&nbsp;&nbsp;&nbsp; 语义为 0x8049324 = 5,此时只改变了内存地址为0x8049324的值，即改变了b的值(0x8049324,5)，而p的值并未改变<br /><br />&nbsp;&nbsp;&nbsp; int **q = &amp;p; //如果写为int **q = &amp;&amp;i; gcc编译不通过<br />&nbsp;&nbsp;&nbsp; 假设q的内存地址为0x8049330,语义为 0x8049330 = addr(p) = 0x8049328;所以q为(0x8049330, 0x8049328)<br />&nbsp;&nbsp;&nbsp; (int **q = &amp;&amp;i, 要是编译过了则q应该表示为(0x8049330, x),内存地址为x的地方表示为(x,0x8049320)，那么地址x为多少呢? )<br /><br />&nbsp;&nbsp;&nbsp; **q = 6<br />&nbsp;&nbsp;&nbsp; 语义为 val(val(q)) = val(0x8049328) = 0x8049324 = 6,将内存地址为0x8049324的内容置为6,即将b的值置为6,b为(0x8049324,6)<br />&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp;&nbsp; 对于结构，这些语义也适用，因为结构里的成员也是有对应地址的，也能表示为(addr,val)的形式。&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; 对&#8220;一个内存地址存着一个对应的值&#8221;的抽象程度越高，越不用关心底层，如java。&nbsp; <br />&nbsp;&nbsp;&nbsp; Haskell已经没有副作用之说了，更不用关心这些了。<br /><br />&nbsp;&nbsp;&nbsp; 就这些。<img src ="http://www.cppblog.com/hex108/aggbug/124234.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hex108/" target="_blank">hex108</a> 2010-08-21 23:20 <a href="http://www.cppblog.com/hex108/archive/2010/08/21/124234.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>构建正则表达式引擎</title><link>http://www.cppblog.com/hex108/archive/2010/06/17/118107.html</link><dc:creator>hex108</dc:creator><author>hex108</author><pubDate>Thu, 17 Jun 2010 12:50:00 GMT</pubDate><guid>http://www.cppblog.com/hex108/archive/2010/06/17/118107.html</guid><wfw:comment>http://www.cppblog.com/hex108/comments/118107.html</wfw:comment><comments>http://www.cppblog.com/hex108/archive/2010/06/17/118107.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/hex108/comments/commentRss/118107.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/hex108/services/trackbacks/118107.html</trackback:ping><description><![CDATA[一． <span style="font-weight: bold;">简介</span><br><br>&nbsp;&nbsp;&nbsp; 该正则表达式暂时能识别 *,|,(,)等特殊符号，如(a|b)*abc。不过扩展到其他符号（如?）也相对比较容易，修改NFA中的构建规则即可。<br><br>二． <span style="font-weight: bold;">引擎的构建</span><br><br>&nbsp;&nbsp;&nbsp; 该正则表达式引擎的构建以《Compilers Principles,Techniques &amp; Tools》3.7节为依据，暂时只能识别*,|,(,)这几个特殊的字符，其工作过程为：构建NFA -&gt; 根据NFA构建DFA -&gt; 用DFA匹配。<br><br>1. 构建NFA<br>该NFA的构建以2条基本规则和3条组合规则为基础，采用归纳的思想构建而成。<br>1）2条基本的规则是:<br>a. 以一个空值&#949;构建一个NFA<br><img alt="" src="http://www.cppblog.com/images/cppblog_com/hex108/1.JPG" width="253" height="67"><br>b. 以一个字符a构建一个NFA<br><img alt="" src="http://www.cppblog.com/images/cppblog_com/hex108/2.JPG" width="256" height="67"><br>2) 3条组合规则是：<br>a. r = s | t （其中s和t都是NFA）<br><img style="width: 390px; height: 155px;" alt="" src="http://www.cppblog.com/images/cppblog_com/hex108/3.JPG"><br>b. r = s t（其中s和t都是NFA）<br><img alt="" src="http://www.cppblog.com/images/cppblog_com/hex108/4.JPG"><br>c. r = s *（其中s为NFA）<br><img style="width: 468px; height: 157px;" alt="" src="http://www.cppblog.com/images/cppblog_com/hex108/5.JPG"><br>3) 如果需要识别如&#8221;?&#8221;等特殊符号，则可再加一些组合规则。<br><br>在具体的程序中，可以以下面的BNF为结构来实现。（具体见源程序regexp.cpp）<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; width: 98%; font-size: 13px;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">r&nbsp;</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">&nbsp;r&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">|</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;s&nbsp;</span><span style="color: #000000;">|</span><span style="color: #000000;">&nbsp;r<br>s&nbsp;</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">&nbsp;s&nbsp;t&nbsp;</span><span style="color: #000000;">|</span><span style="color: #000000;">&nbsp;s<br>t&nbsp;</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">&nbsp;a&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">*</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">|</span><span style="color: #000000;">&nbsp;a<br>a&nbsp;</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">&nbsp;token&nbsp;</span><span style="color: #000000;">|</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">(</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;r&nbsp;</span><span style="color: #000000;">'</span><span style="color: #000000;">)</span><span style="color: #000000;">'</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">|</span><span style="color: #000000;"> &#949; &nbsp;&nbsp; <br></span></div>
<br>2. 构建DFA<br>主要是求&#949;闭包的过程，从一个集合的&#949;闭包转移到一个集合的&#949;闭包。<br>以a*c为例，其NFA图如下所示（用dot画的）<br><img alt="" src="http://www.cppblog.com/images/cppblog_com/hex108/2_nfa.gv.gif" width="496" height="115"><br>为例:<br>起始结点3的&#949;闭包集为 A = {3,1,4}<br>A遇上字母a的转移为MOV(A,a) = { 2 }，其&#949;闭包集为B = { 2,1,4 }<br>A遇上字母c的转移为MOV(A,c) = { 6 }，其&#949;闭包集为B = { 6 }<br>同理可求出其他转移集合，最后得到的DFA如下所示:<br><img alt="" src="http://www.cppblog.com/images/cppblog_com/hex108/2_dfa.gv.gif" width="293" height="141"><br>3. 匹配<br>每匹配成功一个字符则DFA移动到下个相应的结点。<br><br>三． <span style="font-weight: bold;">改进</span><br><br>1. 如龙书中所说，有时候模拟NFA而不是直接构建DFA可能达到更好的效果。<br>2. 每次匹配不成功都需要回溯，这个地方也可以借鉴KMP算法（不过KMP对此好像有点不适用）<br>3. 其他改进方法可以看看《柔性字符串匹配》和龙书《Compilers Principles,Techniques &amp; Tools》3.7节。<br><br>
<meta name="ProgId" content="Word.Document">
<meta name="Generator" content="Microsoft Word 12">
<meta name="Originator" content="Microsoft Word 12">
<link rel="File-List" href="file:///C:%5CDOCUME%7E1%5Cgong%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml">
<link rel="themeData" href="file:///C:%5CDOCUME%7E1%5Cgong%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx">
<link rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5Cgong%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml"><style>
<!--
/* Font Definitions */
@font-face
{font-family:宋体;
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-alt:SimSun;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:3 135135232 16 0 262145 0;}
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;
mso-font-charset:0;
mso-generic-font-family:roman;
mso-font-pitch:variable;
mso-font-signature:-1610611985 1107304683 0 0 159 0;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;
mso-font-charset:0;
mso-generic-font-family:swiss;
mso-font-pitch:variable;
mso-font-signature:-1610611985 1073750139 0 0 159 0;}
@font-face
{font-family:"\@宋体";
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:3 135135232 16 0 262145 0;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{mso-style-unhide:no;
mso-style-qformat:yes;
mso-style-parent:"";
margin:0cm;
margin-bottom:.0001pt;
text-align:justify;
text-justify:inter-ideograph;
mso-pagination:none;
font-size:10.5pt;
mso-bidi-font-size:11.0pt;
font-family:"Calibri","sans-serif";
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-fareast-font-family:宋体;
mso-fareast-theme-font:minor-fareast;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:minor-bidi;
mso-font-kerning:1.0pt;}
p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph
{mso-style-priority:34;
mso-style-unhide:no;
mso-style-qformat:yes;
margin:0cm;
margin-bottom:.0001pt;
text-align:justify;
text-justify:inter-ideograph;
text-indent:21.0pt;
mso-char-indent-count:2.0;
mso-pagination:none;
font-size:10.5pt;
mso-bidi-font-size:11.0pt;
font-family:"Calibri","sans-serif";
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-fareast-font-family:宋体;
mso-fareast-theme-font:minor-fareast;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:minor-bidi;
mso-font-kerning:1.0pt;}
.MsoChpDefault
{mso-style-type:export-only;
mso-default-props:yes;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:minor-bidi;}
/* Page Definitions */
@page
{mso-page-border-surround-header:no;
mso-page-border-surround-footer:no;}
@page Section1
{size:595.3pt 841.9pt;
margin:72.0pt 90.0pt 72.0pt 90.0pt;
mso-header-margin:42.55pt;
mso-footer-margin:49.6pt;
mso-paper-source:0;
layout-grid:15.6pt;}
div.Section1
{page:Section1;}
/* List Definitions */
@list l0
{mso-list-id:1474565200;
mso-list-type:hybrid;
mso-list-template-ids:261280450 -1758036188 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;}
@list l0:level1
{mso-level-number-format:japanese-counting;
mso-level-text:%1．;
mso-level-tab-stop:none;
mso-level-number-position:left;
margin-left:21.0pt;
text-indent:-21.0pt;}
ol
{margin-bottom:0cm;}
ul
{margin-bottom:0cm;}
-->
</style>
<p><strong>四． </strong><strong>代码下载</strong></p>
<p>svn checkout http://regexp.googlecode.com/svn/trunk/ regexp-read-only<strong> <br></strong></p>
或 <a href="http://www.cppblog.com/Files/hex108/regexp.rar">regexp.rar</a>   <img src ="http://www.cppblog.com/hex108/aggbug/118107.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/hex108/" target="_blank">hex108</a> 2010-06-17 20:50 <a href="http://www.cppblog.com/hex108/archive/2010/06/17/118107.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>