﻿<?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++博客-loop_in_codes-随笔分类-通用编程</title><link>http://www.cppblog.com/kevinlynx/category/6540.html</link><description>低调做技术__
C/C++\MMORPG服务器\模块架构__ TODO：linux env/read more books __Kevin Lynx</description><language>zh-cn</language><lastBuildDate>Fri, 20 Apr 2012 09:07:22 GMT</lastBuildDate><pubDate>Fri, 20 Apr 2012 09:07:22 GMT</pubDate><ttl>60</ttl><item><title>使用Github Page来写博客</title><link>http://www.cppblog.com/kevinlynx/archive/2012/04/20/172149.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Fri, 20 Apr 2012 08:21:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2012/04/20/172149.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/172149.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2012/04/20/172149.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/172149.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/172149.html</trackback:ping><description><![CDATA[<p>原文链接<a href="http://codemacro.com/2012/04/20/blog-on-github/">http://codemacro.com/2012/04/20/blog-on-github/</a></p>
<hr/>
<p>最开始知道<a href="http://pages.github.com/">Github Page</a>，是通过<a href="http://codertrace.com">codertrace</a>上的某些注册用户，他们的BLOG就建立在Github Page上，并且清一色的干净整洁（简陋），这看起来很酷。</p>
<p>Github提供了很多很合coder口味的东西，例如Gist，也包括这里提到的Page。Page并不是特用于建立博客的产品，它仅提供静态页面的显示。它最酷的地方，是通过Git的方式来让你管理这些静态页面。通过建立一个repository，并使用markdown语法来编写文章，然后通过Git来管理这些文章，你就可以自动将其发布出去。</p>
<p>当然，要搭建一个像样点的博客，使用Github Page还不太方便。这里可以使用<a href="https://github.com/mojombo/jekyll">Jekyll</a>。Jekyll是一个静态网页生成器，它可以将你的markdown文件自动输出为对应的网页。而Github Page也支持Jekyll。</p>
<p>为了更方便地搭建博客，我还使用了<a href="http://jekyllbootstrap.com">Jekyll-bootstrap</a>。jekyll-bootstrap其实就是一些模板文件，提供了一些博客所需的特殊功能，例如评论，访问统计。</p>
<p>基于以上，我就可以像在Github上做项目一样，编写markdown文章，然后git push即可。可以使用jekyll --server在本地开启一个WEB SERVER，然后编写文章时，可以在本地预览。</p>
<p>Github Page还支持custom domain，如你所见，我将我的域名codemacro.com绑定到了Github Page所提供的IP，而不再是我的VPS。你可以通过kevinlynx.github.com或者codemacro.com访问这个博客。</p>
<hr />
<p>当然实际情况并没有那么简单，例如并没有太多的theme可供选择，虽然jekyll-bootstrap提供了一些，但还是太少。虽然，你甚至可以fork别人的jekyll博客，使用别人定制的theme，但，这对于一个不想过于折腾的人说，门槛也过高了点。</p>
<p>jekyll-bootstrap使用了twitter的bootstrap css引擎，但我并不懂这个，所以，我也只能定制些基本的页面样式。</p>
<hr />
<p>1年前我编写了<a href="https://github.com/kevinlynx/ext-blog">ext-blog</a>，并且在我的VPS上开启了codemacro.com这个博客。本来，它是一个ext-blog很好的演示例子，但维护这个博客给我带来诸多不便。例如，每次发布文章我都需要使用更早前用lisp写的cl-writer，我为什么就不愿意去做更多的包装来让cl-writer更好用？这真是一个垃圾软件，虽然它是我写的。另一方面，codemacro.com使用的主题，虽然是我抄的，但依然太丑，并且恶心。</p>
<p>更别说那个消耗我VPS所有内存的lisp解释器，以及那恶心的两位数字乘法的验证码---你能想象别人得有多强烈的留言欲望，才愿意开一个计算器？</p>
<hr />
<p>说说codertrace.com。我其实写了篇关于codertrace.com的总结，但没有作为博客发布。做这个事情的结果，简单总结来说就是瞎JB折腾没有任何结果。我真的是个苦逼双子男，我每次做件事情都需要巨大的毅力才能让自己专注下去。</p>
<p>整个过程中，收到了些网友的邮件，看到了些评论，虽然不多。邮件/评论中有建议的，也有单纯的交流的，也有单纯鼓励的。我想说的是，thanks guys。</p>
<hr />
<p>Anyway, try Github Page, save your VPS money :D.</p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/172149.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2012-04-20 16:21 <a href="http://www.cppblog.com/kevinlynx/archive/2012/04/20/172149.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>写了个简单的网站，codertrace.com</title><link>http://www.cppblog.com/kevinlynx/archive/2012/02/24/166394.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Fri, 24 Feb 2012 01:22:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2012/02/24/166394.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/166394.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2012/02/24/166394.html#Feedback</comments><slash:comments>16</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/166394.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/166394.html</trackback:ping><description><![CDATA[<h3>简介</h3>
<p>因为写 <a href="https://github.com/kevinlynx/ext-blog">ext-blog</a> 的原因，慢慢喜欢上github_ 。然后突然有一天产生了一个想法：如果可以把自己的博客_ 和 <a href="https://github.com/kevinlynx">github主页</a> 集中到一块展示给别人，会不会是一种很方便的自我简介方式？然后我就动手写了 <a href="http://codertrace.com" target="_blank">codertrace.com</a> 。</p>
<p>所以， <a href="http://codertrace.com" target="_blank">codertrace.com</a> 这个网站的作用就是用来集中让程序员炫耀的。它通过RSS抓取，将你的博客，github主页，或其他有RSS输出的信息集中到一块展示给别人。这些信息通常就可以代表一个程序员。</p>
<p>如果你是程序员，也不妨试试。</p>
<img src="http://www.cppblog.com/images/cppblog_com/kevinlynx/codertrace.png" alt="" height="668" width="1026" border="0" />
<h3>技术信息</h3>
<p>不知道哪个王八蛋说的，程序员每一年得学一门新语言。我2010年末接触了Lisp，然后莫名其妙地写了 <a href="https://github.com/kevinlynx/ext-blog">ext-blog</a> ，又莫名其妙地在2011年末接触了Ruby。因为大学期间太痴迷C++，我勤奋努力，几乎通晓这门语言的各种细节；后来又稍微实践了下编译原理。在这若干年间，断断续续也接触过其他脚本类语言，我甚至在android上用java写过几个 <a href="http://kevinlynx.iteye.com">小应用</a> 。基于这些积累，我发现我可以很快上手Ruby，然后再上手Rails，然后就有了 <a href="http://codertrace.com">codertrace.com</a> （当然还做过一些小的 <a href="http://klquiz.heroku.com" target="_blank">APP</a> )</p>
<p>所以， <a href="http://codertrace.com" target="_blank">codertrace.com</a> 就是一个Ruby on Rails的应用。当我用这货来做WEB的时候，我才发现曾经用Lisp写博客是多么geek。这种感觉就像你在用汇编写一个GUI程序一样。我的意思是，ruby/rails的世界里有太多现成的东西，但lisp的世界里没有。</p>
<p>而且，ruby是一个很爽的语言。我太喜欢它的closure语法，简洁，不需要加其他关键字就可以构造（例如其他语言map(function (item) xxxx end)，或者map(lambda (item) xxx )）。但我不喜欢在使用的地方通过yield去调用---这就像一个hack。我更不喜欢ruby用proc去封装closure。好吧，这其实是我自我分裂，为什么我要把ruby看成一个函数式语言？</p>
<p>脚本语言真是太酷了。</p>
<h3>服务器信息</h3>
<p>我很穷。不管你信不信，我真的舍不得花1000RMB买个VPS来架设 <a href="http://codertrace.com">codertrace.com</a> 。目前， <a href="http://codertrace.com">codertrace.com</a> 架设在 <a href="http://heroku.com">heroku.com</a> ，而且还使用的是免费服务。免费服务竟然只有5M数据库。 <a href="http://codertrace.com" target="_blank">codertrace.com</a> 后台为了异步抓取用户提供的RSS，还使用了一个单独的进程(delayed_job ruby gem)。这也不是免费的。</p>
<p>但ruby的世界里有太多现成的东西了，甚至有很多现成的库解决这里的两个问题：heroku_external_db，这个gem可以让codertrace使用heroku以外的数据库，然后我就在我的VPS上搭了个mysql，这下流量和网站响应速度悲剧了啊，你想你请求个页面，这个页面直接涉及到若干条数据库查询。而这些查询的请求和回应竟然是通过internet网络传输的。</p>
<p>workless，这个gem可以在有异步任务时，例如codertrace上读取RSS，就会自动开启这个worker进程，然后heroku开始计费，当没有任务时，它又自动关闭这个进程。虽然省了美元，但再一次让网站的响应速度打了折扣。</p>
<p>为了实现自定义域名，我需要将 <a href="http://codertrace.com">codertrace.com</a> 指向 <a href="http://heroku.com">heroku.com</a> 提供的IP。但也许你会同我一样愤怒，因为它提供的几个IP都被GFW墙了！所以，目前的实现方案是，我将 <a href="http://codertrace.com">codertrace.com</a> 指向了我博客对应的VPS，然后在VPS上使用nginx反向代理到 <a href="http://heroku.com">heroku.com</a> 提供的IP。即使如此，我最近甚至发现 <a href="http://codertrace.com" target="_blank">codertrace.com</a> 竟然神奇般地会域名解析错误，难道godaddy的name server也要被GFW和谐？？</p>
<h3>故事</h3>
<p>作为一个宅男，在工作的若干年中，若干个假期我都用来打游戏，或者写程序。</p>
<p>所以，当这个成为习惯的时候， <a href="http://codertrace.com" target="_blank">codertrace.com</a> ，就顺理成章地消费了我今年的春节假期。我发现一个人窝在租的小房子里写代码是件很爽的事情。在当前这个社会环境下，你可以专注地去干件喜欢的事情，还不用处理各种生活琐事，真是太爽了。</p>
<p>但为什么我平时得不到这种感觉？因为，我，是一个没钱的程序员。我和我老婆租在一个标间里。在这样狭小的空间里，多个人就是多几倍干扰。这太残酷了。</p>
<h3>末了</h3>
<p>曾经我以为我很牛逼，曾经我以为程序员很牛逼。后来我慢慢发现自己很垃圾。我没有写出来过牛逼的程序，大概也没能力写。还记得那个程序员的故事吗？就是有个傻逼也以为程序员很牛逼，但不幸在一家非IT公司的IT部门工作，他的程序员同事的工作就是每周填个excel表格。他后来很绝望，因为他没有为世界贡献过任何代码。后来，这货丢下一切，坐上去某地的飞机走了。</p>
 <img src ="http://www.cppblog.com/kevinlynx/aggbug/166394.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2012-02-24 09:22 <a href="http://www.cppblog.com/kevinlynx/archive/2012/02/24/166394.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>逆向思路：破解飞秋群聊协议</title><link>http://www.cppblog.com/kevinlynx/archive/2011/01/23/139187.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 23 Jan 2011 13:01:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2011/01/23/139187.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/139187.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2011/01/23/139187.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/139187.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/139187.html</trackback:ping><description><![CDATA[<p><font size=2></font></p>
<p><strong>题外</strong></p>
<p>飞秋是一款局域网内的IM软件，界面类似QQ，实现上与飞鸽（<a href="http://ipmsg.org/index.html.en" target=_blank>IP message</a>）有点渊源，免费，不开源。</p>
<p>公司大概两年前开始使用这款软件作为员工之间办公吹牛的工具。最近游戏玩得少，就想彻底换到linux下，</p>
<p>组内也有其他两人是llinux-er，不过悲剧的是换到linux下就无法收取飞秋群里的聊天信息了，不免寂寞。</p>
<p>所以，就想写个协议兼容的程序（或者说改个东西）来收取群信息。</p>
<p>&nbsp;</p>
<p><strong>准备</strong></p>
<p>我本身并不擅长逆向工程，破解什么的纯碎业余。在GOOGLE/BAIDU之后发现并没有前人留下的成果。</p>
<p>使用抓包程序，以及综合网络上的信息，还是可以得出不少有用的信息：</p>
<p># 飞秋兼容了飞鸽的协议，其协议格式基本上基于文本形式，各个内容之间使用冒号作为分隔，例如：</p>
<blockquote>
<p>1:100:user:pcname:32:message_body</p>
</blockquote>
<p># 飞秋在私聊情况下的消息内容是没有加密的，但群聊信息加密了，解密这个内容也是我的目标所在</p>
<p># 在抓包软件下根据消息的目标IP地址可以推断飞秋发送群信息是基于UDP组播的，即就算你不是这个群</p>
<p>&nbsp;&nbsp; 的成员，也会收到群消息</p>
<p>有用的文章： <a href="http://www.javaeye.com/topic/810507" target=_blank>《局域网内实现飞鸽欺骗》</a>、<a href="http://www.freeeim.com/f/?695.html" target=_blank>《飞鸽传书数据加密分析》</a>(个人感觉没有任何实质内容，而</p>
<p>且飞鸽传书并不是飞秋，属于误导性文章，但是依然有用）。最重要的，稍微浏览IP message的代码，</p>
<p>以及linux下的iptux（另一个兼容飞鸽的局域网IM）代码，都是对接下来的破解有益的。</p>
<p>&nbsp;</p>
<p><strong>破解</strong></p>
<p>我希望我提供更多的，是一种crack的思路，虽然我不是一个cracker。破解和写程序不太一样，它需要</p>
<p>更多的耐心、运气、程序之外的思考。如前所做的准备，尤其重要。</p>
<p>工具及环境：飞秋2.4版本、OllyDbg，为了方便接收群信息，最好有两台电脑。</p>
<p>&nbsp;</p>
<p><strong>STEP 1 找入手点</strong></p>
<p>在开始面对一大推汇编代码时，我们需要一个最接近目标的点。获取这个点根据目标的不同而不同。例如，</p>
<p>这里主要是针对网络数据的解密。所以，最直接的就是去找这些网络数据。当然，根据具体程序的表现，也</p>
<p>可以从其他点入手，例如飞秋收到群消息后会在任务栏闪烁图标，也可以从这个功能逆向过去。</p>
<p>&nbsp;</p>
<p>因为飞秋使用UDP协议，所以我们可以在recvfrom函数下断点（bp recvfrom）。因为接收UDP包的接口</p>
<p>可能还有WSARecvFrom，甚至winsock1.0中的recvfrom，所以最好都下断点。另一台机器发送群消息，</p>
<p>程序在winsock1.0里的recvfrom断下来：</p>
<p>71A43001 &gt;&nbsp; 8BFF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; edi, edi<br>71A43003&nbsp;&nbsp;&nbsp; 55&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; ebp<br>71A43004&nbsp;&nbsp;&nbsp; 8BEC&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ebp, esp<br>71A43006&nbsp;&nbsp;&nbsp; 51&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; ecx
<p>这个不是我们需要的，我们需要根据这个点获得用户层代码，这将是整个破解过程的起点。所以，OD中</p>
<p>ALT+K查看调用堆栈，然后跳到调用recvfrom的函数处：</p>
<p>00490890&nbsp; /$&nbsp; 56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; esi&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 接收数据的函数入口<br>00490891&nbsp; |.&nbsp; 8B7424 08&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; esi, dword ptr [esp+8]<br>00490895&nbsp; |.&nbsp; 8D46 10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; eax, dword ptr [esi+10]<br>00490898&nbsp; |.&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; /pFromLen<br>00490899&nbsp; |.&nbsp; 56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; esi&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |pFrom<br>0049089A&nbsp; |.&nbsp; C700 10000000 mov&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [eax], 10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |<br>004908A0&nbsp; |.&nbsp; 8B09&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ecx, dword ptr [ecx]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |<br>004908A2&nbsp; |.&nbsp; 6A 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |Flags = 0<br>004908A4&nbsp; |.&nbsp; 8D46 18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; eax, dword ptr [esi+18]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |<br>004908A7&nbsp; |.&nbsp; 68 FF3F0000&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 3FFF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |BufSize = 3FFF (16383.)<br>004908AC&nbsp; |.&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |Buffer<br>004908AD&nbsp; |.&nbsp; 51&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; ecx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |Socket<br>004908AE&nbsp; |.&nbsp; E8 C7F30C00&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; &lt;jmp.&amp;WSOCK32.#17&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; \recvfrom
<p>邪恶的OD已经将调用recvfrom时传入参数的指令标记出来了。中文注释是我分析时加的。recvfrom里传入</p>
<p>的接收缓存，是我们应该关注的。如果能跟进这个缓存，假设程序的流程比较简单：接收了数据，然后在某个</p>
<p>地方直接解密，不管它的加密方式是什么，只要能找到这个缓存数据从加密变为解密的地方，对于整个破解而言，</p>
<p>都算是迈进了一大步。</p>
<p>于是，在00490890（上面找到的函数入口）下断点，准备跟进接收缓存。注意：在OD里调试跟在vc里不一样，</p>
<p>跳到调用堆栈里的某个函数，寄存器依然是当前的值。所以需要重新跟。</p>
<p>&nbsp;</p>
<p><strong>STEP 2 内存断点</strong></p>
<p>F9让程序继续运行，再次在另一台机器上发送群消息。这回程序在00490890处断下，然后跟接收缓存：</p>
<p>004908AC&nbsp; |.&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |Buffer = 0011F4CC<br>004908AD&nbsp; |.&nbsp; 51&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; ecx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; |Socket<br>004908AE&nbsp; |.&nbsp; E8 C7F30C00&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; &lt;jmp.&amp;WSOCK32.#17&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; \recvfrom
<p>接收缓存Buffer的值为0011F4CC，如前所述，我们要跟进这个地址指向的内存的变化情况。F8单步运行到</p>
<p>recvfrom后，也就是接收了网络数据后，查看内存内容</p>
<p>(d 0011F4CC)：</p>
<p>0011F4CC&nbsp; 31 5F 6C 62 74 34 5F 30 23 31 32 38 23 38 38 41&nbsp; 1_lbt4_0#128#88A<br>0011F4DC&nbsp; 45 31 44 44 34 36 36 46 44 23 30 23 30 23 37 32&nbsp; E1DD466FD#0#0#72<br>0011F4EC&nbsp; 3A 31 32 39 35 37 32 31 32 31 33 3A 41 64 6D 69&nbsp; :1295721213:Admi<br>0011F4FC&nbsp; 6E 69 73 74 72 61 74 6F 72 3A 50 43 2D 32 30 31&nbsp; nistrator:PC-201<br>0011F50C&nbsp; 30 31 31 30 34 32 30 30 35 3A 34 31 39 34 33 33&nbsp; 011042005:419433<br>0011F51C&nbsp; 39 3A 5E 3B 83 A1 14 6D A4 D2 E3 D8 E8 AB B1 3A&nbsp; 9:^;儭mひ阖璜?<br>0011F52C&nbsp; 5B BC C2 FE E9 DA CB DD 00 BC 59 FC 9D A7 D7 91&nbsp; [悸谒?糦鼭ё
<p>内容开头正是飞秋的协议头，未加密，不过没多大用。根据之前获取的飞秋协议，可知，在0011F51E</p>
<p>处就是聊天内容的密文。</p>
<p>很自然地，为了监视这段内存的变化情况，在该位置下内存访问断点（右击数据区即可看到下断点的选项）。</p>
<p>F9继续走，然后马上断下来：</p>
<p>0049010F&nbsp; |.&nbsp; F3:A5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rep&nbsp;&nbsp;&nbsp;&nbsp; movs dword ptr es:[edi], dword ptr [&gt;<br>00490111&nbsp; |.&nbsp; 8B4A 04&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ecx, dword ptr [edx+4]<br>00490114&nbsp; |.&nbsp; C74424 24 000&gt;mov&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [esp+24], 0<br>0049011C&nbsp; |.&nbsp; 894D 64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp+64], ecx<br>0049011F&nbsp; |.&nbsp; 33C9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xor&nbsp;&nbsp;&nbsp;&nbsp; ecx, ecx
<p>程序到了一个陌生的环境（在这种满世界都是汇编代码的情况下，几乎一不小心就会迷失其中），看了下</p>
<p>附近的代码，没什么可疑。通过下内存访问断点的思路，似乎显得荆棘丛生。</p>
<p>&nbsp;</p>
<p><strong>STEP 3 靠近目标</strong></p>
<p>不妨冷静下来思考，如果没有直接的路，我们可能需要悲惨地大海捞针。在写一个网络程序时，网络底层</p>
<p>收到数据包，无非要么直接进行逻辑处理，要么缓存到一个逻辑处理队列里稍后处理。后者虽然对于程序员</p>
<p>而言是个好方法，但是因为跨了线程，又涉及到队列缓存，对于逆向而言，绝对是悲剧。</p>
<p>&nbsp;</p>
<p>但是如果使用了前者呢？对于一个网络客户端程序而言，也许直接进行逻辑处理才是最KISS的方法。（这种猜测</p>
<p>的破解方式，绝对需要运气。）所以，如果是直接进行处理，那么在接收到网络数据附近，必然就有解密函数。</p>
<p>所以，不妨顺着收包函数附近随意浏览一番。（但不要走进太深的call，不然又迷失了。）</p>
<p>0048FE10&nbsp; /$&nbsp; B8 18400000&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; eax, 4018&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>0048FE15&nbsp; |.&nbsp; E8 560A0C00&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 00550870<br>0048FE1A&nbsp; |.&nbsp; 8D4424 00&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; eax, dword ptr [esp]<br>0048FE1E&nbsp; |.&nbsp; 56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; esi<br>0048FE1F&nbsp; |.&nbsp; 8BF1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; esi, ecx<br>0048FE21&nbsp; |.&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax<br>0048FE22&nbsp; |.&nbsp; E8 690A0000&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 00490890&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 接收网络数据
<p>0048FE10函数调用了刚才发现的收包函数。这个函数在收完数据后，不久就调用了另一个函数：</p>
<p>0048FE3F&nbsp; |.&nbsp; 51&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; ecx<br>0048FE40&nbsp; |.&nbsp; 52&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; edx<br>0048FE41&nbsp; |.&nbsp; 8BCE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ecx, esi<br>0048FE43&nbsp; |.&nbsp; E8 88020000&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 004900D0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 似乎很可疑？
<p>进到004900D0函数，发现这个函数真TMD巨大。随意浏览之，发现OD有这种提示：</p>
<p>00490178&nbsp; |.&nbsp; 68 34FD5E00&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 005EFD34&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; ASCII "_lbt"<br>0049017D&nbsp; |.&nbsp; 8D4C24 14&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; ecx, dword ptr [esp+14]<br>00490181&nbsp; |.&nbsp; 89BC24 544000&gt;mov&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [esp+4054], edi
<p>_lbt，恩，消息头里有这个字符串标识。估计是在做些消息头的逻辑操作。这个函数太长，里面还有若干call，
<p>可谓头大。所以说，代码写得丑，可读性差，对于防破解还是颇有益处的。跳回到0048FE43，发现当前
<p>函数基本完了。
<p>&nbsp;
<p>于是往上看，来到调用这个函数的地方：
<p>0050F647&nbsp; |.&nbsp; E8 C407F8FF&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 0048FE10<br>0050F64C&nbsp; |.&nbsp; BF 01000000&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; edi, 1
<p>回顾下，我们有函数A接收网络数据，有函数B调用A，现在回到了C，来到了调用B的地方0050F647。C函数
<p>也很巨大，直接往下浏览，会发现一些switch语句：
<p>0050F71A&nbsp; |.&nbsp; 81E6 FF000000 and&nbsp;&nbsp;&nbsp;&nbsp; esi, 0FF<br>0050F720&nbsp; |.&nbsp; 8D46 FF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; eax, dword ptr [esi-1]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; Switch (cases 1..D3)<br>0050F723&nbsp; |.&nbsp; 3D D2000000&nbsp;&nbsp; cmp&nbsp;&nbsp;&nbsp;&nbsp; eax, 0D2<br>0050F728&nbsp; |.&nbsp; 0F87 8C000000 ja&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0050F7BA<br>0050F72E&nbsp; |.&nbsp; 33C9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xor&nbsp;&nbsp;&nbsp;&nbsp; ecx, ecx<br>0050F730&nbsp; |.&nbsp; 8A88 60315100 mov&nbsp;&nbsp;&nbsp;&nbsp; cl, byte ptr [eax+513160]<br>0050F736&nbsp; |.&nbsp; FF248D 403051&gt;jmp&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ecx*4+513040]<br>0050F73D&nbsp; |&gt;&nbsp; 8D9424 F40000&gt;lea&nbsp;&nbsp;&nbsp;&nbsp; edx, dword ptr [esp+F4]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; Case 1 of switch 0050F720
<p>往后浏览下这个switch&#8230;case，发现非常之大，这个函数也因此非常巨大。不妨猜测这个是根据不同消息做不同
<p>逻辑处理的地方。这个想法正是给予我们灵感的关键。
<p>&nbsp;
<p>群聊消息必然也有个类型，通过之前OD获取到的网络数据（或者截取网络封包所得），群聊消息的类型为：4194339
<p>（16进制400023H），去掉高位，也就是23H。仔细地对比每一个case，果然发现了一段处理代码：
<p>00512787&nbsp; |&gt; \39AC24 640100&gt;cmp&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [esp+164], ebp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; Case 23 of switch 0050F720<br>0051278E&nbsp; |.&nbsp; 75 07&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jnz&nbsp;&nbsp;&nbsp;&nbsp; short 00512797&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 群聊天处理<br>00512790&nbsp; |.&nbsp; 8BC7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; eax, edi<br>00512792&nbsp; |.&nbsp; E9 8C080000&nbsp;&nbsp; jmp&nbsp;&nbsp;&nbsp;&nbsp; 00513023
<p>这个代码之下的处理也有不少代码。在不涉及到更多细节之前，我们可以大胆地将注意力放在接下来的call上。从这个case</p>
<p>往下，第一个call为：</p>
<p>005127E6&nbsp; |.&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax<br>005127E7&nbsp; |.&nbsp; E8 A4A20000&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 0051CA90&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 非常可疑<br>005127EC&nbsp; |.&nbsp; B8 01000000&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; eax, 1<br>005127F1&nbsp; |.&nbsp; E9 2D080000&nbsp;&nbsp; jmp&nbsp;&nbsp;&nbsp;&nbsp; 00513023
<p>&nbsp;</p>
<p><strong>STEP 4 多尝试</strong></p>
<p>有怀疑，就用事实来证明。果断地在005127E6处下断点。然后发群消息，程序断下来。因为这个函数压入了
<p>eax作为参数，且对ecx做了赋值：
<p>005127E4&nbsp; |.&nbsp; 8BCB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ecx, ebx<br>005127E6&nbsp; |.&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax<br>005127E7&nbsp; |.&nbsp; E8 A4A20000&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 0051CA90&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 非常可疑
<p>在调用一个函数前对ecx做赋值，一般都是C++成员函数调用。eax作为一个参数，非常值得关注，果断查看eax
<p>指向的内存值：
<p>001235C8&nbsp; 41 64 6D 69 6E 69 73 74 72 61 74 6F 72 00 6D 00&nbsp; Administrator.m.<br>001235D8&nbsp; 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00&nbsp; ................<br>001235E8&nbsp; 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00&nbsp; ................<br>001235F8&nbsp; 00 00 50 43 2D 32 30 31 30 31 31 30 34 32 30 30&nbsp; ..PC-20101104200<br>00123608&nbsp; 35 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00&nbsp; 5...............<br>00123618&nbsp; 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00&nbsp; ................<br>00123628&nbsp; 8A 7B 00 00 C0 A8 00 03 09 79 00 00 01 00 00 00&nbsp; 妠..括..y.....<br>00123638&nbsp; 04 00 00 00 00 00 00 00 80 00 00 00 38 38 41 45&nbsp; .......€...88AE<br>00123648&nbsp; 31 44 44 34 36 36 46 44 00 00 00 00 00 00 00 00&nbsp; 1DD466FD........<br>00123658&nbsp; 00 00 00 00 F4 C7 23 00 FD 22 3B 4D 23 00 40 00&nbsp; ....羟#.?;M#.@.<br>00123668&nbsp; 49 00 00 00 36 00 00 00 5E 3B 83 A1 14 6D A4 D2&nbsp; I...6...^;儭mひ
<p>有用户名、机器名、发送者MAC地址，这么多可疑信息。完全可以猜测，eax传入的是一个结构体地址，</p>
<p>当然对象地址也可以，反正是个复杂数据结构。更重要的，在这块内存往下不远处，果断地发现了从</p>
<p>网络接收到的加密的聊天内容：</p>
<p>00123670&nbsp; 5E 3B 83 A1 14 6D A4 D2 E3 D8 E8 AB B1 3A 5B BC&nbsp; ^;儭mひ阖璜?[<br>00123680&nbsp; C2 FE E9 DA CB DD 00 BC 59 FC 9D A7 D7 91 CF 5A&nbsp; 漫橼溯.糦鼭ё懴Z
<p>F8直接步过0051CA90函数。任务栏开始出现闪烁的图标（虽然没有闪），上面的内存数据变了：
<p>00123670&nbsp; 74 65 73 74 7B 2F 66 6F 6E 74 3B 2D 31 34 20 30&nbsp; test{/font;-14 0<br>00123680&nbsp; 20 30 20 30 20 34 30 30 20 30 20 30 20 30 20 31&nbsp;&nbsp; 0 0 400 0 0 0 1<br>00123690&nbsp; 33 34 20 33 20 32 20 31 20 32 20 CB CE CC E5 20&nbsp; 34 3 2 1 2 宋体
<p>test正是我发的内容。</p>
<p>&nbsp;</p>
<p><strong>STEP 5 缩小范围</strong></p>
<p>实际上走到这里，凭借运气和程序编写的常识，已经找到关键点。不过看来0051CA90这个函数做的事情
<p>有点多，除了解密内容似乎还有UI上的一些处理（例如那个闪烁的任务栏图标）。所以，我们要做的是，进一步
<p>跟进，找到关键点。
<p>&nbsp;
<p>现在缩小范围要容易得多，因为我们得到了一个会变化的内存地址：00123670。只需要F8一步一步地
<p>运行代码，一旦发现内存内容改变，则可以进一步进如，从而找到关键call。具体过程我就不给了，大概为：
<p>0051CB02&nbsp; |.&nbsp; 52&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; edx<br>0051CB03&nbsp; |.&nbsp; 68 00400000&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 4000<br>0051CB08&nbsp; |.&nbsp; 56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; esi<br>0051CB09&nbsp; |.&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax<br>0051CB0A&nbsp; |.&nbsp; 56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; esi<br>0051CB0B&nbsp; |.&nbsp; E8 F041F7FF&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 00490D00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 跟进</p>
<p>&nbsp;</p>
<p>00490DB0&nbsp; |.&nbsp; 6A 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 0<br>00490DB2&nbsp; |.&nbsp; 83E1 03&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and&nbsp;&nbsp;&nbsp;&nbsp; ecx, 3<br>00490DB5&nbsp; |.&nbsp; 6A 22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 22<br>00490DB7&nbsp; |.&nbsp; F3:A4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rep&nbsp;&nbsp;&nbsp;&nbsp; movs byte ptr es:[edi], byte ptr [es&gt;<br>00490DB9&nbsp; |.&nbsp; 8BBC24 344000&gt;mov&nbsp;&nbsp;&nbsp;&nbsp; edi, dword ptr [esp+4034]<br>00490DC0&nbsp; |.&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 数据长度<br>00490DC1&nbsp; |.&nbsp; 8D4424 20&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; eax, dword ptr [esp+20]<br>00490DC5&nbsp; |.&nbsp; 57&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; edi&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 输出缓存<br>00490DC6&nbsp; |.&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 输入缓存（加密内容）<br>00490DC7&nbsp; |.&nbsp; 8D4C24 20&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; ecx, dword ptr [esp+20]<br>00490DCB&nbsp; |.&nbsp; E8 5049F7FF&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 00405720&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 最终解密函数
<p>&nbsp;</p>
<p>00405720函数内的实现基本上全是数据操作指令。加解密算法无非就是捣鼓这些数据，所以当我进到</p>
<p>00405720函数时，基本上可以断定它就是最终的解密函数。</p>
<p>&nbsp;</p>
<p><strong>STEP 6 解密</strong></p>
<p>事实上我们并不需要去弄懂它的具体解密算法，就算是直接的C++代码，没有算法论文的话也很难看懂，更何况</p>
<p>是这里的汇编。最直接的方式，就是查看这个解密函数对外界的依赖情况，例如需要的参数，this里是否有依赖</p>
<p>的数据。在了解了这些情况后，大可以将这段汇编复制出来直接作为C++内嵌汇编代码使用。</p>
<p>&nbsp;</p>
<p>不过，这里我想到了更简单的方式。因为我注意到飞秋和飞鸽在实现上有着不解之缘，而且我琢磨着作者也不会</p>
<p>为了一个加解密不太重要的应用场合而去整些高深的算法。我想到，飞秋也许会直接使用飞鸽里的加解密代码！</p>
<p>&nbsp;</p>
<p>在IP message的源码里，有blowfish加密算法的实现，我们来看接口：</p>
<p>class CBlowFish<br>{<br>private:<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp; *PArray;<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp; (*SBoxes)[256];<br>&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp; Blowfish_encipher(DWORD *xl, DWORD *xr);<br>&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp; Blowfish_decipher(DWORD *xl, DWORD *xr);
<p>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CBlowFish(const BYTE *key=NULL, int keybytes=0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ~CBlowFish();<br>&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp; Initialize(const BYTE *key, int keybytes);<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp; GetOutputLength(DWORD lInputLong, int mode);<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp; Encrypt(const BYTE *pInput, BYTE *pOutput, DWORD lSize, int mode=BF_CBC|BF_PKCS5, _int64 IV=0);<br>&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp; Decrypt(const BYTE *pInput, BYTE *pOutput, DWORD lSize, int mode=BF_CBC|BF_PKCS5, _int64 IV=0);<br>};
<p>从接口实现来说算是简洁漂亮友好和谐。我也用Decrypt这个函数的参数比对了上面没找到的那个call（00405720），</p>
<p>因为这里只是怀疑这个call就是这里的Decrypt，但并没有确切的证据。不过，对比下他们的参数就可以非常肯定了：</p>
00490DB0&nbsp; |.&nbsp; 6A 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;参数IV<br>00490DB2&nbsp; |.&nbsp; 83E1 03&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; and&nbsp;&nbsp;&nbsp;&nbsp; ecx, 3<br>00490DB5&nbsp; |.&nbsp; 6A 22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;参数mode<br>00490DB7&nbsp; |.&nbsp; F3:A4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rep&nbsp;&nbsp;&nbsp;&nbsp; movs byte ptr es:[edi], byte ptr [es&gt;<br>00490DB9&nbsp; |.&nbsp; 8BBC24 344000&gt;mov&nbsp;&nbsp;&nbsp;&nbsp; edi, dword ptr [esp+4034]<br>00490DC0&nbsp; |.&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 参数 数据长度<br>00490DC1&nbsp; |.&nbsp; 8D4424 20&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; eax, dword ptr [esp+20]<br>00490DC5&nbsp; |.&nbsp; 57&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; edi&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 参数输出缓存<br>00490DC6&nbsp; |.&nbsp; 50&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; eax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 参数输入缓存（加密内容）<br>00490DC7&nbsp; |.&nbsp; 8D4C24 20&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; ecx, dword ptr [esp+20]<br>00490DCB&nbsp; |.&nbsp; E8 5049F7FF&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 00405720&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 最终解密函数
<p>最重要的，是参数mode。Decrypt默认参数mode是BF_CBC|BF_PKCS5的位组合，结果，恰好为22！</p>
<p>所以，基本上可以断定：飞秋的加解密实现，就是使用了IP message的blowfish算法：blowfish.cpp/h/h2。</p>
<p>&nbsp;</p>
<p><strong>STEP 7 密钥</strong></p>
<p>查看CBlowFish的使用，在解密前需要初始化，大概就是传入密钥之类。如果我们上面的猜测没有错，那么我们</p>
<p>从网络上取得的数据，然后取得密钥，直接使用blowfish的源码，就可以解密出消息内容。</p>
<p>&nbsp;</p>
<p>接下来的关键就是，找到这个密钥。关于这个密钥，之前在飞秋的配置文件FeiqCfg.xml里绕了很久的圈子，因为</p>
<p>发现加入一个群的时候，这个文件里就会多出一项很长的16进制字符串。也一度猜测密钥就是保存在这个字符串的</p>
<p>某个偏移里。接下来会让人大跌眼镜。</p>
<p>&nbsp;</p>
<p>因为CBlowFish这个类确实简单，使用它的最简单方式就是直接创建局部对象，然后传入key和keysize，即可完成</p>
<p>初始化。在之前展示的思路里，我也一度先去尝试最简单的方法。对于C++局部对象的创建，有个显著特征就是</p>
<p>mov ecx, dword ptr [esp+xxx]，也就是往ecx里写入一个栈地址。而且可以肯定的是，这个构造代码，必然发生</p>
<p>于call 00405720前面不远处，往上跟进：</p>
<p>00490D3F&nbsp; |&gt; \8B8C24 304000&gt;mov&nbsp;&nbsp;&nbsp;&nbsp; ecx, dword ptr [esp+4030]<br>00490D46&nbsp; |&gt;&nbsp; 51&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; ecx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>00490D47&nbsp; |.&nbsp; 52&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; edx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>00490D48&nbsp; |.&nbsp; 8D4C24 0C&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; ecx, dword ptr [esp+C]<br>00490D4C&nbsp; |.&nbsp; E8 3F3DF7FF&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 00404A90&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>一个压入两个参数的函数调用，对比CBlowFish的构造函数，刚好是2个参数。跟进00404A90：
<p>&nbsp;
<p>00404A90&nbsp; /$&nbsp; 56&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; esi<br>00404A91&nbsp; |.&nbsp; 8BF1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; esi, ecx<br>00404A93&nbsp; |.&nbsp; 6A 48&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 48<br>00404A95&nbsp; |.&nbsp; E8 69301600&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 00567B03<br>00404A9A&nbsp; |.&nbsp; 68 00100000&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 1000<br>00404A9F&nbsp; |.&nbsp; 8906&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [esi], eax<br>00404AA1&nbsp; |.&nbsp; E8 5D301600&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 00567B03
<p>&nbsp;</p>
<p>又是可爱的立即数！48H、1000H，这种特别的立即数总能让人安心，对比CBlowFish构造函数实现：</p>
<p>CBlowFish::CBlowFish (const BYTE *key, int keybytes)<br>{<br>&nbsp;&nbsp;&nbsp; PArray = new DWORD [NPASS + 2];//NPASS=16<br>&nbsp;&nbsp;&nbsp; SBoxes = new DWORD [4][256];
<p>&nbsp;&nbsp;&nbsp; if (key)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Initialize(key, keybytes);<br>}
<p>sizeof(DWORD)*18=48H，sizeof(DWORD)*4*256=1000H！这么极具喜剧意义的汇编C++代码映射，</p>
<p>基本可以肯定，00404AA1处，正是构造CBlowFish对象的地方，而构造的参数，正是我们魂牵梦萦的解密密钥：</p>
<p>&nbsp;</p>
<p>00490D46&nbsp; |&gt; \51&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; ecx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; key长度<br>00490D47&nbsp; |.&nbsp; 52&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; edx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 密钥key<br>00490D48&nbsp; |.&nbsp; 8D4C24 0C&nbsp;&nbsp;&nbsp;&nbsp; lea&nbsp;&nbsp;&nbsp;&nbsp; ecx, dword ptr [esp+C]<br>00490D4C&nbsp; |.&nbsp; E8 3F3DF7FF&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp; 00404A90&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; 构造blowfish对象
<p>&nbsp;</p>
<p>在此处下断点，发送群消息，程序断下来，看看密钥究竟是什么。如果它确实是FeiqCfg.xml里的某个值，</p>
<p>那么我们还要进一步跟这个值具体在哪个配置项里。看看吧，密钥edx：</p>
<p>&nbsp;</p>
<blockquote>
<p>edx=00123644, (ASCII "88AE1DD466FD")</p>
</blockquote>
<p>&#160;</p>
<p>&#160;</p>
<p>TM的密钥居然是发送者的MAC地址！当我看到这个的时候我几乎快摔倒地上。如果飞秋使用一个MAC地址</p>
<p>作为密钥，那么这意味着：通过自己写的程序，可以取得局域网内其他群里的聊天内容！这个实在太邪恶了。</p>
<p>上回抓包的时候，虽然看不到内容，但可以看到美术、策划在群里聊的无比欢乐。这回有喜感了。</p>
<p>&nbsp;</p>
<p><strong>STEP END 可略</strong></p>
<p>看看时间，悲剧地发现整篇文章花了接近3个小时才写完。此刻我正踌躇要不要把代码发上来，但转念一想</p>
<p>最后那个STEP的发现实在让人蛋疼，所以还是算了。打算稍微封装下，然后使用这份代码在iptux 上改改包装</p>
<p>个界面，目的就算达成了。相信浏览完整篇文章，写出自己的代码不是什么大问题。</p>
<p>&nbsp;</p>
<p>其实我大可以直接给结论，但是我依然乐于分享过程和思路。一来算是自我总结记录（每次拿起OD，总是快捷</p>
<p>键一路忘）；二来也给有心人一个指引。</p>
<p>&nbsp;</p>
<p>最后，对这种东西还是有必要出个免责声明：根据本文章所造成的一切后果与文章作者无关。为了不糟蹋我这3个</p>
<p>小时的时间，转载麻烦注明出处。</p>
<p>PS，最后回顾下结论，其实发现这个解密非常非常简单。你说如果直接给卢本陶（飞秋作者）发封邮件，他会不</p>
<p>会直接告诉我？</p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/139187.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2011-01-23 21:01 <a href="http://www.cppblog.com/kevinlynx/archive/2011/01/23/139187.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>静态库中全局变量的初始化问题</title><link>http://www.cppblog.com/kevinlynx/archive/2010/01/17/105885.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 17 Jan 2010 11:34:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2010/01/17/105885.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/105885.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2010/01/17/105885.html#Feedback</comments><slash:comments>16</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/105885.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/105885.html</trackback:ping><description><![CDATA[<p><font size="2"></font>&nbsp; <p><font size="2">在我自己写的一个工厂类实现中，每个产品会注册创建接口到这个工厂类。工厂类使用这些<br>注册进来的创建接口来完成产品的创建。其结构大致如下： </font> <p><font size="2">product *factory::create( long product_type )<br>{<br>&nbsp;&nbsp;&nbsp; creator c = m_creators[product_type];<br>&nbsp;&nbsp;&nbsp; return c();<br>} </font> <p><font size="2">factory::instance().register( PRODUCT_A_TYPE, productA::create );<br>...<br>factory::instance().create( PRODUCT_A_TYPE ); </font> <p><font size="2">这个很普通的工厂实现中，需要写上很多注册代码。每次添加新的产品种类时，也需要修改<br>这些的注册代码。而恰好，这些注册代码可能会被放在一个统一的地方。为了消除这个地方<br>，我使用了偶然间看到的&lt;Modern C++ design&gt;里的做法： </font> <p><font size="2">const bool _local = factory::instance().register( PRODUCT_A_TYPE,... </font> <p><font size="2">也就是说，通过对全局常量_local的自动初始化，来自动完成对该产品的注册。 </font> <p><font size="2">结果，因为这些代码全部被放置于一个静态库。最终的代码文件结构大致为： </font> <p><font size="2">lib<br>&nbsp;&nbsp;&nbsp; - product_a.cpp : 定义了全局常量_local<br>&nbsp;&nbsp;&nbsp; - product_a.h<br>&nbsp;&nbsp;&nbsp; - factory.cpp<br>&nbsp;&nbsp;&nbsp; - factory.h<br>exe<br>&nbsp;&nbsp;&nbsp; - main.cpp </font> <p><font size="2">现在看起来世界很美，因为factory甚至不知道世界上还有个跟上层逻辑相关的product_a。<br>这种模块耦合几乎为0的结构让我窃喜。 </font> <p><font size="2">悲剧的事情首先发生于，开VC调试器，发现打在product_a.cpp里的断点失效。就是那个总<br>是提示说没有为该文件加载调试符号。开始还不在意，以为又是代码和调试符号文件不匹配<br>的原因，折腾了好久，不得其果。 </font> <p><font size="2">后来分析了下，发现这个调试提示，就像我开着调试器打开了一个非本工程的代码文件，而<br>断点就打在这个文件里一样。也就是说，VC把我product_a.cpp当成不是这个工程里的代码<br>文件。 </font> <p><font size="2">按照这个思路写些实验代码，最终发现问题所在：VC链接器根本没链接进product_a.cpp里<br>的代码。表现出来的情况就是，该编译单元里的全局常量（全局变量一样）根本没有得到初<br>始化，因为我跟到factory::register并没有被调用到。为什么VC不链接这个编译单元对应<br>的目标文件？或者说，为什么VC不初始化这个全局常量？ </font> <p><font size="2">原因就在于，product_a.cpp太独立了。一个在整个编译链接阶段都无法确定该文件是否被<br>使用的文件，VC就直接不链接了。相反，当在factory.cpp里写下类似代码： </font> <p><font size="2">void test()<br>{<br>&nbsp;&nbsp;&nbsp; product_a obj;<br>} </font> <p><font size="2">虽然说test函数不会被调用，一切情况也变得正常了。好了，不扯了，给最后我的结论： </font> <p><font size="2">1、如果静态库中某个编译单元在编译阶段被确认为它并没有被外部使用，那么当这个静态<br>库被链接进可执行文件时，链接器忽略掉该编译单元里的代码，那么，链接器自然也不会为<br>该编译单元里出现的全局变量常量生成初始化代码（关于这部分初始化代码可以阅读<br>&lt;linker and loader&gt;一书）；<br>2、上面那条结论存在一种传染性，意思是，当可执行文件里的代码使用到静态库中文件A里<br>的代码，A里又有地方使用到B里的代码，那么B依然会被链接。这种依赖性，应该可以让编<br>译器在编译阶段就发现（显然，上面我举的例子里，factory只有在运行期间才会依赖到<br>product_a.cpp里的代码） </font></p><img src ="http://www.cppblog.com/kevinlynx/aggbug/105885.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2010-01-17 19:34 <a href="http://www.cppblog.com/kevinlynx/archive/2010/01/17/105885.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>强大的bcb</title><link>http://www.cppblog.com/kevinlynx/archive/2009/08/15/93396.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sat, 15 Aug 2009 03:17:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2009/08/15/93396.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/93396.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2009/08/15/93396.html#Feedback</comments><slash:comments>17</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/93396.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/93396.html</trackback:ping><description><![CDATA[<p><font size="2">早就听说bcb(borland c++ builder)是一个强大的RAD开发工具，也早就听说曾经的borland搞出的编译器堪称经典。</font></p> <p><font size="2">恰好最近在做一个GUI工具，想在界面开发上尽量快一点。每一次用上MFC都让我觉得浑身难受，总有些常用的</font></p> <p><font size="2">界面功能它就是没有。在接口实现上，MFC基本上就只是封装了WIN API而已。想想世界上还有什么强大的GUI库，</font></p> <p><font size="2">找了一下，其实不管GUI库封装的怎么样，我更多地还是需要一个工具，能够快速地堆积出界面。</font></p> <p><font size="2"></font>&nbsp;</p> <p><font size="2">于是，在网上下载了被国人精简了的bcb2009。然后，噩梦开始了。首先，我需要把逻辑层代码（也就是实现具体</font></p> <p><font size="2">功能的那一层）移植到BCB下。然后得到了很多和语法相关的编译错误：</font></p> <p><font size="2"></font>&nbsp;</p> <p><font size="2">1.</font><br>E2397: Template argument cannot have static or local linkage</p> <p>这个错误发生于：</p> <p>void func()<br>{</p> <p>&nbsp;&nbsp;&nbsp; struct Info</p> <p>&nbsp;&nbsp;&nbsp; {</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p> <p>&nbsp;&nbsp;&nbsp; };</p> <p>&nbsp;&nbsp; std::queue&lt;Info&gt; abc;<br>}</p> <p>它的意思是，模板参数必须是全局链接的，总之它不允许std::queue的参数是一个在函数内部临时定义</p> <p>的类型（谁来告诉我这是C++标准）。</p> <p>&nbsp;</p> <p>2.</p> <p>E2357 Reference initialized with 'FileLoader::RawData', needs lvalue of type 'FileLoader::RawData'</p> <p>这个错误发生于：</p> <p>FileLoader::RawData FileLoader::GetRawData() const;</p> <p>FileLoader::RawData &amp;raw = loader.GetRawData(); //不能用引用</p> <p>很久没看C++书，所以，谁又来告诉我C++标准里，这里到底能不能用引用？</p> <p>&nbsp;</p> <p>3.</p> <p>E2515 Cannot explicitly specialize a member of a generic template class</p> <p>这个错误发生的情景更复杂些：</p> <p>template &lt;typename _Tp&gt;</p> <p>class Test<br>{</p> <p>&nbsp;&nbsp; template &lt;typename _U&gt;</p> <p>&nbsp;&nbsp; class Other;</p> <p>&nbsp;&nbsp; template &lt;&gt;</p> <p>&nbsp;&nbsp; class Other&lt;void&gt;</p> <p>&nbsp;&nbsp; {</p> <p>&nbsp;&nbsp; };<br>};</p> <p>意思是说，我不能在一个模板类里特化成员模板类。谁又来告诉我标准规定的是什么？</p> <p>&nbsp;</p> <p>4.</p> <p>void func( Obj &amp;a )<br>{<br>}</p> <p>func( Obj() );这个也被视为错误。必须得在调用func之前自己定义个临时变量。</p> <p>&nbsp;</p> <p>5.</p> <p>我曾经留下了关于<a href="http://www.cppblog.com/kevinlynx/archive/2008/08/20/59451.html" target="_blank">宏递归</a>的一些代码，被用在我写的<a href="http://www.cppblog.com/kevinlynx/archive/2008/08/13/58684.html" target="_blank">lua-binder和lua-caller</a>中自动生成代码。这下好了，</p> <p>BCB开始警告我，我的这些宏不能工作了。它和MSVC在某些事情上分歧可真是大：</p> <p>#define PARAM( n ) ,typename P##n //注意这个宏包含一个逗号</p> <p>#define CHR( x, y ) CHR1( x, y )</p> <p>#define CHR1( x, y ) x##y</p> <p>#define BCB_ERROR( a, b ) CHR( a, b )</p> <p>BCB_ERROR( 1, PARAM( 1 ) ) 当这样使用宏时，基于我在GNU C上看到的关于宏的规则，会先展开</p> <p>PARAM(1)，于是得到BCB_ERROR( 1, ,typename P2 )。然后，BCB认为PARAM(1)展开的逗号需要参与</p> <p>BCB_ERROR的展开了。于是，我的整个宏库无法工作了。</p> <p>关于这个问题，我直接用MSVC写了个生成器，让MSVC替我生成各种参数的lua-binder和lua-caller，然后</p> <p>写成外部头文件，最后直接在BCB里包含了这些头文件。从而使我的lua-binder和lua-caller可以继续使用。</p> <p>&nbsp;</p> <p>然后，我的1W多行代码终于在BCB下50多个WARNINGS的提示下编译成功了。怀揣着兴奋的心情，想自己终</p> <p>于可以rapid开发界面了。创建了个VCL FORM APPLICATION，噩梦又开始了：</p> <p>&nbsp;</p> <p>1.</p> <p>BCB莫名其妙地在我编译一个CPP文件时给出如下提示：</p> <p>F1004 Internal compiler error at 0x59b4ea8 with base 0x5980000</p> <p>看起来像是BCB的编译器给崩溃了。囧。google了一下，发现不是我人品问题，很多人遇到相同的问题。</p> <p>别人给出的解决方案是：restart your bcb。从昨天晚上到现在为止，这个错误发生了好几次。</p> <p>&nbsp;</p> <p>2.</p> <p>new std::ofstream();会让程序崩溃，往不该写的地方写了东西。我就奇怪了，你BCB自己带的C++IO实现，</p> <p>难道还有BUG？再次google，还真发现是BCB自己的BUG，并且在几个版本之前就存在这个BUG。那个天真</p> <p>的老外还说希望在BCB2009下能被修复。修改方案如下：</p> <p>1）xlocale文件里把这句话注释了：*(size_t *)&amp;table_size = 1 &lt;&lt; CHAR_BIT;</p> <p>2）xlocale里把成员_Id_cnt访问属性改为public，然后在自己的文件里定义一次。</p> <p>&nbsp;</p> <p>3.</p> <p>程序终于可以运行了。但是BCB的IDE环境总是不那么贴心。我移动了几个窗口改成我习惯的样子，但是一重启</p> <p>居然又恢复成default（难道是因为盗版）。它的智能提示似乎总是跟着鼠标指针，有时候指向某个符号，鼠标</p> <p>就显示忙。为了提示某个类的成员，某个函数的原型，BCB偶尔都会卡一下。其实我不介意我的编辑器没有这</p> <p>些提示功能，在MSVC下我也从不用VA来帮我写代码。我甚至不厌其烦地在VIM下敲代码切窗口去看函数原型，</p> <p>但是，你他妈作为一个IDE就得像个IDE的样子，要不，你干脆关掉所有功能，别给我卡就行了。</p> <p>&nbsp;</p> <p>这个时候我开始怀疑选择BCB会不会是一个错误的开始，或者说在使用某个东西时，总会带着使用其他同类东西</p> <p>的感觉甚至偏见去看待这个新事物。但是，在我想坚持继续使用BCB时，我一compile，它又提示我：</p> <p><font size="3">F1004 Internal compiler error at 0x59b4ea8 with base 0x5980000</font></p> <p><font size="2"></font></p><img src ="http://www.cppblog.com/kevinlynx/aggbug/93396.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2009-08-15 11:17 <a href="http://www.cppblog.com/kevinlynx/archive/2009/08/15/93396.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针和模块健壮</title><link>http://www.cppblog.com/kevinlynx/archive/2009/06/28/88711.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 28 Jun 2009 11:35:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2009/06/28/88711.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/88711.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2009/06/28/88711.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/88711.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/88711.html</trackback:ping><description><![CDATA[<p><font size=2>仔细想想能导致一个C++程序崩溃的几乎90%原因都是跟指针有关。空指针野指针，一不小心<br>程序就崩了。写C++程序的人基本上都知道这个问题。在我们周围避免这些问题的常规方法<br>也很多，诸如auto_ptr（及其他基于template的原始指针wrapper）、SAFE_DELETE。当然也<br>会有很多人在实现一个函数时会很勤劳地对每一个parameter进行合法判断。 </font>
<p><font size=2>其实，我们都知道，auto_ptr这些东西始终是无法避免野指针和空指针带来的灾难。<br>SAFE_DELETE也不能阻止别人使用这个空指针。 </font>
<p><font size=2>在我看过的一些开源项目的代码中，这些代码给人的感觉就是别人总能详细地掌控各种资源<br>（包括指针及其他变量）的使用情况。相比之下，公司隔壁组的老大则显得保守很多。他要<br>求我们几乎要对所有指针的使用进行空值判断（野指针也判断不了），当然，各种成员变量<br>也要进行即使现在看上去没多大用的初始化。 </font>
<p><font size=2>也许，这样做后程序是不会挂掉了。但是，就我们的观点来看，这样反而会隐藏一些BUG。<br>为什么我们不能详尽地去管理一个指针？一个指针变为空了，总是因为在这之前发生了错误<br>。当然，野指针本身就是愚蠢代码产生的东西，这里没必要讨论。空指针之所以为空，也是<br>因为在很多时候我们把空作为失败/错误/无效的标志。 </font>
<p><font size=2>恰好上周我的一些代码就真的在空指针上出现了问题。外网的服务器随时会因为玩家的一些<br>临界操作行为而崩溃掉。虽然我通过修改脚本来屏蔽这个问题（因为不能说停机维护就停机<br>维护），但是总感觉程序是不安全的。人不吃点教训绝对不学乖。 </font>
<p><font size=2>后来我对这个问题彻底思考了一下。很多程序员都自认聪明。在写C++程序时，我从来不提<br>供没用的public接口，尤其是set/get。我也从来不对没必要的成员变量进行初始化。我给<br>的理由是对于这些东西我都有很清晰的把握，我为什么要做stupid的事情？ </font>
<p><font size=2>但是，我几乎从来没有界定，指针在哪些情况下需要去判断为空？函数的参数绝对不需要。<br>假如函数的参数就是个空指针，那是client程序员的责任。仅供模块内使用的指针（包含其<br>他资源）在内部使用时也不需要去判断。如果去判断了，那说明你对你自己写的模块都缺乏<br>精确的把握，证明你的设计思维不够清晰。 </font>
<p><font size=2>什么时候需要判断？当指针依赖于外部环境时，例如读配置文件、载入资源，因为外部因素<br>不确定不在自己控制范围内，那么进行判断。同样，当使用了其他模块返回的指针值时，也<br>需要判断。这个其实和&#8220;外部环境&#8221;属于同一种情况。因为我们对其他模块也不清楚，更为<br>隐蔽的是（随着其他模块的改变，将来会在你的模块里爆发崩溃错误），其他模块由别人维<br>护，其变化更不受自己控制。之前我对这一点界定不是很清楚，这也是我犯错的原因。 </font>
<p><font size=2>现在想想，像游戏服务器这种程序，里面塞着各种各样的游戏功能。无论是哪一个模块出现<br>个空指针访问出错的问题，都会直接让服务器崩掉。关键是这个结果经常伴随着玩家的损失<br>。所以理想状态下，把每一个模块都放置在单独的进程里，确实是很有好处的。 </font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/88711.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2009-06-28 19:35 <a href="http://www.cppblog.com/kevinlynx/archive/2009/06/28/88711.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GDI+中从内存读取图片/保存图片到内存</title><link>http://www.cppblog.com/kevinlynx/archive/2009/05/28/86040.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 28 May 2009 12:23:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2009/05/28/86040.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/86040.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2009/05/28/86040.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/86040.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/86040.html</trackback:ping><description><![CDATA[<p><font size=2>要给项目中增加一个新的模块，需要先在服务器端做一些图片处理相关的工作。本来，对图片</font></p>
<p><font size=2>做一些诸如ALPHA混合旋转缩放的操作，在游戏客户端应该是很容易的事。但是这事要在服务</font></p>
<p><font size=2>器做，就不得不引入一些第三方库。反正我们的服务器运行于WINDOWS下，这里又需要处理</font></p>
<p><font size=2>JPG图片的加载，我就考虑到了GDI+。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>在这之前对GDI+没有过任何接触。直接翻了MSDN，还好居然有个一系列的usage。GDI+的Image</font></p>
<p><font size=2>本身支持JPG的直接载入。但是并没有我理想中的CreateFromMemory( const void *buf )接口。</font></p>
<p><font size=2>看起来唯一可以从内存创建Gdiplus::Image对象的方法是从一个叫IStream*的COM东西。我揣摩</font></p>
<p><font size=2>微软为什么没有提供我理想中的那个接口，或者说要把GDI+设计成这样，可能还是考虑到对多语</font></p>
<p><font size=2>言的支</font><font size=2>持。于是问题转换为如何将一个C语言的const void*转换为IStream*。我甚至在开始的时候</font></p>
<p><font size=2>感觉</font><font size=2>到是不是要自己实现个Stream。后来在google上找到了一个似乎是标准的方法：首先创建个</font></p>
<p><font size=2>HGLOBAL对象，然后通过GlobalLock就可以将一个C的const void*直接memcpy到这个HGLOBAL</font></p>
<p><font size=2>里，最后，通过CreateStreamOnHGlobal这样的接口就可以得到一个IStream。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>恶心的是，基于之前对服务器内存使用的优化，我现在对于内存的使用非常敏感（谁说现在内存</font></p>
<p><font size=2>大了就可以任意malloc了？？）。上面那个过程对于资源的管理在MSDN文档中似乎显得有点</font></p>
<p><font size=2>模糊。CreateStreamOnHGlobal函数的第二个参数指定当IStream-&gt;Release的时候，是否会自动</font></p>
<p><font size=2>删除这个HGLOBAL对象。我虽然对COM不懂，但也知道它的对象是基于一种引用计数的管理方式。</font></p>
<p><font size=2>逐字看了下文档，发现一个final单词，原来是IStream-&gt;Release最后一次释放时，会同时释放掉</font></p>
<p><font size=2>这个HGLOBAL对象。更让人发指的是，我猜测Image( IStream * )来创建Image时，Image又</font></p>
<p><font size=2>会对这个IStream进行一次AddRef。我发觉MSDN对于Gdiplus::Image::FromStream函数的说明</font></p>
<p><font size=2>也有点模糊。我揣摩使用FromStream获得的Image*，是否需要手动去delete？这个地方的内存</font></p>
<p><font size=2>资源管理，一定得搞个水落石出。结果是，FromStream的实现就是简单地new了个Image。而</font></p>
<p><font size=2>Image内部肯定会对IStream进行AddRef，并且，如果在Image销毁前销毁这个HGLOBAL，这个</font></p>
<p><font size=2>Image基本也就废了。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>也就是说，Image本身不对HGLOBAL中的图片数据进行复制。囧。别想让我再写个wrap class把</font></p>
<p><font size=2>HGLOBAL和Image纠结在一起，简单考虑，将CreateStreamOnHGlobal第二个参数设为TRUE。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>要将一个Image保存为一段内存，也比较麻烦。我的方法和google上的相同。当然，微软的库依</font></p>
<p><font size=2>然让我在很多细节上栽跟斗（如前所说，可能这是基于多语言支持的考虑）。首先需要创建个空</font></p>
<p><font size=2>的IStream，即CreateStreamOnHGlobal第一个参数为NULL。然后将Image Save到这个IStream。</font></p>
<p><font size=2>再根据该IStream::Seek获取其大小，自己再分配段内存，最后IStream::Read读取进来。同样，</font></p>
<p><font size=2>需要注意相关内存资源的管理。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>下午简单把以上两个过程简单封装了下。</font></p>
<a href="http://www.cppblog.com/Files/kevinlynx/mem_image.zip" target=_blank>下载代码。</a>
<p><font size=2></font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/86040.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2009-05-28 20:23 <a href="http://www.cppblog.com/kevinlynx/archive/2009/05/28/86040.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DNF游戏声音资源提取</title><link>http://www.cppblog.com/kevinlynx/archive/2009/05/12/82740.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 12 May 2009 14:50:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2009/05/12/82740.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/82740.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2009/05/12/82740.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/82740.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/82740.html</trackback:ping><description><![CDATA[<p><font size="2">&nbsp;&nbsp;&nbsp; 上午公司断网，晚上失眠头痛没精神，于是随便打开了DNF游戏目录下的资源文件。以<br>前一直对提取游戏资源存在好奇，需要对一些关键字节猜测其加密方式。<br>&nbsp;&nbsp;&nbsp; DNF游戏目录下soundpacks下的npk文件看起来似乎比较简单，这里直接给出文件格式，<br>懒得写分析思路了。<br>&nbsp;&nbsp;&nbsp; 文件开头的十六个字节是一个固定字符串：NeoplePack_Bill\0。<br>&nbsp;&nbsp;&nbsp; 接下来四个字节表示本npk文件里打包了多少个WAV文件。npk文件是一个包含了很多声<br>音或者图片的打包文件。类似这种打包文件，一般文件头都会保存一个文件列表。而这个列<br>表里又会附加上偏移量和大小等信息。<br>&nbsp;&nbsp;&nbsp; 接下来的数据就是这里所说的列表。每一个列表项包含三个数据域：偏移、大小、文件<br>名。如下示意： </font> <p><font size="2">&nbsp;&nbsp;&nbsp; NeoplPack_Bill\0 (16 bytes)<br>&nbsp;&nbsp;&nbsp; file_count( 4 bytes)<br>&nbsp;&nbsp;&nbsp; item1:offset(4 bytes), size(4 bytes)<br>&nbsp;&nbsp;&nbsp; item2:offset(4 bytes), size(4 bytes)<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; itemn:offset(4 bytes), size(4 bytes)<br>&nbsp;&nbsp;&nbsp; ... </font> <p><font size="2">&nbsp;&nbsp;&nbsp; 文件列表之后，就是具体的每个文件的内容。开始我还在担心npk会为每一个声音文件<br>加密。或者只保存声音文件的具体数据，而声音文件文件头则只保存一份（因为所有文件的<br>文件头很有可能全部是一样的）。后来稍微搜索了下WAV的格式，只需要比对下npk中某一个<br>文件内容的头部是否和WAV格式的头部相同，就可以基本断定其是否加密。<br>&nbsp;&nbsp;&nbsp; 结果是，npk对包内的每一个WAV文件没做加密。<br>&nbsp;&nbsp;&nbsp; 然后立即写了个程序，根据文件列表中的偏移值和大小值，将每一个WAV单独取出来，就<br>OK了。<br>&nbsp;&nbsp;&nbsp; 完整的格式为： </font> <p><font size="2">&nbsp;&nbsp;&nbsp; NeoplPack_Bill\0 (16 bytes)<br>&nbsp;&nbsp;&nbsp; file_count( 4 bytes)<br>&nbsp;&nbsp;&nbsp; item1:offset(4 bytes), size(4 bytes)<br>&nbsp;&nbsp;&nbsp; item2:offset(4 bytes), size(4 bytes)<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; itemn:offset(4 bytes), size(4 bytes)<br>&nbsp;&nbsp;&nbsp; file1<br>&nbsp;&nbsp;&nbsp; file2<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; filen </font> <p><font size="2">&nbsp;&nbsp;&nbsp; 我想图片资源也应该差不多，不过图片资源肯定要复杂些。下午公司网络好了，网上搜<br>索了下，发现居然已经有了DNF资源提取工具了，唉。 </font> <p><font size="2">&nbsp;&nbsp;&nbsp; 提供下<a href="http://www.cppblog.com/Files/kevinlynx/dnfsnd_extractor.zip" target="_blank">源代码和MingW编译好的可执行文件</a>，另声明：本文及相关工具代码只作学习研究<br>用，任何后果与作者无关。</font></p><img src ="http://www.cppblog.com/kevinlynx/aggbug/82740.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2009-05-12 22:50 <a href="http://www.cppblog.com/kevinlynx/archive/2009/05/12/82740.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>玩了一下alienbrain的EventsScript</title><link>http://www.cppblog.com/kevinlynx/archive/2009/02/28/75179.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sat, 28 Feb 2009 08:48:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2009/02/28/75179.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/75179.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2009/02/28/75179.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/75179.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/75179.html</trackback:ping><description><![CDATA[<p><font size=2>从我接触的UNIX文化中我知道，要学会更好地去使用工具去组合工具来完成日常的工作。项目使用alienbrain作为源代码管理工具。每次在VC里有些代码文件里总会写些并不想签入到代码服务器上的代码，例如我经常#include "vld.h"来检测内存泄漏。但是真的等到签入代码时，又经常忘记删除这些代码。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>于是我想，如果每次在我签入代码前，VC或者alienbrain或者其他工具能根据我的设置检查我的代码中是否存在这种不想签入的代码，然后提示我。问了一下向来很熟悉工具的<a href="http://www.yulefox.com/" target=_blank>FOX</a>（很抱歉，我甚至不会使用WORD，我真不敢相信我的坦率:D），结果他也不知道。今天闲来无事翻了下alienbrain附带的文档，发现EventsScript。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>alienbrain里的EventScript总的来说是一种让用户定制更多功能的机制。它定义诸如签入签出获取版本等动作为event，然后用户可以使用alienbrain支持的VBScript和JavaScript脚本来处理这些事件。例如，当你签出一个文件时，ab(alienbrain)会调用你的脚本，让你来处理这一事件）。当脚本返回时，ab继续处理刚才的操作（如果你实在想中止这一动作也可以）。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>这正是我需要的一种机制。迅速地看完了EventScript这一节的文档。再想想我的需求，大体思路就是：给check in挂个脚本，让该脚本调用一个外部程序，处理将要check in的文件，分析这个文件。我可以规定在我的代码中凡是不想签入的代码都可以附加一个tag，例如：#include "vld.h" //DUMMY，分析该代码文件时，发现代码中有//DUMMY字符串，就提示我说该代码文件有不想被签入的代码，位于某某行。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>看来我似乎需要一个grep工具，万恶的是windows只有find。不过find基本可以满足我的需求，但是find的输出结果看起来并不是那么友好，并不是可以被另一个程序处理的格式。（linux下通过建立标准的输入输出格式，从而可以轻松地让多个程序协作起来更好地完成很多事）无论如何，我需要使用ab提供给我的接口来试验下我的想法。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>然后噩梦开始了。先是发现NxNLauncher（ab提供的一个接口，大致是这样拼写的，现在机器上没ab）的Exec接口是异步执行进程，从而StdOut属性及ExitCode总是得不到正确的值。后来一不小心在ab的wiki上看到，说Exec有个隐藏参数，用来标识是同步执行进程还是异步执行，显然，这个隐藏参数undocumented。- -!</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>再然后是每次执行find后，find程序倒是被执行起来了，结果却崩溃了。万恶的是我执行其他程序却很正常。google了一下居然发现没人遇到这个问题。最近我的windows总是崩溃各种程序，visual stdio, word, find。但是，我并不打算自己写个find，即使是简化版的。于是我决定写个findwrapper，让ab来调用我自己写的程序，然后我这个程序调用find。然后为了搞定两个程序的通信，又想起了管道pipe。当然，这里的通信很简单，我只需要让find的输出重定向到我的findwrapper，然后我的findwrapper又会被重定向到ab，然后我的ab脚本就可以捕获这些输出，然后再分析这些输出就可以了。- -！</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>其实，最简单的解决方法，就是我自己写个撇脚的程序，自己去分析要签入的文件是否含有//DYMMY字符串。但是很显然，我更喜欢证明我最初的想法是否正确。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>然后，在我的findwrapper里，再一次让find崩溃。经过几次试验，显然问题出在共享管道上。find和ping的不同在于，find可以从标准输入里获取输入，而ping只需要一个标准输出（也许还有stderr）。在CreateProcess里传入的startup info结构体里，需要提供标准输入、标准输出、及错误输出三个handle。显然，问题出在标准输入上，我提供了错误的值。按照一般的接口设计，我以为我如果不提供输入句柄，find就会忽略。但是find显然没那么聪明，或者说，find更喜欢直接*ptr = 10而不是if( ptr != 0 ) *ptr = 10。无论如何，当我把输入句柄填为输出句柄后，findwrapper可以捕获find的输出了，当然，find也不崩溃了。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2>我总觉得折腾windows api有点噩梦的感觉。我记得几年前刚学windows编程的时候很多函数总要试验很多次才能得到正确的结果，基本上我要尝试各个参数的诸多组合。这还不包括上下文的影响，你要RegisterClass出了问题，CreateWindow楞是不会给你一点点有意义的error code，OMG。</font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2></font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/75179.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2009-02-28 16:48 <a href="http://www.cppblog.com/kevinlynx/archive/2009/02/28/75179.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>小写了个XML解析器</title><link>http://www.cppblog.com/kevinlynx/archive/2008/12/10/69079.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 10 Dec 2008 08:22:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/12/10/69079.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/69079.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/12/10/69079.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/69079.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/69079.html</trackback:ping><description><![CDATA[<p><font size=2>&nbsp;&nbsp;&nbsp; 开始用FLEX做词法分析，然后在此基础上稍微做些符号匹配（实在称不上语法分析），即完成了XML<br>文件的简单解析。<br>&nbsp;&nbsp;&nbsp; 我把XML文件拆分成：&lt;, &gt;, /&gt;, &lt;/, =, ID, STRING 等token。这样一整理，用FLEX直接生成词法<br>分析程序。每一次getToken就返回这些token。上层的语法匹配就变得比较简单。例如当得到"/&gt;"token<br>时，我就可以判断这是一个节点的结束；当得到ID token时，就可以推测下一个token为"="，再下一个<br>是个STRING。不过对于部分token，也需要做一两个token的回溯，例如当遇到"&lt;"时，并不一定表示一个<br>新节点的开始，它可能是新节点的开始，同样也可能是上一个节点的结束("&lt;/")。<br>&nbsp;&nbsp;&nbsp; 以我薄弱的编译原理知识来看，解析XML变得非常容易。除此之外，还需要写一些上层代码来保存<br>XML结构，以方面更上层代码获取XML文件的配置信息。因为我打算用纯C来写这个东西，所以数据结构方<br>面只有自己处理。这里我以一种变相的树结构来保存：每一个节点有两个域：first child, sibling。<br>其实这样做是一个很明显的通用做法，因为XML种每一个节点都可能拥有不定数量的children节点，如果<br>让parent直接去保存，显然很笨。例如：<br>&nbsp;&nbsp;&nbsp; &lt;Resource&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bmp file="1.bmp"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bmp file="2.bmp"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/Resource&gt;<br>&nbsp;&nbsp;&nbsp; 可以使用这样的数据结构来存储：<br>&nbsp;&nbsp;&nbsp; struct xmlNode<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct xmlNode *child;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct xmlNode *sibling;<br>&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp; 对于Resource这个node而言，其child域指向第一个bmp节点(file属性为1.bmp那个节点)；对于第一<br>个bmp节点而言，其sibling域则指向了第二个bmp节点。<br>&nbsp;&nbsp;&nbsp; 这个简单的xml解析器是在公司外网机器上写的，没有VC，没有任何IDE。代码我是用VIM敲的，敲好<br>后写makefile，用mingw里的gcc、make来生成程序，用gdb来调试程序。这算是第一次离开VC写的一个非<br>练习程序(起码用makefile来组织工程)。- -| makefile写的比较烂，gdb用得很不熟，不过好歹调试出来<br>了。越来越想换个平台，只可惜工作还是得在windows vc下，很扫兴。<br>&nbsp;&nbsp;&nbsp; 后来发觉词法分析也很简单，用FLEX的时候正则表达式都写出来了。前段时间一直在看编译原理，虽然不<br>用功。但是就这里而言，基本可以直接根据正则表达式画出DFA。终于不用接触那恶心的从NFA转DFA的<br>过程，因为我至今不会，更不会写代码转。- - 总而言之，自己手写了词法分析。边写边参考编译原理<br>与实践中附带的tiny-c编译器的词法分析部分，最终发现我抄了一遍。MD，一点技术含量都没有。 </font></p>
<p><font size=2>附上全部源代码（对于代码我还是比较满意的:D），<a href="http://www.cppblog.com/Files/kevinlynx/klxmlparser.rar" target=_blank>下载</a></font></p>
<p><font size=2></font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/69079.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-12-10 16:22 <a href="http://www.cppblog.com/kevinlynx/archive/2008/12/10/69079.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最近的两个问题：less for std::map，静态变量初始化顺序</title><link>http://www.cppblog.com/kevinlynx/archive/2008/11/11/66623.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 11 Nov 2008 09:55:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/11/11/66623.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/66623.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/11/11/66623.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/66623.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/66623.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt"><br>说下最近自己遇到的两个值得让人注意的问题。<br>其一是关于自己给std::map写less predicate，std::map第三个参数是一个典型的functor。map内部将使用<br>这个functor去判定两个元素是否相等，默认使用的是std::less。但是为什么传入的是一个判断第一个参数<br>小于第二个参数的functor，而不是一个判断两个参数是否相等的functor？按照STL文档的说法，当检查两<br>个参数没有小于也没有大于的关系时，就表示两个参数相等。不管怎样，我遇到了需要自己写这个functor<br>的需求，于是：</p>
<p style="FONT-SIZE: 10pt">struct MyLess<br>{<br>&nbsp;&nbsp;&nbsp; bool operator() ( long left, long right )<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //...<br>&nbsp;&nbsp;&nbsp; }<br>};</p>
<p style="FONT-SIZE: 10pt">DEBUG模式下编译没问题，RELEASE模式下却出现C3848的错误。这就有点奇怪了，如果确实存在语法错误，<br>那么DEBUG和RELASE应该一样才对。查了下MSDN，C3848的错误是因为const限定符造成的，如：</p>
<p style="FONT-SIZE: 10pt">const MyLess pr;<br>pr(); // C3848</p>
<p style="FONT-SIZE: 10pt">于是，在operator()后加上const，就OK了。看了下VC std::map相关代码，以为是DEBUG和RELEASE使用了不<br>同的代码造成。但是我始终没找到不同点。另一方面，就STL内部的风格来看，应该不会把predicator处理<br>成const &amp;之类的东西，全部是value形式的。奇怪了。</p>
<p style="FONT-SIZE: 10pt">第二个问题，涉及到静态变量。这个东西给我的印象特别深刻，因为以前去一家外企应聘时被问到，当时<br>觉得那个LEADER特别厉害。回来后让我反思，是不是过多地关注了C++里的花哨，而漏掉了C里的朴素？导致<br>我至今对纯C存在偏好。</p>
<p style="FONT-SIZE: 10pt">说正题，我现在有如下的文件关系：</p>
<p style="FONT-SIZE: 10pt">// def.h<br>struct Obj<br>{<br>&nbsp;&nbsp;&nbsp; Obj()<br>&nbsp;{<br>&nbsp;&nbsp;ObjMgr::AddObj( id, this );<br>&nbsp;}<br>&nbsp;int id;<br>};</p>
<p style="FONT-SIZE: 10pt">struct ObjMgr<br>{<br>&nbsp;&nbsp;&nbsp; static void AddObj( int id, Obj *t )<br>&nbsp;{<br>&nbsp;&nbsp;ObjTable[id] = t;<br>&nbsp;}<br>&nbsp;static std::map&lt;int, Obj*&gt; ObjTable;<br>};</p>
<p style="FONT-SIZE: 10pt">static Obj _t;</p>
<p style="FONT-SIZE: 10pt">// ObjMgr.cpp<br>#include "def.h"</p>
<p style="FONT-SIZE: 10pt">static std::map&lt;int, Obj*&gt;::ObjMgr ObjTable;</p>
<p style="FONT-SIZE: 10pt">// main.cpp<br>#include "def.h"</p>
<p style="FONT-SIZE: 10pt">这里举的例子可能有点不恰当，我在一台没有编译器的机器上写的这篇文章。忽略掉这些旁支末节。我的意思，<br>就是想让Obj自己自动向ObjMgr里添加自己。我们都知道静态变量将在程序启动时被初始化，先于main执行之前。</p>
<p style="FONT-SIZE: 10pt">上面代码有两个问题：</p>
<p style="FONT-SIZE: 10pt">一、<br>代码没有按照我预期地执行，如果你按照我的意思做个测试，你的程序甚至在进main之前就crash了。我假定你<br>用的是VC，因为我没在其他编译器上试验过。问题就在于，Obj的构造依赖于ObjTable这个map对象。在调试过程<br>中我发现，虽然ObjTable拥有了内存空间，其this指针有效，但是，map对象并没有得到构造。我的意思是，Obj<br>的构造先于ObjTable构造（下几个断点即可轻易发现），那么在执行map::operator[]时，就出错了，因为这个时候<br>map里相关数据还没准备好。</p>
<p style="FONT-SIZE: 10pt">那是否存在某种机制可以手动静态变量的初始化顺序呢？不知道。我最后怎样解决这个问题的？</p>
<p style="FONT-SIZE: 10pt">二、<br>在还没有想到解决办法之前（不改变我的设计），我发现了这段代码的另一个问题：我在头文件里定义了静态<br>变量：static Obj _t; 这有什么问题？想想预编译这个过程即可知道，头文件在预编译阶段被文本展开到CPP<br>文件里，然后，ObjMgr.cpp和main.cpp文件里都将出现static Obj _t;代码。也就是说，ObjMgr.obj和main.obj<br>里都有一个文件静态变量_t。</p>
<p style="FONT-SIZE: 10pt">看来，在头文件里放这个静态变量是肯定不对的。于是，我将_t移动到ObjMgr.cpp里：</p>
<p style="FONT-SIZE: 10pt">// ObjMgr.cpp<br>#include "def.h"</p>
<p style="FONT-SIZE: 10pt">static std::map&lt;int, Obj*&gt;::ObjMgr ObjTable;<br>static Obj _t;</p>
<p style="FONT-SIZE: 10pt">按照这样的顺序定义后，_t的构造居然晚于ObjTable了。也就是说，放置于前面的变量定义，就意味着它将被<br>首先构造初始化。这样两个问题都解决了。</p>
<p style="FONT-SIZE: 10pt">但是，谁能保证这一点特性？C标准文档里？还是VC编译器自己？</p>
<p style="FONT-SIZE: 10pt">&nbsp;</p>
<p style="FONT-SIZE: 10pt">&nbsp;</p>
<p style="FONT-SIZE: 10pt">&nbsp;</p>
<p style="FONT-SIZE: 10pt"><br>&nbsp;</p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/66623.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-11-11 17:55 <a href="http://www.cppblog.com/kevinlynx/archive/2008/11/11/66623.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>代码自动生成-宏递归思想</title><link>http://www.cppblog.com/kevinlynx/archive/2008/08/20/59451.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 20 Aug 2008 09:48:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/08/20/59451.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/59451.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/08/20/59451.html#Feedback</comments><slash:comments>24</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/59451.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/59451.html</trackback:ping><description><![CDATA[<p><font size=2>Macro Recursion<br>author: Kevin Lynx </font>
<p><font size=2><strong>Preface </strong></font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 本文可能是&lt;<a href="http://www.cppblog.com/kevinlynx/archive/2008/03/19/44828.html" target=_blank>代码自动生成-宏带来的奇技淫巧</a>&gt;的续写。我尽力阐述如何让宏递归（或者说重复）地有规律地产生一<br>些符号，而让我们少写很多重复代码，也许这些代码只有那么一点点的不同。将这项小技巧用于底层库的编写，会让代码<br>看起来干净不少，同时文件尺寸也会骤然下降。</font></p>
<font size=2>
<p><br><strong>Problem</strong></p>
<p><strong></strong><br>&nbsp;&nbsp;&nbsp; 如果你曾经写过<a href="http://www.cppblog.com/kevinlynx/archive/2008/03/17/44678.html" target=_blank>functor</a>，那么你肯定对某些代码进行粘贴复制然后修改。更让人郁闷的是，这些代码基本是一样的。<br>例如，一个典型的functor可能为：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;template&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">typename&nbsp;Prototype</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;functor;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;template&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">typename&nbsp;R,&nbsp;typename&nbsp;P1</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;functor</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">R(P1)</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;template&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">typename&nbsp;R,&nbsp;typename&nbsp;P1,&nbsp;typename&nbsp;P2</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;functor</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">R(P1,P2)</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; //好，接下去你可能厌烦了，可能会复制一个带有两个参数的functor，然后修改为处理3个参数的。<br>&nbsp;&nbsp;&nbsp; 这只是一个很简单的问题。宏不是c++里的东西，本文自然也不是讨论各种花哨的模板技术的。如果我之前那篇关于<br>宏的文章只是让你去分析问题以及更深层次地认识宏，那么现在我将分享我的这部分思想给你。<br>&nbsp;&nbsp;&nbsp; 关于上面的问题，我们期待得到这样的解决方案：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;template&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">typename&nbsp;R,&nbsp;DEF_PARAM(&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">&nbsp;)</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;functor</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">R(&nbsp;DEF_ARG(&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">&nbsp;)&nbsp;)</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 那么，它将自动生成：<br></font><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;template&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">typename&nbsp;R,&nbsp;typename&nbsp;P1,&nbsp;typename&nbsp;P2</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;functor</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">R(P1,P2)</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 也就是说，DEF_PARAM(n)这个宏将根据n值自动生成一串符号，例如DEF_PARAM(2)就生成typename P1, typename P2。<br>同样，DEF_ARG(n)也会根据参数生成类似于P1,P2,...,Pn的符号串。 </font></p>
<p><font size=2><strong>思考</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 仔细思考下，我们可以看出DEF_PARAM和DEF_ARG这样的宏具有一种递归的特性（其实说成重复可能更合适）：每次展<br>开的内容基本一样，不断调用自身直到遇到终止条件。<br>&nbsp;&nbsp;&nbsp; 那么，我们的目标锁定于，用宏来实现递归。</font></p>
<p><font size=2><br><strong>Pre-Implement</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; 在开始之前，我需要告诉你一些基本的东西：<br>&nbsp;&nbsp;&nbsp; 在阅读一个宏时，你最好按照预处理的处理方式去逐个展开。当我说到展开时，我的意思是把宏替换为宏体。预处理器<br>展开宏的过程大致为：如果宏参数也是个宏，那么先将宏参数全部展开，再展开该宏；这个时候会扫描展开后的宏，如果<br>遇到其他宏，则继续展开。例如有一下宏： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PI&nbsp;3.14</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;MUL_PI(&nbsp;n&nbsp;)&nbsp;n&nbsp;*&nbsp;PI</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;TWO&nbsp;2</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 当我们写下MUL_PI( TWO )时，预处理发现MUL_PI的参数TWO 是个宏，那么先将TWO展开得到2，然后将2放进宏体展开<br>得到 2 * PI；预处理器对 2 * PI 进行扫描，发现还有宏PI，于是对PI做展开，得到 2 * 3.14。这个过程是递归的。<br>&nbsp;&nbsp;&nbsp; 但是也有例外，如果MUL_PI对宏参数进行了#或者##，那么该宏参数不会被展开。（参见以前那篇文章吧）<br>&nbsp;&nbsp;&nbsp; 任何时候，你可以通过以下宏去查看某个宏展开后的样子，可以方便你调试你的宏： </font>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;TO_STRING(&nbsp;x&nbsp;)&nbsp;TO_STRING1(&nbsp;x&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;TO_STRING1(&nbsp;x&nbsp;)&nbsp;#x&nbsp;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; （为什么要写个TO_STRING1，因为这是为了让x充分展开，避免上面提到的那个例外）&nbsp;&nbsp;&nbsp; </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 其他规则我会在文中需要的地方提出来。<br>实现 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 就像大部分介绍递归函数时候给的例子，这里我也将阶乘作为例子。考虑如下典型的阶乘函数：<br></font>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;fac(&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;n&nbsp;)<br><img id=Codehighlighter1_25_95_Open_Image onclick="this.style.display='none'; Codehighlighter1_25_95_Open_Text.style.display='none'; Codehighlighter1_25_95_Closed_Image.style.display='inline'; Codehighlighter1_25_95_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_25_95_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_25_95_Closed_Text.style.display='none'; Codehighlighter1_25_95_Open_Image.style.display='inline'; Codehighlighter1_25_95_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_25_95_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_25_95_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;n&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;)&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;n&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;fac(&nbsp;n&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 其核心部分在于 n * fac( n - 1 )，我们假设我们的宏也可以写成这样的的形式：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC(&nbsp;n&nbsp;)&nbsp;n&nbsp;*&nbsp;FAC(&nbsp;n&nbsp;-&nbsp;1&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 但是这样的宏是有问题的：<br>&nbsp;&nbsp;&nbsp; 当宏被展开时，如果遇到了自身，那么将被处理为一般符号，例如展开FAC( 3 )时，会遇到 FAC( 2 )，那么就把FAC<br>( 2 )中的FAC当成了一搬符号。<br>&nbsp;&nbsp;&nbsp; 这样的限制注定了我们无法让宏真正地调用自身来实现递归。于是，我们不得不写下以下丑陋的符号，从而去模拟递<br>归的每一次符号调用： </font>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_1(&nbsp;n&nbsp;)&nbsp;1</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_2(&nbsp;n&nbsp;)&nbsp;n&nbsp;*&nbsp;FAC_##(n-1)(&nbsp;n&nbsp;-&nbsp;1&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_3(&nbsp;n&nbsp;)&nbsp;n&nbsp;*&nbsp;FAC_##(n-1)(&nbsp;n&nbsp;-&nbsp;1&nbsp;)&nbsp;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 这系列宏有点别扭（如果你足够细心），因为我们明显知道FAC_2返回的将是2，而FAC_3返回的当时6。我们这里只是<br>模拟，同样，这使得我们可以把FAC_1作为递归的终止条件。<br>&nbsp;&nbsp;&nbsp; 我们的预想是，当调用FAC_3时，它把n-1的值2合并到FAC_中，从而调用FAC_2，以此类推。<br>&nbsp;&nbsp;&nbsp; 但是这依然有问题，编译器会提示&#8216;找不到符号FAC_&#8217;。导致这个问题的原因在于：宏展开只是单纯的字符替换，是我们<br>想太多了，预处理器并不会去计算( n - 1 )的值是多少，也就是我们无法得到FAC_2这个宏。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 所以，FAC_3( 3 ) 会被初次替换为 3 * FAC_(3-1)( 3 - 1 )。这个时候编译器就把FAC_当成了一个普通符号。我们可以<br>自己定义个FAC_来证明这一点： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_(&nbsp;n&nbsp;)&nbsp;T&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 那么，FAC_3( 3 )就被替换为 3 * T(3-1)( 3 - 1 )。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 解决这个问题的办法关键在于，让预处理器自动计算出( n - 1 )。记住，我们解决问题的唯一办法是：字符替换。<br>所以，我们可以写下如下代码： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;DEC_1&nbsp;0</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;DEC_2&nbsp;1</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;DEC_3&nbsp;2&nbsp;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;DEC_##n&nbsp;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 通过，DEC( n )这个宏，我们可以获取到一个( n -1 )的数。例如，DEC( 3 )被替换为 DEC_3，继续替换为 2。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 于是，我们新的FAC系列宏变为： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_1(&nbsp;n&nbsp;)&nbsp;1</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_2(&nbsp;n&nbsp;)&nbsp;n&nbsp;*&nbsp;FAC_##DEC(&nbsp;n&nbsp;)(&nbsp;n&nbsp;-&nbsp;1&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_3(&nbsp;n&nbsp;)&nbsp;n&nbsp;*&nbsp;FAC_##DEC(&nbsp;n&nbsp;)(&nbsp;n&nbsp;-&nbsp;1&nbsp;)&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 不好意思，这样依然是不正确的！预处理器直接把FAC_和DEC( n )连接成符号了，而不是单个地先处理他们，最后再<br>合并他们。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; OK，先解决这个问题：先处理FAC_和DEC( n )，再合并他们，而不是先合并他们。要解决这个问题，可以通过第三个宏<br>来实现： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;CHR(&nbsp;x,&nbsp;y&nbsp;)&nbsp;x##y&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 作为连接两个符号为一个符号的宏，这个宏显然是不正确的，因为宏展开还有个规则：如果宏体对宏参数使用了#或##，<br>那么宏参数不会被展开，也就是说：如果CHR( FAC_, DEC( 3 ) 那么得到的只会是 FAC_DEC( 3 )。通常情况下我们是<br>再写个宏： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;CHR(&nbsp;x,&nbsp;y&nbsp;)&nbsp;CHR1(&nbsp;x,&nbsp;y&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;CHR1(&nbsp;x,&nbsp;y&nbsp;)&nbsp;x##y&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 从而可以保证在正式连接x和y前，x和y都被完全展开。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 这个时候，我们的FAC系列宏变为： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_1(&nbsp;n&nbsp;)&nbsp;1</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_2(&nbsp;n&nbsp;)&nbsp;n&nbsp;*&nbsp;CHR(&nbsp;FAC_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;n&nbsp;-&nbsp;1&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_3(&nbsp;n&nbsp;)&nbsp;n&nbsp;*&nbsp;CHR(&nbsp;FAC_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;n&nbsp;-&nbsp;1&nbsp;)&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 结果呢？结果还是有问题。= =<br>&nbsp;&nbsp;&nbsp; 我们假设CHR( FAC_, DEC( n ) )已经真的按我们预想展开为 FAC_2了，那么FAC_3( 3 )会被展开为什么呢？<br>被展开为 3 * FAC_2( 3 - 1 )。这是错误的，传给 FAC_2 的参数是 3 - 1就意味着错误。我们又臆想预处理器会<br>帮我们计算 3 - 1的结果了。我们必须保证传给 FAC_2的参数是个数字2。解决这个问题的办法就是通过DEC(n)宏。 </font>
<p><font size=2>&nbsp;&nbsp; 于是，FAC系列宏变为： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_1(&nbsp;n&nbsp;)&nbsp;1</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_2(&nbsp;n&nbsp;)&nbsp;n&nbsp;*&nbsp;CHR(&nbsp;FAC_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;FAC_3(&nbsp;n&nbsp;)&nbsp;n&nbsp;*&nbsp;CHR(&nbsp;FAC_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 这个时候，FAC_3( 3 )将会被替换为：3*2*1。这就是我们要的结果。 </font>
<p><font size=2><strong>In practice</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 以上只是向你展示一个过程，用宏去计算阶乘，就像用模板去计算阶乘（模板元编程）一样，只是一个用于展示的东西，<br>没有什么实际价值。接下来我们开始有实际的工作，完成之前的预想： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">template&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">typename&nbsp;R,&nbsp;typename&nbsp;P1,&nbsp;typename&nbsp;P2,&nbsp;typename&nbsp;P3</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;functor</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">R&nbsp;(P1,&nbsp;P2,&nbsp;P3)</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 直接: </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">template&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">typename&nbsp;R,&nbsp;PARAM(&nbsp;</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">&nbsp;)</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;functor</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">R&nbsp;(ARG(&nbsp;</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">&nbsp;))</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 先考虑PARAM宏，该宏的任务就是生成类似于：typename P1, typename P2, typename P3这样的符号。我们假象它每一次<br>递归都生成 typename Pn, 的字符串，那么当他递归完时，可能就生成typename P1, typename P2, typename P3, 结果<br>多了个逗号，也许最后一次结果不该有逗号。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; ARG宏和PARAM宏本质上相同，只是重复的符号不是typename Pn，而是Pn。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 最直接想到的是： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PARAM_1(&nbsp;n&nbsp;)&nbsp;typename&nbsp;P##n</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PARAM_2(&nbsp;n&nbsp;)&nbsp;CHR(&nbsp;PARAM_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)##,typename&nbsp;P##n</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PARAM_3(&nbsp;n&nbsp;)&nbsp;CHR(&nbsp;PARAM_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)##,typename&nbsp;P##n&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 结果我们得到了个错误的展开结果：<br>typename PDEC( 2 ),typename PDEC( 3 ),typename P3 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 这个问题出在：PARAM_3( 3 )当替换为 PARAM_2( DEC( n ) )时，因为PARAM_2(n)宏对于宏参数n使用了##，也就是那个<br>typename P##n，所以这里不会把 DEC( n )展开，而是直接接到P后面。所以就成了typename PDEC( 3 )。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 为了消除这个问题，我们改进PARAM为： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;TYPE(&nbsp;n&nbsp;)&nbsp;,typename&nbsp;P##n</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PARAM_1(&nbsp;n&nbsp;)&nbsp;CHR(&nbsp;typename&nbsp;P,&nbsp;n&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PARAM_2(&nbsp;n&nbsp;)&nbsp;CHR(&nbsp;CHR(&nbsp;PARAM_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;),&nbsp;TYPE(&nbsp;n&nbsp;)&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PARAM_3(&nbsp;n&nbsp;)&nbsp;CHR(&nbsp;CHR(&nbsp;PARAM_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;),&nbsp;TYPE(&nbsp;n&nbsp;)&nbsp;)&nbsp;</span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 之所以加入TYPE(n)，是因为 ,typename P##n 这个宏本身存在逗号，将其直接用于宏体会出现问题。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 于是，我们得到了正确的结果。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 其实，PARAM系列宏宏体基本是一样的，除了终止条件那个宏，为什么我们要写这么多呢？理由在于宏体不能自己调用<br>自己，所以才有了PARAM_3, PARAM_2。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 我们可以将上面的一系列宏抽象化，使其具有可复用性： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PARAM(&nbsp;n&nbsp;)&nbsp;,typename&nbsp;P##n</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PARAM_END&nbsp;typename&nbsp;P&nbsp;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;ARG(&nbsp;n&nbsp;)&nbsp;,P##n</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;ARG_END&nbsp;P&nbsp;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PARAM_1(&nbsp;n&nbsp;)&nbsp;CHR(&nbsp;typename&nbsp;P,&nbsp;n&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PARAM_2(&nbsp;n&nbsp;)&nbsp;CHR(&nbsp;CHR(&nbsp;PARAM_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;),&nbsp;TYPE(&nbsp;n&nbsp;)&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;PARAM_3(&nbsp;n&nbsp;)&nbsp;CHR(&nbsp;CHR(&nbsp;PARAM_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;),&nbsp;TYPE(&nbsp;n&nbsp;)&nbsp;)&nbsp;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;REPEAT_1(&nbsp;n,&nbsp;f,&nbsp;e&nbsp;)&nbsp;CHR(&nbsp;e,&nbsp;n&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;REPEAT_2(&nbsp;n,&nbsp;f,&nbsp;e&nbsp;)&nbsp;CHR(&nbsp;CHR(&nbsp;REPEAT_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;DEC(&nbsp;n&nbsp;),&nbsp;f,&nbsp;e&nbsp;),&nbsp;f(&nbsp;n&nbsp;)&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;REPEAT_3(&nbsp;n,&nbsp;f,&nbsp;e&nbsp;)&nbsp;CHR(&nbsp;CHR(&nbsp;REPEAT_,&nbsp;DEC(&nbsp;n&nbsp;)&nbsp;)(&nbsp;DEC(&nbsp;n&nbsp;),&nbsp;f,&nbsp;e&nbsp;),&nbsp;f(&nbsp;n&nbsp;)&nbsp;)&nbsp;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;DEF_PARAM(&nbsp;n&nbsp;)&nbsp;REPEAT_##n(&nbsp;n,&nbsp;PARAM,&nbsp;PARAM_END&nbsp;)</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;DEF_ARG(&nbsp;n&nbsp;)&nbsp;REPEAT_##n(&nbsp;n,&nbsp;ARG,&nbsp;ARG_END&nbsp;)&nbsp;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 我们创建了可重用的REPEAT系列宏，用于创建诸如typename P1, typename P2, typename P3或者P1,P2,P3之类的符号，<br>通过更上层的DEF_PARAM(n)和DEF_ARG(n)，就可以直接创建出我们上面所需要的符号串，例如： </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; DEF_PARAM( 3 ) 就得到 typename P1, typename P2, typename P3<br>&nbsp;&nbsp;&nbsp; DEF_ARG( 3 ) 就得到 P1, P2, P3 </font>
<p><font size=2><strong>More in practice</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 下载中提供了我使用这个宏递归技术写的lua_binder(如果你看过&lt;<a href="http://www.cppblog.com/kevinlynx/archive/2008/08/13/58684.html" target=_blank>实现自己的LUA绑定器-一个模板编程挑战</a> &gt;)，你<br>可以与上一个版本做一下比较，代码少了很多。<br>&nbsp;&nbsp;&nbsp; 同样，我希望你也能获取这种宏递归的思想。&nbsp;&nbsp;&nbsp; </font></p>
<p><font size=2><strong>相关下载</strong></font></p>
<p><font size=2>&nbsp;&nbsp; <a href="http://www.cppblog.com/Files/kevinlynx/mr_luabinder.zip" target=_blank>使用宏递归的lua_binder</a></font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/59451.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-08-20 17:48 <a href="http://www.cppblog.com/kevinlynx/archive/2008/08/20/59451.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现LUA脚本同步处理事件:LUA的coroutine</title><link>http://www.cppblog.com/kevinlynx/archive/2008/08/12/58636.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 12 Aug 2008 08:02:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/08/12/58636.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/58636.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/08/12/58636.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/58636.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/58636.html</trackback:ping><description><![CDATA[<p><font size=2>author : Kevin Lynx</font>
<p><font size=2><strong>需求</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 受WOW的影响，LUA越来越多地被应用于游戏中。脚本被用于游戏中主要用于策划编写游戏规则相关。实际运用中，<br>我们会将很多宿主语言函数绑定到LUA脚本中，使脚本可以更多地控制程序运行。例如我们可以绑定NPCDialog之类的函数<br>到LUA中，然后策划便可以在脚本里控制游戏中弹出的NPC对话框。<br>&nbsp;&nbsp;&nbsp; 我们现在面临这样的需求：对于宿主程序而言，某些功能是不能阻塞程序逻辑的（对于游戏程序尤其如此），但是为<br>了方便策划，我们又需要让脚本看起来被阻塞了。用NPCDialog举个例子，在脚本中有如下代码 ：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; ret </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> NPCDialog( </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Hello bitch</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> )<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> ret </span><span style="COLOR: #000000">==</span><span style="COLOR: #000000"> OK then print(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">OK</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">) end</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 对于策划而言，NPCDialog应该是阻塞的，除非玩家操作此对话框，点击OK或者关闭，不然该函数不会返回。而对于<br>宿主程序C++而言，我们如何实现这个函数呢： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> do_npc_dialog( lua_State </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">L )<br><img id=Codehighlighter1_49_169_Open_Image onclick="this.style.display='none'; Codehighlighter1_49_169_Open_Text.style.display='none'; Codehighlighter1_49_169_Closed_Image.style.display='inline'; Codehighlighter1_49_169_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_49_169_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_49_169_Closed_Text.style.display='none'; Codehighlighter1_49_169_Open_Image.style.display='inline'; Codehighlighter1_49_169_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span id=Codehighlighter1_49_169_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_49_169_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">content </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> lua_tostring( L, </span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000"> );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua_pushnumber( ret );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp; }</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 显然，该函数不能阻塞，否则它会阻塞整个游戏线程，这对于服务器而言是不可行的。但是如果该函数立即返回，那<br>么它并没有收集到玩家对于那个对话框的操作。<br>&nbsp;&nbsp;&nbsp; 综上，我们要做的是，让脚本感觉某个操作阻塞，但事实上宿主程序并没有阻塞。 </font>
<p><font size=2><strong>事件机制</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 一个最简单的实现（对于C程序员而言也许也是优美的），就是使用事件机制。我们将对话框的操作结果作为一个事件。<br>脚本里事实上没有哪个函数是阻塞的。为了处理一些&#8220;阻塞&#8221;函数的处理结果，脚本向宿主程序注册事件处理器（同GUI事件<br>处理其实是一样的），例如脚本可以这样：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; function onEvent( ret )<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> ret </span><span style="COLOR: #000000">==</span><span style="COLOR: #000000"> OK then print(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">OK</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">) end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #000000"> register </span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000"> handler<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; SetEventHandler( </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">onEvent</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> )<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; NPCDialog(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Hello bitch</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 宿主程序保存事件处理器onEvent函数名，当玩家操作了对话框后，宿主程序回调脚本中的onEvent，完成操作。<br>&nbsp;&nbsp;&nbsp; 事实上我相信有很多人确实是这么做的。这样做其实就是把一个顺序执行的代码流，分成了很多块。但是对于sleep<br>这样的脚本调用呢？例如： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job A<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; sleep(</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job B<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; sleep(</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job C<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; <img src="http://www.cppblog.com/Images/dot.gif"></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 那么采用事件机制将可能会把代码分解为：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; function onJobA<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job A<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetEventHandlerB(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">onJobB</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; function onJobB<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job B<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetEventHandlerC(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">onJobC</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; function onJobC<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job C<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #000000"> script starts here<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; SetEventHandlerA( </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">onJobA</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> )<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; sleep(</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">)</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 代码看起来似乎有点难看了，最重要的是它不易编写，策划估计会抓狂的。我想，对于非专业程序员而言，程序的<br>顺序执行可能理解起来更为容易。 </font>
<p><font size=2><strong>SOLVE IT</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 我们的解决方案，其实只有一句话：当脚本执行到阻塞操作时（如NPCDialog），挂起脚本，当宿主程序某个操作完<br>成时，让脚本从之前的挂起点继续执行。<br>&nbsp;&nbsp;&nbsp; 这不是一种假想的功能。我在刚开始实现这个功能之前，以为LUA不支持这个功能。我臆想着如下的操作：<br>&nbsp;&nbsp;&nbsp; 脚本：<br>&nbsp;&nbsp;&nbsp; ret = NPCDialog("Hello bitch")<br>&nbsp;&nbsp;&nbsp; if ret == 0 then print("OK") end<br>&nbsp;&nbsp;&nbsp; 宿主程序：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> do_npc_dialog( lua_State </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">L )<br><img id=Codehighlighter1_49_112_Open_Image onclick="this.style.display='none'; Codehighlighter1_49_112_Open_Text.style.display='none'; Codehighlighter1_49_112_Closed_Image.style.display='inline'; Codehighlighter1_49_112_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_49_112_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_49_112_Closed_Text.style.display='none'; Codehighlighter1_49_112_Open_Image.style.display='inline'; Codehighlighter1_49_112_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span id=Codehighlighter1_49_112_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_49_112_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua_suspend_script( L );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp; }</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 某个地方某个操作完成了：<br>&nbsp;&nbsp;&nbsp; lua_resume_script( L );<br>&nbsp;&nbsp;&nbsp; 当我实现了这个功能后，我猛然发现，实际情况和我这里想的差不多（有点汗颜）。</font></p>
<p><font size=2><br><strong>认识Coroutine</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; coroutine是LUA中类似线程的东西，但是它其实和fiber更相似。也就是说，它是一种非抢占式的线程，它的切换取决<br>于任务本身，也就是取决你，你决定它们什么时候发生切换。建议你阅读lua manual了解更多。<br>&nbsp;&nbsp;&nbsp; coroutine支持的典型操作有：lua_yield, lua_resume，也就是我们需要的挂起和继续执行。<br>&nbsp;&nbsp;&nbsp; lua_State似乎就是一个coroutine，或者按照LUA文档中的另一种说法，就是一个thread。我这里之所以用&#8217;似乎&#8216;是<br>因为我自己也无法确定，我只能说，lua_State看起来就是一个coroutine。<br>&nbsp;&nbsp;&nbsp; LUA提供lua_newthread用于手工创建一个coroutine，然后将新创建的coroutine放置于堆栈顶，如同其他new出来的<br>对象一样。网上有帖子说lua_newthread创建的东西与脚本里调用coroutine.create创建出来的东西不一样，但是根据我<br>的观察来看，他们是一样的。lua_newthread返回一个lua_State对象，所以从这里可以看出，&#8220;lua_State看起来就是一个<br>coroutine&#8221;。另外，网上也有人说创建新的coroutine代价很大，但是，一个lua_State的代价能有多大？当然，我没做过<br>测试，不敢多言。<br>&nbsp;&nbsp;&nbsp; lua_yield用于挂起一个coroutine，不过该函数只能用于coroutine内部，看看它的参数就知道了。<br>&nbsp;&nbsp;&nbsp; lua_resume用于启动一个coroutine，它可以用于coroutine没有运行时启动之，也可以用于coroutine挂起时重新启动<br>之。lua_resume在两种情况下返回：coroutine挂起或者执行完毕，否则lua_resume不返回。<br>&nbsp;&nbsp;&nbsp; lua_yield和lua_resume对应于脚本函数：coroutine.yield和coroutine.resume，建议你写写脚本程序感受下coroutine，<br>例如：<br></font></p>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; function main()<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">main start</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; coroutine.yield()<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">main end</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; co</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">coroutine.create( main );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; coroutine.resume(co)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br><strong>REALLY SOLVE IT</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; 你可能会想到，我们为脚本定义一个main，然后在宿主程序里lua_newthread创建一个coroutine，然后将main放进去，<br>当脚本调用宿主程序的某个&#8217;阻塞&#8216;操作时，宿主程序获取到之前创建的coroutine，然后yield之。当操作完成时，再resume<br>之。<br>&nbsp;&nbsp;&nbsp; 事实上方法是对的，但是没有必要再创建一个coroutine。如之前所说，一个lua_State看上去就是一个coroutine，<br>而恰好，我们始终都会有一个lua_State。感觉上，这个lua_State就像是main coroutine。（就像你的主线程）<br>&nbsp;&nbsp;&nbsp; 思路就是这样，因为具体实现时，还是有些问题，所以我罗列每个步骤的代码。<br>&nbsp;&nbsp;&nbsp; 初始lua_State时如你平时所做：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; lua_State </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">L </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> lua_open();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; luaopen_base( L );</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 注册脚本需要的宿主程序函数到L里：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; lua_pushcfunction( L, sleep );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; lua_setglobal( L, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">my_sleep</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> );</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 载入脚本文件并执行时稍微有点不同：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; luaL_loadfile( L, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">test.lua</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> );<br><img id=Codehighlighter1_60_73_Open_Image onclick="this.style.display='none'; Codehighlighter1_60_73_Open_Text.style.display='none'; Codehighlighter1_60_73_Closed_Image.style.display='inline'; Codehighlighter1_60_73_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_60_73_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_60_73_Closed_Text.style.display='none'; Codehighlighter1_60_73_Open_Image.style.display='inline'; Codehighlighter1_60_73_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>lua_resume( L, </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000"> ); </span><span id=Codehighlighter1_60_73_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_60_73_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000"> 调用resume </span><span style="COLOR: #008000">*/</span></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 在你的&#8217;阻塞&#8216;函数里需要挂起coroutine：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> lua_yield( L, </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000"> );</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 注意，lua_yield函数非常特别，它必须作为return语句被调用，否则会调用失败，具体原因我也不清楚。而在这里，<br>它作为lua_CFunction的返回值，会不会引发错误？因为lua_CFunction约定返回值为该函数对于脚本而言的返回值个数。<br>实际情况是，我看到的一些例子里都这样安排lua_yield，所以i do what they do。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 在这个操作完成后（如玩家操作了那个对话框），宿主程序需要唤醒coroutine：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; lua_resume( L, </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000"> ); </span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 大致步骤就这些。如果你要单独创建新的lua_State，反而会搞得很麻烦，我开始就是那样的做的，总是实现不了自己<br>预想中的效果。 </font>
<p><font size=2><strong>相关下载：</strong><br>&nbsp;&nbsp;&nbsp; 例子程序中，我给了一个sleep实现。脚本程序调用sleep时将被挂起，宿主程序不断检查当前时间，当时间到时，resume<br>挂起的coroutine。<a href="http://www.cppblog.com/Files/kevinlynx/lua_test_coro.rar" target=_blank>下载例子</a></font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2><strong>8.13补充</strong></font></p>
<p><font size=2><strong>&nbsp;</strong>&nbsp; 可能有时候，我们提供给脚本的函数需要返回一些值给脚本，例如NPCDialog返回操作结果，我们只需要在宿主程序里lua_resume</font></p>
<p><font size=2>之前push返回值即可，当然，需要设置lua_resume第二个参数为返回值个数。<br><br><strong>2.9.2010</strong><br>&nbsp;&nbsp;&nbsp; lua_yield( L, nResults )第二个参数指定返回给lua_resume的值个数。如下：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;lua_pushnumber(&nbsp;L,&nbsp;</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;lua_yield(&nbsp;L,&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<img src="http://www.cppblog.com/Images/dot.gif"><img src="http://www.cppblog.com/Images/dot.gif"><img src="http://www.cppblog.com/Images/dot.gif"><img src="http://www.cppblog.com/Images/dot.gif">..<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;lua_resume(&nbsp;L,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;ret&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;LUA_YIELD&nbsp;)<br><img id=Codehighlighter1_134_190_Open_Image onclick="this.style.display='none'; Codehighlighter1_134_190_Open_Text.style.display='none'; Codehighlighter1_134_190_Closed_Image.style.display='inline'; Codehighlighter1_134_190_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_134_190_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_134_190_Closed_Text.style.display='none'; Codehighlighter1_134_190_Open_Image.style.display='inline'; Codehighlighter1_134_190_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_134_190_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_134_190_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lua_Number&nbsp;r&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;luaL_checknumber(&nbsp;L,&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;}</span></span></div>
</font>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/58636.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-08-12 16:02 <a href="http://www.cppblog.com/kevinlynx/archive/2008/08/12/58636.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent 源码分析：min_heap带来的超时机制</title><link>http://www.cppblog.com/kevinlynx/archive/2008/07/18/56511.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Fri, 18 Jul 2008 07:56:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/07/18/56511.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/56511.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/07/18/56511.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/56511.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/56511.html</trackback:ping><description><![CDATA[<p><font size=2>author : Kevin Lynx</font>
<p><font size=2><strong>什么是min heap ?</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 首先看什么是heap，heap是这样一种数据结构：1.它首先是一棵完成二叉树；2.父亲节点始终大于(或其他逻辑关系<br>)其孩子节点。根据父亲节点与孩子节点的这种逻辑关系，我们将heap分类，如果父亲节点小于孩子节点，那么这个heap<br>就是min heap。<br>&nbsp;&nbsp;&nbsp; 就我目前所看到的实现代码来看，heap基本上都是用数组(或者其他的连续存储空间)作为其存储结构的。这可以保证<br>数组第一个元素就是heap的根节点。这是一个非常重要的特性，它可以保证我们在取heap的最小元素时，其算法复杂度为<br>O(1)。<br>&nbsp;&nbsp;&nbsp; 原谅我的教科书式说教，文章后我会提供我所查阅的比较有用的关于heap有用的资料。 </font>
<p><font size=2><strong>What Can It Do?</strong><br>&nbsp;&nbsp;&nbsp; libevent中的min_heap其实不算做真正的MIN heap。它的节点值其实是一个时间值。libevent总是保证时间最晚的节<br>点为根节点。<br>&nbsp;&nbsp;&nbsp; libevent用这个数据结构来实现IO事件的超时控制。当某个事件(libevent中的struct event)被添加时(event_add)，<br>libevent将此事件按照其超时时间(由用户设置)保存在min_heap里。然后libevent会定期地去检查这个min_heap，从而实现<br>了超时机制。 </font>
<p><font size=2><strong>实现</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; min_heap相关源码主要集中在min_heap.h以及超时相关的event.c中。<br>&nbsp;&nbsp;&nbsp; 首先看下min_heap的结构体定义：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">typedef&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;min_heap<br><img id=Codehighlighter1_24_67_Open_Image onclick="this.style.display='none'; Codehighlighter1_24_67_Open_Text.style.display='none'; Codehighlighter1_24_67_Closed_Image.style.display='inline'; Codehighlighter1_24_67_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_24_67_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_24_67_Closed_Text.style.display='none'; Codehighlighter1_24_67_Open_Image.style.display='inline'; Codehighlighter1_24_67_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_24_67_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_24_67_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">**</span><span style="COLOR: #000000">&nbsp;p;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;n,&nbsp;a;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">&nbsp;min_heap_t;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; p指向了一个动态分配的数组(随便你怎么说，反正它是一个由realloc分配的连续内存空间)，数组元素为event*，这也是<br>heap中的节点类型。这里libevent使用连续空间去保存heap，也就是保存一棵树。因为heap是完成树，所以可以保证其元素在<br>数组中是连续的。n表示目前保存了多少个元素，a表示p指向的内存的尺寸。<br>&nbsp;&nbsp;&nbsp; struct event这个结构体定义了很多东西，但是我们只关注两个成员：min_heap_idx：表示该event保存在min_heap数组中<br>的索引，初始为-1；ev_timeout：该event的超时时间，将被用于heap操作中的节点值比较。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 接下来看几个与堆操作不大相关的函数：<br>&nbsp;&nbsp;&nbsp; min_heap_elem_greater：比较两个event的超时值大小。<br>&nbsp;&nbsp;&nbsp; min_heap_size：返回heap元素值数量。<br>&nbsp;&nbsp;&nbsp; min_heap_reserve：调整内存空间大小，也就是调整p指向的内存区域大小。凡是涉及到内存大小调整的，都有一个策略问<br>题，这里采用的是初始大小为8，每次增大空间时以2倍的速度增加。<br>&nbsp;&nbsp;&nbsp; 看几个核心的：真正涉及到heap数据结构操作的函数：往堆里插入元素、从堆里取出元素：<br>&nbsp;&nbsp;&nbsp; 相关函数为：min_heap_push、min_heap_pop、min_heap_erase、min_heap_shift_up_、min_heap_shift_down_。 </font>
<p><font size=2><strong>heap的核心操作：</strong> </font>
<p><font size=2><strong>- 往堆里插入元素：</strong><br>&nbsp;&nbsp;&nbsp; 往堆里插入元素相对而言比较简单，如图所示，每一次插入时都从树的最右最下(也就是叶子节点)开始。然后比较即将插入<br>的节点值与该节点的父亲节点的值，如果小于父亲节点的话(不用在意这里的比较规则，上下文一致即可)，那么交换两个节点，<br>将新的父亲节点与其新的父亲节点继续比较。重复这个过程，直到比较到根节点。</font></p>
<p><font size=2>&nbsp; <a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventmin_heap_E003/heap_add_2.jpg"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=174 alt=heap_add src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventmin_heap_E003/heap_add_thumb.jpg" width=210 border=0></a> <br>&nbsp;&nbsp;&nbsp; libevent实现这个过程的函数主要是min_heap_shift_up_。每一次min_heap_push时，首先检查存储空间是否足够，然后直接<br>调用min_heap_shift_up_插入。主要代码如下：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;min_heap_shift_up_(min_heap_t</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;s,&nbsp;unsigned&nbsp;hole_index,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;e)<br><img id=Codehighlighter1_77_440_Open_Image onclick="this.style.display='none'; Codehighlighter1_77_440_Open_Text.style.display='none'; Codehighlighter1_77_440_Closed_Image.style.display='inline'; Codehighlighter1_77_440_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_77_440_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_77_440_Closed_Text.style.display='none'; Codehighlighter1_77_440_Open_Image.style.display='inline'; Codehighlighter1_77_440_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_77_440_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_77_440_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_80_90_Open_Image onclick="this.style.display='none'; Codehighlighter1_80_90_Open_Text.style.display='none'; Codehighlighter1_80_90_Closed_Image.style.display='inline'; Codehighlighter1_80_90_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_80_90_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_80_90_Closed_Text.style.display='none'; Codehighlighter1_80_90_Open_Image.style.display='inline'; Codehighlighter1_80_90_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_80_90_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_80_90_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;获取父节点&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;parent&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(hole_index&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">;<br><img id=Codehighlighter1_137_156_Open_Image onclick="this.style.display='none'; Codehighlighter1_137_156_Open_Text.style.display='none'; Codehighlighter1_137_156_Closed_Image.style.display='inline'; Codehighlighter1_137_156_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_137_156_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_137_156_Closed_Text.style.display='none'; Codehighlighter1_137_156_Open_Image.style.display='inline'; Codehighlighter1_137_156_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_137_156_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_137_156_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;只要父节点还大于子节点就循环&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(hole_index&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;min_heap_elem_greater(s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[parent],&nbsp;e))<br><img id=Codehighlighter1_226_383_Open_Image onclick="this.style.display='none'; Codehighlighter1_226_383_Open_Text.style.display='none'; Codehighlighter1_226_383_Closed_Image.style.display='inline'; Codehighlighter1_226_383_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_226_383_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_226_383_Closed_Text.style.display='none'; Codehighlighter1_226_383_Open_Image.style.display='inline'; Codehighlighter1_226_383_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_226_383_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_226_383_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_230_239_Open_Image onclick="this.style.display='none'; Codehighlighter1_230_239_Open_Text.style.display='none'; Codehighlighter1_230_239_Closed_Image.style.display='inline'; Codehighlighter1_230_239_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_230_239_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_230_239_Closed_Text.style.display='none'; Codehighlighter1_230_239_Open_Image.style.display='inline'; Codehighlighter1_230_239_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_230_239_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_230_239_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;交换位置&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[hole_index]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[parent])</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">min_heap_idx&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;hole_index;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hole_index&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;parent;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parent&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(hole_index&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;(s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[hole_index]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;e)</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">min_heap_idx&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;hole_index;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span></div>
<p></span><br></p>
<p></font><font size=2><strong><br>- 从堆里取元素：</strong><br>&nbsp;&nbsp;&nbsp; 大部分时候，从堆里取元素只局限于取根节点，因为这个节点是最有用的。对于数组存储结构而言，数组第一个元素即为根<br>节点。取完元素后，我们还需要重新调整整棵树以使其依然为一个heap。<br>&nbsp;&nbsp;&nbsp; 这需要保证两点：1.依然是完成树；2.父亲节点依然小于孩子节点。<br>&nbsp;&nbsp;&nbsp; 在具体实现heap的取元素操作时，具体到代码层次，方法都是有那么点微小差别的。libvent里的操作过程大致如图所示（实际上libevent中父节点的时间值小于子节点的时间值，时间值的比较通过evutil_timercmp实现）：</font></p>
<p><font size=2><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventmin_heap_E003/heap_remove.jpg"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=384 alt=heap_remove src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventmin_heap_E003/heap_remove_thumb.jpg" width=294 border=0></a> <br>&nbsp;&nbsp;&nbsp; 主要过程分为两步：<br>&nbsp;&nbsp;&nbsp; 1.比较左右孩子节点，选择最大的节点，移到父亲节点上；按照这种方式处理被选择的孩子节点，直到没有孩子节点为止。例如，<br>&nbsp;&nbsp;&nbsp; 当移除了100这个节点后，我们在100的孩子节点19和36两个节点里选择较大节点，即36，将36放置到100处；然后选择原来的36的<br>左右孩子25和1，选25放置于原来的36处；<br>&nbsp;&nbsp;&nbsp; 2.按照以上方式处理后，树会出现一个空缺，例如原先的25节点，因为被移动到原先的36处，25处就空缺了。因此，为了保证完 <br>成性，就将最右最下的叶子节点(也就是连续存储结构中最后一个元素)，例如这里的7，移动到空缺处，然后按照插入元素的方式处理<br>新插入的节点7。<br>&nbsp;&nbsp;&nbsp; 完成整个过程。 </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; libevent完成这个过程的函数主要是min_heap_shift_down_： <br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_0_36_Open_Image onclick="this.style.display='none'; Codehighlighter1_0_36_Open_Text.style.display='none'; Codehighlighter1_0_36_Closed_Image.style.display='inline'; Codehighlighter1_0_36_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_0_36_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_0_36_Closed_Text.style.display='none'; Codehighlighter1_0_36_Open_Image.style.display='inline'; Codehighlighter1_0_36_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span id=Codehighlighter1_0_36_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_0_36_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;hole_index&nbsp;为取出的元素的位置，e为最右最下的元素值&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;min_heap_shift_down_(min_heap_t</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;s,&nbsp;unsigned&nbsp;hole_index,&nbsp;</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;e)<br><img id=Codehighlighter1_117_764_Open_Image onclick="this.style.display='none'; Codehighlighter1_117_764_Open_Text.style.display='none'; Codehighlighter1_117_764_Closed_Image.style.display='inline'; Codehighlighter1_117_764_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_117_764_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_117_764_Closed_Text.style.display='none'; Codehighlighter1_117_764_Open_Image.style.display='inline'; Codehighlighter1_117_764_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_117_764_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_117_764_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_123_148_Open_Image onclick="this.style.display='none'; Codehighlighter1_123_148_Open_Text.style.display='none'; Codehighlighter1_123_148_Closed_Image.style.display='inline'; Codehighlighter1_123_148_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_123_148_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_123_148_Closed_Text.style.display='none'; Codehighlighter1_123_148_Open_Image.style.display='inline'; Codehighlighter1_123_148_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_123_148_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_123_148_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;取得hole_index的右孩子节点索引&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;min_child&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;(hole_index&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(min_child&nbsp;</span><span style="COLOR: #000000">&lt;=</span><span style="COLOR: #000000">&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">n)<br><img id=Codehighlighter1_230_687_Open_Image onclick="this.style.display='none'; Codehighlighter1_230_687_Open_Text.style.display='none'; Codehighlighter1_230_687_Closed_Image.style.display='inline'; Codehighlighter1_230_687_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_230_687_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_230_687_Closed_Text.style.display='none'; Codehighlighter1_230_687_Open_Image.style.display='inline'; Codehighlighter1_230_687_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_230_687_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_230_687_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_240_277_Open_Image onclick="this.style.display='none'; Codehighlighter1_240_277_Open_Text.style.display='none'; Codehighlighter1_240_277_Closed_Image.style.display='inline'; Codehighlighter1_240_277_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_240_277_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_240_277_Closed_Text.style.display='none'; Codehighlighter1_240_277_Open_Image.style.display='inline'; Codehighlighter1_240_277_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_240_277_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_240_277_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;有点恶心的一个表达式，目的就是取两个孩子节点中较大的那个孩子索引&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_child&nbsp;</span><span style="COLOR: #000000">-=</span><span style="COLOR: #000000">&nbsp;min_child&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">n&nbsp;</span><span style="COLOR: #000000">||</span><span style="COLOR: #000000">&nbsp;min_heap_elem_greater(s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[min_child],&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[min_child&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">]);<br><img id=Codehighlighter1_390_419_Open_Image onclick="this.style.display='none'; Codehighlighter1_390_419_Open_Text.style.display='none'; Codehighlighter1_390_419_Closed_Image.style.display='inline'; Codehighlighter1_390_419_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_390_419_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_390_419_Closed_Text.style.display='none'; Codehighlighter1_390_419_Open_Image.style.display='inline'; Codehighlighter1_390_419_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_390_419_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_390_419_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;找到了位置，这里似乎是个优化技巧，不知道具体原理&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">(min_heap_elem_greater(e,&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[min_child])))<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br><img id=Codehighlighter1_505_513_Open_Image onclick="this.style.display='none'; Codehighlighter1_505_513_Open_Text.style.display='none'; Codehighlighter1_505_513_Closed_Image.style.display='inline'; Codehighlighter1_505_513_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_505_513_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_505_513_Closed_Text.style.display='none'; Codehighlighter1_505_513_Open_Image.style.display='inline'; Codehighlighter1_505_513_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_505_513_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_505_513_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;换位置&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[hole_index]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;s</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">p[min_child])</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">min_heap_idx&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;hole_index;<br><img id=Codehighlighter1_596_607_Open_Image onclick="this.style.display='none'; Codehighlighter1_596_607_Open_Text.style.display='none'; Codehighlighter1_596_607_Closed_Image.style.display='inline'; Codehighlighter1_596_607_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_596_607_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_596_607_Closed_Text.style.display='none'; Codehighlighter1_596_607_Open_Image.style.display='inline'; Codehighlighter1_596_607_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_596_607_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_596_607_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;重复这个过程&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hole_index&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;min_child;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_child&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;(hole_index&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_693_719_Open_Image onclick="this.style.display='none'; Codehighlighter1_693_719_Open_Text.style.display='none'; Codehighlighter1_693_719_Closed_Image.style.display='inline'; Codehighlighter1_693_719_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_693_719_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_693_719_Closed_Text.style.display='none'; Codehighlighter1_693_719_Open_Image.style.display='inline'; Codehighlighter1_693_719_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_693_719_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_693_719_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;执行第二步过程，将最右最下的节点插到空缺处&nbsp;</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;min_heap_shift_up_(s,&nbsp;hole_index,&nbsp;&nbsp;e);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br></font>
<p><font size=2><strong>STL中的heap</strong> </font>
<p><font size=2>值得一提的是，STL中提供了heap的相关操作算法，借助于模板的泛化特性，其适用范围非常广泛。相关函数为：<br>make_heap, pop_heap, sort_heap, is_heap, sort 。其实现原理同以上算法差不多，相关代码在algorithm里。SGI的<br>STL在stl_heap.h里。 </font>
<p><font size=2><strong>参考资料：</strong></font>
<p><a href="http://www.cprogramming.com/tutorial/computersciencetheory/heap.html" target=_blank>What is a heap?</a>
<p><a href="http://en.wikipedia.org/wiki/Heap_(data_structure)" target=_blank>Heap_(data_structure)</a>
<p><a href="http://www.vias.org/cppcourse/chap22_07.html" target=_blank>Heap Remove</a> </p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/56511.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-07-18 15:56 <a href="http://www.cppblog.com/kevinlynx/archive/2008/07/18/56511.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>libevent 源码分析：evbuffer缓冲(附带libevent vs2005完整包下载)</title><link>http://www.cppblog.com/kevinlynx/archive/2008/07/16/56291.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 16 Jul 2008 06:28:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/07/16/56291.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/56291.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/07/16/56291.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/56291.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/56291.html</trackback:ping><description><![CDATA[<p><font size=2>Author : Kevin Lynx</font>
<p><font size=2><strong>前言</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 可以说对于任何网络库(模块)而言，一个缓冲模块都是必不可少的。缓冲模块主要用于缓冲从网络接收到的数据，以及<br>用户提交的数据(用于发送)。很多时候，我们还需要将网络模块层(非TCP层)的这些缓冲数据拷贝到用户层，而这些内存拷贝<br>都会消耗时间。<br>&nbsp;&nbsp;&nbsp; 在这里，我简要分析下libevent的相关代码(event.h和buffer.c)。 </font>
<p><font size=2><strong>结构</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 关于libevent的缓冲模块，主要就是围绕evbuffer结构体展开。先看下evbuffer的定义：<br></font>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_0_10_Open_Image onclick="this.style.display='none'; Codehighlighter1_0_10_Open_Text.style.display='none'; Codehighlighter1_0_10_Closed_Image.style.display='inline'; Codehighlighter1_0_10_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_0_10_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_0_10_Closed_Text.style.display='none'; Codehighlighter1_0_10_Open_Image.style.display='inline'; Codehighlighter1_0_10_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span id=Codehighlighter1_0_10_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_0_10_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">event.h</span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_28_213_Open_Image onclick="this.style.display='none'; Codehighlighter1_28_213_Open_Text.style.display='none'; Codehighlighter1_28_213_Closed_Image.style.display='inline'; Codehighlighter1_28_213_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_28_213_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_28_213_Closed_Text.style.display='none'; Codehighlighter1_28_213_Open_Image.style.display='inline'; Codehighlighter1_28_213_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;evbuffer&nbsp;</span><span id=Codehighlighter1_28_213_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_28_213_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;u_char&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">buffer;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;u_char&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">orig_buffer;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;misalign;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;totallen;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;off;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">cb)(</span><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;evbuffer&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">,&nbsp;size_t,&nbsp;size_t,&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">cbarg;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; libevent的缓冲是一个连续的内存区域，其处理数据的方式(写数据和读数据)更像一个队列操作方式：从后写入，从前<br>读出。evbuffer分别设置相关指针(一个指标)用于指示读出位置和写入位置。其大致结构如图：</font></p>
<p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventevbufferlibevent2005_CB59/evbuffer_str.jpg"><font size=2><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=140 alt=evbuffer_str src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/libeventevbufferlibevent2005_CB59/evbuffer_str_thumb.jpg" width=290 border=0></font></a><font size=2> <br>&nbsp;&nbsp;&nbsp; orig_buffer指向由realloc分配的连续内存区域，buffer指向有效数据的内存区域，totallen表示orig_buffer指向的内存<br>区域的大小，misalign表示buffer相对于orig_buffer的偏移，off表示有效数据的长度。 </font></p>
<p><font size=2><strong>实际运作</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 这里我将结合具体的代码分析libevent是如何操作上面那个队列式的evbuffer的，先看一些辅助函数： </font>
<p><font size=2>evbuffer_drain:<br>&nbsp;&nbsp;&nbsp; 该函数主要操作一些指标，当每次从evbuffer里读取数据时，libevent便会将buffer指针后移，同时增大misalign，减小off，<br>而该函数正是做这件事的。说白了，该函数就是用于调整缓冲队列的前向指标。 </font>
<p><font size=2>evbuffer_expand:<br>&nbsp;&nbsp;&nbsp; 该函数用于扩充evbuffer的容量。每次向evbuffer写数据时，都是将数据写到buffer+off后，buffer到buffer+off之间已被<br>使用，保存的是有效数据，而orig_buffer和buffer之间则是因为读取数据移动指标而形成的无效区域。<br>&nbsp;&nbsp;&nbsp; evbuffer_expand的扩充策略在于，首先判断如果让出orig_buffer和buffer之间的空闲区域是否可以容纳添加的数据，如果<br>可以，则移动buffer和buffer+off之间的数据到orig_buffer和orig_buffer+off之间(有可能发生内存重叠，所以这里移动调用的<br>是memmove)，然后把新的数据拷贝到orig_buffer+off之后；如果不可以容纳，那么重新分配更大的空间(realloc)，同样会移动<br>数据。<br>&nbsp;&nbsp;&nbsp; 扩充内存的策略为：确保新的内存区域最小尺寸为256，且以乘以2的方式逐步扩大(256、512、1024、...)。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 了解了以上两个函数，看其他函数就比较简单了。可以看看具体的读数据和写数据： </font>
<p><font size=2>evbuffer_add:<br>&nbsp;&nbsp;&nbsp; 该函数用于添加一段用户数据到evbuffer中。很简单，就是先判断是否有足够的空闲内存，如果没有则调用evbuffer_expand<br>扩充之，然后直接memcpy，更新off指标。 </font>
<p><font size=2>evbuffer_remove:<br>&nbsp;&nbsp;&nbsp; 该函数用于将evbuffer中的数据复制给用户空间(读数据)。简单地将数据memcpy，然后调用evbuffer_drain移动相关指标。 </font>
<p><font size=2><strong>其他</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 回过头看看libevent的evbuffer其实是非常简单的(跟我那个kl_net里的buffer一样)，不知道其他人有没有更优的缓冲管理<br>方案。evbuffer还提供了两个函数：evbuffer_write和evbuffer_read，用于直接在套接字(其他文件描述符)上写/读数据。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 另外，关于libevent，因为官方提供的VC工程文件有问题，很多人在windows下编译不过。金庆曾提供过一种方法。其实主要<br>就是修改event-config.h文件，修改编译相关配置。这里我也提供一个解决步骤，顺便提供完整包下载： </font>
<p><font size=2>1. vs2005打开libevent.dsw，转换四个工程(event_test, libevent, signal_test, time_test)<br>2. 删除libevent项目中所有的文件，重新添加文件，文件列表如下：<br>&nbsp;&nbsp; buffer.c<br>&nbsp;&nbsp; evbuffer.c<br>&nbsp;&nbsp; evdns.c<br>&nbsp;&nbsp; evdns.h<br>&nbsp;&nbsp; event.c<br>&nbsp;&nbsp; event.h<br>&nbsp;&nbsp; event_tagging.c<br>&nbsp;&nbsp; event-config.h<br>&nbsp;&nbsp; event-internal.h<br>&nbsp;&nbsp; evhttp.h<br>&nbsp;&nbsp; evrpc.h<br>&nbsp;&nbsp; evrpc.c<br>&nbsp;&nbsp; evrpc-internal.h<br>&nbsp;&nbsp; evsignal.h<br>&nbsp;&nbsp; evutil.c<br>&nbsp;&nbsp; evutil.h<br>&nbsp;&nbsp; http.c<br>&nbsp;&nbsp; http-internal.h<br>&nbsp;&nbsp; log.c<br>&nbsp;&nbsp; log.h<br>&nbsp;&nbsp; min_heap.h<br>&nbsp;&nbsp; strlcpy.c<br>&nbsp;&nbsp; strlcpy-internal.h<br>&nbsp;&nbsp; tree.h<br>&nbsp;&nbsp; win32.c<br>&nbsp;&nbsp; config.h<br>&nbsp;&nbsp; signal.c<br>3. 替换event-config.h，使用libevent-iocp中的<br>4. 项目设置里添加HAVE_CONFIG_H预处理宏<br>5. 修改win32.c中win32_init函数，加入WSAStartup函数，类似于：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WSADATA wd;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int err;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct win32op *winop;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t size;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( ( err = WSAStartup( MAKEWORD( 2, 2 ), &amp;wd ) ) != 0 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; event_err( 1, "winsock startup failed : %d", err );<br>6. 修改win32.c中win32_dealloc函数，在函数末尾加上WSACleanup的调用：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WSACleanup();<br>6. 至此libevent编译成功；<br>7. 几个例子程序，只需要加入HAVE_CONFIG_H预处理宏，以及连接ws2_32.lib即可；<br>&nbsp;&nbsp; (time_test需要修改time-test.c文件，即在包含event.h前包含windows.h)</font>
<p><a href="http://www.cppblog.com/Files/kevinlynx/libevent-1.4.5-stable-vs2005.zip" target=_blank><font size=2>libevent-1.4.5-stable-vs2005.zip下载</font></a>
<p><font size=2></font>&nbsp;
<p><font size=2></font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/56291.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-07-16 14:28 <a href="http://www.cppblog.com/kevinlynx/archive/2008/07/16/56291.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>建立异步操作组件:队列和线程</title><link>http://www.cppblog.com/kevinlynx/archive/2008/06/25/54554.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 25 Jun 2008 07:47:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/06/25/54554.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/54554.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/06/25/54554.html#Feedback</comments><slash:comments>29</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/54554.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/54554.html</trackback:ping><description><![CDATA[<p><font size=2>6.25.2008 </font>
<p><font size=2>Kevin Lynx </font>
<p><font size=2><strong>引言</strong> </font>
<p><font size=2>在一个高效的系统中，我们经常会将一些费时的操作转换为异步操作。例如往数据库中写日志。如果数据库<br>配置在网络上，那么往数据库中插入一些日志信息将非常慢(相对于程序其他部分)。 </font>
<p><font size=2><strong>如何转换为异步？</strong> </font>
<p><font size=2>将类似于以上过程转换为异步操作，一个典型的做法是：建立一个单独的数据库日志线程，一个线程安全的<br>队列。要写日志时，只需要往队列里放入数据，数据库日志线程则从这个队列里取数据然后完成写操作。 </font>
<p><font size=2>大致的过程类似于： </font>
<p><font size=2><strong></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">typedef&nbsp;safe_list</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;SafeList;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>SafeList&nbsp;gLogList;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;database&nbsp;thread.read&nbsp;log&nbsp;string.</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">unsigned&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;__stdcall&nbsp;DBThread(&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">p&nbsp;)<br><img id=Codehighlighter1_141_276_Open_Image onclick="this.style.display='none'; Codehighlighter1_141_276_Open_Text.style.display='none'; Codehighlighter1_141_276_Closed_Image.style.display='inline'; Codehighlighter1_141_276_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_141_276_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_141_276_Closed_Text.style.display='none'; Codehighlighter1_141_276_Open_Image.style.display='inline'; Codehighlighter1_141_276_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_141_276_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_141_276_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(&nbsp;</span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">&nbsp;)<br><img id=Codehighlighter1_165_235_Open_Image onclick="this.style.display='none'; Codehighlighter1_165_235_Open_Text.style.display='none'; Codehighlighter1_165_235_Closed_Image.style.display='inline'; Codehighlighter1_165_235_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_165_235_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_165_235_Closed_Text.style.display='none'; Codehighlighter1_165_235_Open_Image.style.display='inline'; Codehighlighter1_165_235_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_165_235_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_165_235_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;read&nbsp;gLogList&nbsp;and&nbsp;write&nbsp;log&nbsp;string&nbsp;into&nbsp;the&nbsp;database</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;_endthreadex(&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;other&nbsp;threads.&nbsp;write&nbsp;log&nbsp;string.</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;wrtie_log(&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;std::</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">log&nbsp;)<br><img id=Codehighlighter1_357_390_Open_Image onclick="this.style.display='none'; Codehighlighter1_357_390_Open_Text.style.display='none'; Codehighlighter1_357_390_Closed_Image.style.display='inline'; Codehighlighter1_357_390_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_357_390_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_357_390_Closed_Text.style.display='none'; Codehighlighter1_357_390_Open_Image.style.display='inline'; Codehighlighter1_357_390_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_357_390_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_357_390_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;gLogList.push_back(&nbsp;log&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>将他们包装起来</strong> </font>
<p><font size=2>我们很有可能会在同一个系统中多次遇到类似需要转换为异步操作的地方，如果每一次都手动去创建一个队列和一<br>个线程，那将会多么乏味啊！懒惰的程序员喜欢重用各种代码。所以，我自己觉得很有必要将这一切封装起来。我<br>们只需要封装这个队列和创建线程的繁琐细节，让应用层全部专注于具体的逻辑处理： </font>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">template&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">typename&nbsp;_NodeType</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;async_operator<br><img id=Codehighlighter1_51_1052_Open_Image onclick="this.style.display='none'; Codehighlighter1_51_1052_Open_Text.style.display='none'; Codehighlighter1_51_1052_Closed_Image.style.display='inline'; Codehighlighter1_51_1052_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_51_1052_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_51_1052_Closed_Text.style.display='none'; Codehighlighter1_51_1052_Open_Image.style.display='inline'; Codehighlighter1_51_1052_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_51_1052_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_51_1052_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img id=Codehighlighter1_65_88_Open_Image onclick="this.style.display='none'; Codehighlighter1_65_88_Open_Text.style.display='none'; Codehighlighter1_65_88_Closed_Image.style.display='inline'; Codehighlighter1_65_88_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_65_88_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_65_88_Closed_Text.style.display='none'; Codehighlighter1_65_88_Open_Image.style.display='inline'; Codehighlighter1_65_88_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_65_88_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_65_88_Open_Text><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;the&nbsp;list&nbsp;node&nbsp;type.</span><span style="COLOR: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;_NodeType&nbsp;node_type;<br><img id=Codehighlighter1_126_139_Open_Image onclick="this.style.display='none'; Codehighlighter1_126_139_Open_Text.style.display='none'; Codehighlighter1_126_139_Closed_Image.style.display='inline'; Codehighlighter1_126_139_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_126_139_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_126_139_Closed_Text.style.display='none'; Codehighlighter1_126_139_Open_Image.style.display='inline'; Codehighlighter1_126_139_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_126_139_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_126_139_Open_Text><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;the&nbsp;list.</span><span style="COLOR: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;multi_list</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">node_type,&nbsp;Mutex,&nbsp;Semaphore</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;list_type;<br><img id=Codehighlighter1_207_229_Open_Image onclick="this.style.display='none'; Codehighlighter1_207_229_Open_Text.style.display='none'; Codehighlighter1_207_229_Closed_Image.style.display='inline'; Codehighlighter1_207_229_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_207_229_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_207_229_Closed_Text.style.display='none'; Codehighlighter1_207_229_Open_Image.style.display='inline'; Codehighlighter1_207_229_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_207_229_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_207_229_Open_Text><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;operator&nbsp;signature</span><span style="COLOR: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;functor</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">,&nbsp;TYPE_LIST1(&nbsp;list_type</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;)</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;operator_type;<br><img id=Codehighlighter1_301_360_Open_Image onclick="this.style.display='none'; Codehighlighter1_301_360_Open_Text.style.display='none'; Codehighlighter1_301_360_Closed_Image.style.display='inline'; Codehighlighter1_301_360_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_301_360_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_301_360_Closed_Text.style.display='none'; Codehighlighter1_301_360_Open_Image.style.display='inline'; Codehighlighter1_301_360_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_301_360_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_301_360_Open_Text><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;init&nbsp;function&nbsp;signature,&nbsp;called&nbsp;when&nbsp;the&nbsp;thread&nbsp;starts.</span><span style="COLOR: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;functor</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;init_type;<br><img id=Codehighlighter1_402_437_Open_Image onclick="this.style.display='none'; Codehighlighter1_402_437_Open_Text.style.display='none'; Codehighlighter1_402_437_Closed_Image.style.display='inline'; Codehighlighter1_402_437_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_402_437_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_402_437_Closed_Text.style.display='none'; Codehighlighter1_402_437_Open_Image.style.display='inline'; Codehighlighter1_402_437_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_402_437_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_402_437_Open_Text><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;called&nbsp;before&nbsp;the&nbsp;thread&nbsp;exits.</span><span style="COLOR: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;functor</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;release_type;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img id=Codehighlighter1_492_619_Open_Image onclick="this.style.display='none'; Codehighlighter1_492_619_Open_Text.style.display='none'; Codehighlighter1_492_619_Closed_Image.style.display='inline'; Codehighlighter1_492_619_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_492_619_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_492_619_Closed_Text.style.display='none'; Codehighlighter1_492_619_Open_Image.style.display='inline'; Codehighlighter1_492_619_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_492_619_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_492_619_Open_Text><span style="COLOR: #808080">///</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;start&nbsp;the&nbsp;thread&nbsp;and&nbsp;execute&nbsp;the&nbsp;operation.<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;@param&nbsp;op&nbsp;the&nbsp;callback&nbsp;function&nbsp;operate&nbsp;the&nbsp;list&nbsp;node.<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;</span><span style="COLOR: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;execute(&nbsp;operator_type&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">op,&nbsp;init_type&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">init&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;init_type(),&nbsp;release_type&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">release&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;release_type()&nbsp;);&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img id=Codehighlighter1_736_810_Open_Image onclick="this.style.display='none'; Codehighlighter1_736_810_Open_Text.style.display='none'; Codehighlighter1_736_810_Closed_Image.style.display='inline'; Codehighlighter1_736_810_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_736_810_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_736_810_Closed_Text.style.display='none'; Codehighlighter1_736_810_Open_Image.style.display='inline'; Codehighlighter1_736_810_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_736_810_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_736_810_Open_Text><span style="COLOR: #808080">///</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;exit&nbsp;the&nbsp;thread.It&nbsp;will&nbsp;block&nbsp;until&nbsp;the&nbsp;thread&nbsp;exited.<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;exit();&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img id=Codehighlighter1_834_899_Open_Image onclick="this.style.display='none'; Codehighlighter1_834_899_Open_Text.style.display='none'; Codehighlighter1_834_899_Closed_Image.style.display='inline'; Codehighlighter1_834_899_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_834_899_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_834_899_Closed_Text.style.display='none'; Codehighlighter1_834_899_Open_Image.style.display='inline'; Codehighlighter1_834_899_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_834_899_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_834_899_Open_Text><span style="COLOR: #808080">///</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;get&nbsp;the&nbsp;list&nbsp;so&nbsp;that&nbsp;you&nbsp;can&nbsp;pust&nbsp;list&nbsp;nodes.<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #808080">///</span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;list_type&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">list();&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;list_type&nbsp;_list;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;operator_type&nbsp;_operator;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;init_type&nbsp;_init;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;release_type&nbsp;_release;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;thread&nbsp;_thread;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>我利用了已有的组件：线程安全的容器multi_list、包装任意执行体的functor、线程维护类thread。那么，现在，<br>应用层只需要定义队列节点类型，写应用相关的回调函数(任意可被functor包装的广义函数)。(见附件例子) </font>
<p><font size=2>之所以为这个组件加上init和release，是因为有些东西(例如COM)需要在线程启动时初始化，而在线程快结束时释放，例如对于<br>使用COM的应用来说，就需要在线程初始化时CoInitialize，结束时CoUninitialize。 </font>
<p><font size=2><strong>闲说下其他东西</strong> </font>
<p><font size=2>在本文的附件代码里，你可以获取到functor、thread、multi_list这些东西，所以我有必要提一下。 </font>
<p><font size=2>关于functor，你可以参看&lt;实现functor - 增强型的函数指针&gt;，基本上可以看成增强版的C回调函数；至于multi_list，基本上<br>是一个container adapter (套用下STL的概念)，使用条件变量参与线程同步，据说效率要比简单的互斥高点；至于thread，我需要<br>特别说下： </font>
<p><font size=2>thread最为重要的就是为其附加了一个windows的消息队列(只要调用PeekMessage之类的函数该队列就存在)，本意是可以让其他线<br>程传送数据到该线程，但是目前只用于线程退出，即其他线程可以在任何时候要求该线程安全地退出(该线程没有阻塞的情况下，<br>阻塞时获取不到消息)。我不知道这个安全退出策略是否真的有必要存在，但是我讨厌看到各种撇脚的退出方法(例如设置全局标志<br>变量，增加额外的--没封装前---event对象之类)。 </font>
<p><font size=2><strong>结束</strong> </font>
<p><font size=2>不知道其他人是如何做这种异步转换操作的，在这里我只是起个抛砖引玉的作用，欢迎大家提出意见。 </font>
<p>&nbsp;</p>
<a href="http://www.cppblog.com/Files/kevinlynx/async_logger.rar" target=_blank>例子下载</a> 
<img src ="http://www.cppblog.com/kevinlynx/aggbug/54554.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-06-25 15:47 <a href="http://www.cppblog.com/kevinlynx/archive/2008/06/25/54554.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SGI STL的内存池</title><link>http://www.cppblog.com/kevinlynx/archive/2008/06/12/53054.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 12 Jun 2008 13:26:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/06/12/53054.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/53054.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/06/12/53054.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/53054.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/53054.html</trackback:ping><description><![CDATA[<p><font size=2>stl中各种容器都有一个可选的模板参数：allocator，也就是一个负责内存分配的组件。STL标准规定的allcator<br>被定义在memory文件中。STL标准规定的allocator只是单纯地封装operator new，效率上有点过意不去。 </font>
<p><font size=2>SGI实现的STL里，所有的容器都使用SGI自己定义的allocator。这个allocator实现了一个small object的内存池。<br>Loki里为了处理小对象的内存分配，也实现了类似的内存管理机制。 </font>
<p><font size=2>该内存池大致上，就是一大块一大块地从系统获取内存，然后将其分成很多小块以链表的形式链接起来。其内部<br>有很多不同类型的链表，不同的链表维护不同大小的内存块。每一次客户端要求分配内存时，allcator就根据请求<br>的大小找到相应的链表(最接近的尺寸)，然后从链表里取出内存。当客户端归还内存时，allocator就将这块内存<br>放回到对应的链表里。 </font>
<p><font size=2>我简单地画了幅图表示整个结构：</font>
<p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/SGISTL_12DE6/allocator.jpg"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=352 alt=allocator src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/SGISTL_12DE6/allocator_thumb.jpg" width=434 border=0></a>
<p><font size=2>allocator内部维护一个链表数组，数组元素全部是链表头指针。链表A每一个节点维护一个8bytes的内存块，链表<br>B每一个节点维护一个16bytes的内存块。 </font>
<p><font size=2>当客户端请求分配10bytes的内存时，allocator将10调整为最接近的16bytes(只能大于10bytes)，然后发现16bytes<br>这个链表(链表B)里有可用内存块，于是从B里取出一块内存返回。当客户端归还时，allocator找到对应的链表，将<br>内存重新放回链表B即可。 </font>
<p><font size=2>大致过程就这么简单，也许有人要说用链表维护一块内存，链表本身就会浪费一些内存(在我很早前接触内存池时，<br>总会看到类似的论点= =|)，其实通过一些简单的技巧是完全可以避免的。例如，这里allocator维护了很多内存块，<br>反正这些内存本身就是闲置的，因此我们就可以直接在这些内存里记录链表的信息(下一个元素)。 </font>
<p><font size=2>还是写点代码详细说下这个小技巧：</font>
<p>&nbsp;&nbsp;&nbsp; </p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;Obj<br><img id=Codehighlighter1_15_40_Open_Image onclick="this.style.display='none'; Codehighlighter1_15_40_Open_Text.style.display='none'; Codehighlighter1_15_40_Closed_Image.style.display='inline'; Codehighlighter1_15_40_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_15_40_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_15_40_Closed_Text.style.display='none'; Codehighlighter1_15_40_Open_Image.style.display='inline'; Codehighlighter1_15_40_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_15_40_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_15_40_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Obj&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">next;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">;&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">mem&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;malloc(&nbsp;</span><span style="COLOR: #000000">100</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Obj&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">header&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(Obj</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)&nbsp;mem;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Obj&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">cur_obj&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;header;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;Obj&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">next_obj&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;cur_obj;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;;&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">&nbsp;i&nbsp;)<br><img id=Codehighlighter1_195_441_Open_Image onclick="this.style.display='none'; Codehighlighter1_195_441_Open_Text.style.display='none'; Codehighlighter1_195_441_Closed_Image.style.display='inline'; Codehighlighter1_195_441_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_195_441_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_195_441_Closed_Text.style.display='none'; Codehighlighter1_195_441_Open_Image.style.display='inline'; Codehighlighter1_195_441_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_195_441_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_195_441_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cur_obj&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;next_obj;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;next_obj&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(Obj</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)((</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)next_obj&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;i&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">9</span><span style="COLOR: #000000">&nbsp;)<br><img id=Codehighlighter1_304_364_Open_Image onclick="this.style.display='none'; Codehighlighter1_304_364_Open_Text.style.display='none'; Codehighlighter1_304_364_Closed_Image.style.display='inline'; Codehighlighter1_304_364_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_304_364_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_304_364_Closed_Text.style.display='none'; Codehighlighter1_304_364_Open_Image.style.display='inline'; Codehighlighter1_304_364_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_304_364_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_304_364_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cur_obj</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">next&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_387_435_Open_Image onclick="this.style.display='none'; Codehighlighter1_387_435_Open_Text.style.display='none'; Codehighlighter1_387_435_Closed_Image.style.display='inline'; Codehighlighter1_387_435_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_387_435_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_387_435_Closed_Text.style.display='none'; Codehighlighter1_387_435_Open_Image.style.display='inline'; Codehighlighter1_387_435_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_387_435_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_387_435_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cur_obj</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">next&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;next_obj;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;free(&nbsp;mem&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p>&nbsp;
<p><font size=2>这样，通过header指针和next域，就可以逐块(这里是10byts)地访问mem所指向的内存，而这些链表的节点，都<br>是直接保存在这块内存里的，所以完全没有额外消耗。 </font>
<p><font size=2>我用C模仿着SGI的这个allocator写了个可配置的内存池，在其上按照STL的标准包装了一个allocator，可以直接<br>用于VC自带的STL里。</font><a href="http://www.cppblog.com/Files/kevinlynx/alloc.rar" target=_blank><font size=2>测试代码</font></a><font size=2>稍微测试了下，发现在不同的机器上有明显的差距。 </font></p>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/53054.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-06-12 21:26 <a href="http://www.cppblog.com/kevinlynx/archive/2008/06/12/53054.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>调试经验总结-VC下的错误对话框(陆续更新6.12.2008)</title><link>http://www.cppblog.com/kevinlynx/archive/2008/04/24/47998.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 24 Apr 2008 05:43:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/04/24/47998.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/47998.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/04/24/47998.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/47998.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/47998.html</trackback:ping><description><![CDATA[<p><font size="2">很早前就想写点总结将编程中遇到的各种错误刨根挖底地罗列出来。但是因为这些错误（VC中开调试器遇到的各种错误对话框）都是随机性的，真正想总结的时候又不想不起来有哪些错误。恰好最近运气比较背，各种错误都被我遇遍了，于是恰好有机会做个总结。 </font> <p><font size="2">这里所说的VC下的错误对话框时指在VC中开调试器运行程序时，IDE弹出的对话框。</font>  <p><strong>1.不是错误的错误：断言</strong> .</p> <p>将断言视为错误其实有点可笑，但是因为有些同学甚至不知道这个，所以我稍微提一下。断言对话框大致上类似于：</p> <p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/assert_1.jpg"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="257" alt="assert" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/assert_thumb_1.jpg" width="444" border="0"></a> </p> <p>断言对话框是由assert引起的，在对话框上通常会给出表达式，例如assert( 0 ); 弹出对话框时就会将0这个表达式显示出来（Expression:0)。关于assert的具体信息建议自己google。这里稍微提一下一个技巧：有时候为了让assert提供更多的信息，我们可以这样写一个assert:</p> <p>assert( expression &amp;&amp; "Function : invalid argument!" ); </p> <p>因为字符串被用在布尔表达式中时，始终为true，不会妨碍对expression的判断，当断言发生时(expression为false) 时，断言对话框上就会显示这个字符串，从而方便我们调试。</p> <p>要解决这个问题，首先要确定断言发生的位置，如果是你自己设置的断言被引发，就很好解决，如果是系统内部的函数产生的，那么一般是因为你传入的函数参数无效引起。</p> <p>&nbsp;</p> <p><strong>2.内存相关：最简单的非法访问：</strong></p> <p>C、C++程序中经常误用无效的指针，从而大致各种各样的非法内存访问（写/读）。最简单的情况类似于：</p> <p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/wrongaccess.jpg"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="186" alt="wrongaccess" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/wrongaccess_thumb.jpg" width="444" border="0"></a> </p> <p>这样的情况由类似以下代码引起：</p> <p>char *p = 0;  <p>*p = 'a';  <p>当你看到类似于“写入位置XXXX时发生访问冲突“时，那么你大致可以断定，你的程序在某个地方访问到非法内存。开调试器对调用堆栈进行跟踪即可找出错误。</p> <p>&nbsp;</p> <p><strong>3.内存相关：不小心的栈上数组越界：</strong></p> <p>当你写下类似以下的代码时：</p> <p>char str[3];  <p>strcpy( str, "abc" );  <p>就将看到如下的对话框：  <p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/stackerror.jpg"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="235" alt="stackerror" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/stackerror_thumb.jpg" width="516" border="0"></a>&nbsp; <p>对话框大致的意思就是说str周围的栈被破坏了，因为str本身就被放在栈上，所以strcpy(str,"abc")多写入的'\0'就写到非法的栈区域。看到这样的对话框可以根据调用堆栈定位到错误发生的函数，然后检查此函数内部定义的数组访问，即可解决问题。  <p>&nbsp; <p><strong>4.内存相关：不小心的堆上数组越界：<br></strong>并不是每次数组越界都会得到上面所描述的错误，当数组是在堆上分配时，情况就变得隐秘得多：  <p>char *str = new char [2];  <p>strcpy( str, "ab" ); //执行到这里时并不见得会崩溃  <p>delete [] str;//但是到这里时就肯定会崩溃  <p>以上代码导致的错误对话框还要诡异些：  <p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/heaperror.jpg"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="206" alt="heaperror" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/heaperror_thumb.jpg" width="516" border="0"></a>  <p>似乎不同的DAMAGE对应的错误号（这里是47）都不一样，因为这里的错误发生在delete，而delete跟new很可能在不同的地方，所以这个错误调试起来不是那么容易，很多时候只能靠经验。  <p>当看到类似的对话框时，根据调用堆栈跟到delete时，你就可以大致怀疑堆上数组越界。  <p>&nbsp; <p><strong>5.调用相关：函数调用约定带来的错误：</strong>  <p>这是所有我这里描述的错误中最诡异的一种，先看下对话框大致的样子：  <p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/run_functioncall2.jpg"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="195" alt="run_functioncall2" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/run_functioncall2_thumb.jpg" width="446" border="0"></a>  <p>对话框大致的意思就是说（没开调试器时对话框样式可能不一样），通过函数指针调用某个函数时，函数指针的类型（函数原型）可能与函数指针指向的函数的类型不一样。这里的类型不一致主要是调用约定(call conversation）不一样。如果函数类型（参数个数，返回值）不一样，一般不会出错。  <p>调用约定是指调用一个函数时，函数参数的压入顺序、谁来清理栈的内容等。例如默认的C、C++调用约定__cdecl，对于函数的参数是从右往左压入。而__stdcall（WIN API的调用约定）则是从左向右压。我这里所说的函数类型不一样，就是指一个函数是使用__cdecl，还是__stdcall。例如以下代码：  <p>&nbsp;</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #000000">#include </span><span style="color: #000000">&lt;</span><span style="color: #000000">iostream</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #0000ff">void</span><span style="color: #000000"> __stdcall show( </span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">char</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">*</span><span style="color: #000000">str ) <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img id="Codehighlighter1_63_67_Open_Image" onclick="this.style.display='none'; Codehighlighter1_63_67_Open_Text.style.display='none'; Codehighlighter1_63_67_Closed_Image.style.display='inline'; Codehighlighter1_63_67_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_63_67_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_63_67_Closed_Text.style.display='none'; Codehighlighter1_63_67_Open_Image.style.display='inline'; Codehighlighter1_63_67_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"></span><span id="Codehighlighter1_63_67_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_63_67_Open_Text"><span style="color: #000000">{ <br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">}</span></span><span style="color: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #0000ff">void</span><span style="color: #000000"> __stdcall show2() <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img id="Codehighlighter1_96_100_Open_Image" onclick="this.style.display='none'; Codehighlighter1_96_100_Open_Text.style.display='none'; Codehighlighter1_96_100_Closed_Image.style.display='inline'; Codehighlighter1_96_100_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_96_100_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_96_100_Closed_Text.style.display='none'; Codehighlighter1_96_100_Open_Image.style.display='inline'; Codehighlighter1_96_100_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"></span><span id="Codehighlighter1_96_100_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_96_100_Open_Text"><span style="color: #000000">{ <br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">}</span></span><span style="color: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span><span style="color: #0000ff">int</span><span style="color: #000000"> main() <br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img id="Codehighlighter1_117_239_Open_Image" onclick="this.style.display='none'; Codehighlighter1_117_239_Open_Text.style.display='none'; Codehighlighter1_117_239_Closed_Image.style.display='inline'; Codehighlighter1_117_239_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_117_239_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_117_239_Closed_Text.style.display='none'; Codehighlighter1_117_239_Open_Image.style.display='inline'; Codehighlighter1_117_239_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"></span><span id="Codehighlighter1_117_239_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_117_239_Open_Text"><span style="color: #000000">{ <br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">typedef </span><span style="color: #0000ff">void</span><span style="color: #000000"> (</span><span style="color: #000000">*</span><span style="color: #000000">Func)( </span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">char</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">*</span><span style="color: #000000">); <br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"></span><span style="color: #0000ff">void</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">*</span><span style="color: #000000">p </span><span style="color: #000000">=</span><span style="color: #000000"> show; <br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">Func my_func </span><span style="color: #000000">=</span><span style="color: #000000"> (Func) p; <br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">my_func( </span><span style="color: #000000">"</span><span style="color: #000000">kevin</span><span style="color: #000000">"</span><span style="color: #000000"> ); <br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"></span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">; <br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">}</span></span><span style="color: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p>&nbsp;</p> <p>因为Func默认地被处理为__cdecl，而show是__stdcall的，所以当通过函数指针my_func时，就导致了以上对话框的出现。但是当p指向show2时，又不会出错，这是因为show2没有参数，不同的调用约定不影响这个规则。<br><br></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><strong style="mso-bidi-font-weight: normal"><span lang="EN-US">6.</span></strong><strong style="mso-bidi-font-weight: normal"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">异常相关：默认终止程序</span><span lang="EN-US"><o:p></o:p></span></strong></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">当我们使用</span><span lang="EN-US">C++</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">库时，因为库本身可能会抛出</span><span lang="EN-US">C++</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">异常，如果你不捕获这个异常，那么</span><span lang="EN-US">C++</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">默认就会调用</span><span lang="EN-US">abort</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（或者</span><span lang="EN-US">exit</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）函数终止程序。例如：</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span lang="EN-US"><o:p>&nbsp;</p> <div style="border-right: #cccccc 1px solid; padding-right: 5px; border-top: #cccccc 1px solid; padding-left: 4px; font-size: 13px; padding-bottom: 4px; border-left: #cccccc 1px solid; width: 98%; word-break: break-all; padding-top: 4px; border-bottom: #cccccc 1px solid; background-color: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"><span style="color: #0000ff">void</span><span style="color: #000000"> test()<br><img id="Codehighlighter1_12_61_Open_Image" onclick="this.style.display='none'; Codehighlighter1_12_61_Open_Text.style.display='none'; Codehighlighter1_12_61_Closed_Image.style.display='inline'; Codehighlighter1_12_61_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_12_61_Closed_Image" style="display: none" onclick="this.style.display='none'; Codehighlighter1_12_61_Closed_Text.style.display='none'; Codehighlighter1_12_61_Open_Image.style.display='inline'; Codehighlighter1_12_61_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"></span><span id="Codehighlighter1_12_61_Closed_Text" style="border-right: #808080 1px solid; border-top: #808080 1px solid; display: none; border-left: #808080 1px solid; border-bottom: #808080 1px solid; background-color: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id="Codehighlighter1_12_61_Open_Text"><span style="color: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">throw</span><span style="color: #000000"> std::exception( </span><span style="color: #000000">"</span><span style="color: #000000">some exceptions</span><span style="color: #000000">"</span><span style="color: #000000"> );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">}</span></span><span style="color: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align="top"></span></div> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"></o:p></span>&nbsp;</p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">当你调用</span><span lang="EN-US">test</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">函数时，如果不</span><span lang="EN-US">catch</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这个异常，开调试器就会得到类似的错误对话框：</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span lang="EN-US"><o:p><img height="199" alt="" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/exception.jpg" width="443" border="0">&nbsp;</o:p></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">而如果不开调试器，则会得到：</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span lang="EN-US"><o:p>&nbsp;<img height="232" alt="" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/abort.jpg" width="439" border="0"></o:p></span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">当你看到类似于“</span><span lang="EN-US">This application has requested the Runtime to terminate it…”</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">之类的字眼时，那就表明程序调用了</span><span lang="EN-US">abort</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（或</span><span lang="EN-US">exit</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）函数，导致程序异常终止。其实这个错误只要开调试器，一般可以准确定位错误的发生点。</span></p> <p class="MsoNormal" style="margin: 0cm 0cm 0pt"><span lang="EN-US"><o:p>&nbsp;</o:p></span></p> <p><strong>7.VC运行时检查-未初始化变量</strong></p> <p>VC的调试器会对代码进行运行时检查，这可能会导致VC弹出对你看上去正确的代码。这也许不是一个错误。例如：</p> <p>int test_var;  <p>if( test_var == -1 )<br>{<br>&nbsp;&nbsp;&nbsp; test_var = 0;<br>}  <p>test_var没有初始化就进行if判断，当运行以上代码开调试器时，就会得到如下对话框：  <p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/withoutinit.jpg"><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="192" alt="withoutinit" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/withoutinit_thumb.jpg" width="442" border="0"></a> <p><strong>8.破坏的堆</strong> <p>VC对于在堆上分配的内存都做了记录，我想这主要用于free释放内存时做归还处理。 <p>char *p = (char*) malloc( 100 );<br>p += 10;<br>free( p ); <p>当执行以上代码时，因为p的值已经改变，提交到free的指针值变化，VC就会给出以下错误提示： <p><a href="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/bad_heap.jpg"><img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="190" alt="bad_heap" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/WindowsLiveWriter/VC_BA3E/bad_heap_thumb.jpg" width="443" border="0"></a></p><img src ="http://www.cppblog.com/kevinlynx/aggbug/47998.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-04-24 13:43 <a href="http://www.cppblog.com/kevinlynx/archive/2008/04/24/47998.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TCP/IP Concepts 1</title><link>http://www.cppblog.com/kevinlynx/archive/2008/04/18/47471.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Fri, 18 Apr 2008 02:02:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/04/18/47471.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/47471.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/04/18/47471.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/47471.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/47471.html</trackback:ping><description><![CDATA[<p><br><font style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #ffffff">最近在草草地看&lt;TCP/IP详解&gt;TCP那一部分，之所以草草地看是因为觉得早晚一天会回过头去细看。手头上<br>有工作要做，所以先草草地把之前随便摘抄的TCP/IP相关概念贴出来：<br><br>继续草草地贴:<br><br>--------------------------------------------------------------------------------------------------------------------------------------------------------<br>TCP segment:<br>Thus, we have simply &#8220;passed the buck&#8221; to TCP, which must take the stream from the application <br>and divide it into discrete messages for IP. These messages are called TCP segments.</font></p>
<p><font style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #ffffff">On regular intervals, it forms segments to be transmitted using IP. The size of the segment is <br>controlled by two primary factors. The first issue is that there is an overall limit to the size <br>of a segment, chosen to prevent unnecessary fragmentation at the IP layer. This is governed by a <br>parameter called the maximum segment size (MSS), which is determined during connection establishment. <br>The second is that TCP is designed so that once a connection is set up, each of the devices tells the <br>other how much data it is ready to accept at any given time. If this is lower than the MSS value, a <br>smaller segment must be sent. This is part of the sliding window system described in the next topic.</font></p>
<p><font style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #ffffff">Since TCP works with individual bytes of data rather than discrete messages, it must use an <br>identification scheme that works at the byte level to implement its data transmission and tracking <br>system. This is accomplished by assigning each byte TCP processes a sequence number.</font></p>
<p><font style="FONT-SIZE: 10pt; BACKGROUND-COLOR: #ffffff"><br>Since applications send data to TCP as a stream of bytes and not prepackaged messages, each <br>application must use its own scheme to determine where one application data element ends and the <br>next begins.<br><br>--------------------------------------------------------------------------------------------------------------------------------------------------------<br></p>
<p>TCP MSS:<br><a href="http://www.tcpipguide.com/free/t_TCPMaximumSegmentSizeMSSandRelationshiptoIPDatagra.htm">http://www.tcpipguide.com/free/t_TCPMaximumSegmentSizeMSSandRelationshiptoIPDatagra.htm</a></p>
<p>In addition to the dictates of the current window size, each TCP device also has associated <br>with it a ceiling on TCP size—a segment size that will never be exceeded regardless of how<br>&nbsp;large the current window is. This is called the maximum segment size (MSS). When deciding <br>how much data to put into a segment, each device in the TCP connection will choose the amount<br>&nbsp;based on the current window size, in conjunction with the various algorithms described in <br>the reliability section, but it will never be so large that the amount of data exceeds the<br>&nbsp;MSS of the device to which it is sending. </p>
<p><br>Note: I need to point out that the name &#8220;maximum segment size&#8221; is in fact misleading. The<br>&nbsp;value actually refers to the maximum amount of data that a segment can hold—it does not <br>include the TCP headers. So if the MSS is 100, the actual maximum segment size could be 120 <br>(for a regular TCP header) or larger (if the segment includes TCP options). </p>
<p>This was computed by starting with the minimum MTU for IP networks of 576. </p>
<p>Devices can indicate that they wish to use a different MSS value from the default by including <br>a Maximum Segment Size option in the SYN message they use to establish a connection. Each <br>device in the connection may use a different MSS value.<br><br>--------------------------------------------------------------------------------------------------------------------------------------------------------<br><br>delayed ACK algorithm<br><br><a href="http://tangentsoft.net/wskfaq/intermediate.html#delayed-ack">http://tangentsoft.net/wskfaq/intermediate.html#delayed-ack</a></p>
<p>In a simpleminded implementation of TCP, every data packet that comes in is immediately acknowledged<br>&nbsp;with an ACK packet. (ACKs help to provide the reliability TCP promises.)</p>
<p>In modern stacks, ACKs are delayed for a short time (up to 200ms, typically) for three reasons: a) <br>to avoid the silly window syndrome; b) to allow ACKs to piggyback on a reply frame if one is ready <br>to go when the stack decides to do the ACK; and c) to allow the stack to send one ACK for several <br>frames, if those frames arrive within the delay period.</p>
<p>The stack is only allowed to delay ACKs for up to 2 frames of data.</p>
<p><br>&nbsp;</p>
<p>--------------------------------------------------------------------------------------------------------------------------------------------------------<br>Nagle algorithm:</p>
<p>Nagle's algorithm, named after John Nagle, is a means of improving the efficiency of TCP/IP networks by reducing the number of packets that need to be sent over the network.</p>
<p>Nagle's document, Congestion Control in IP/TCP Internetworks (RFC896) describes what he called the 'small packet problem', where an application repeatedly emits data in small chunks, frequently only 1 byte in size. Since TCP packets have a 40 byte header (20 bytes for TCP, 20 bytes for IPv4), this results in a 41 byte packet for 1 byte of useful information, a huge overhead. This situation often occurs in Telnet sessions, where most keypresses generate a single byte of data which is transmitted immediately. Worse, over slow links, many such packets can be in transit at the same time, potentially leading to congestion collapse.</p>
<p>Nagle's algorithm works by coalescing a number of small outgoing messages, and sending them all at once. Specifically, as long as there is a sent packet for which the sender has received no acknowledgment, the sender should keep buffering its output until it has a full packet's worth of output, so that output can be sent all at once.</p>
<p><br>[edit] Algorithm<br>if there is new data to send<br>&nbsp; if the window size &gt;= MSS and available data is &gt;= MSS<br>&nbsp;&nbsp;&nbsp; send complete MSS segment now<br>&nbsp; else<br>&nbsp;&nbsp;&nbsp; if there is unconfirmed data still in the pipe<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enqueue data in the buffer until an acknowledge is received<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; send data immediately<br>&nbsp;&nbsp;&nbsp; end if<br>&nbsp; end if<br>end if<br>where MSS = Maximum segment size</p>
<p>This algorithm interacts badly with TCP delayed acknowledgments, a feature introduced into TCP at roughly the same time in the early 1980s, but by a different group. With both algorithms enabled, applications which do two successive writes to a TCP connection, followed by a read, experience a constant delay of up to 500 milliseconds, the "ACK delay". For this reason, TCP implementations usually provide applications with an interface to disable the Nagle algorithm. This is typically called the TCP_NODELAY option. The first major application to run into this problem was the X Window System.</p>
<p>The tinygram problem and silly window syndrome are sometimes confused. The tinygram problem occurs when the window is almost empty. Silly window syndrome occurs when the window is almost full</p>
<p>===================================================================================================================================<br>3.17 - What is the Nagle algorithm?<br>The Nagle algorithm is an optimization to TCP that makes the stack wait until all data is acknowledged on the connection before it sends more data. The exception is that Nagle will not cause the stack to wait for an ACK if it has enough enqueued data that it can fill a network frame. (Without this exception, the Nagle algorithm would effectively disable TCP's sliding window algorithm.) For a full description of the Nagle algorithm, see RFC 896.</p>
<p>So, you ask, what's the purpose of the Nagle algorithm?</p>
<p>The ideal case in networking is that each program always sends a full frame of data with each call to send(). That maximizes the percentage of useful program data in a packet.</p>
<p>The basic TCP and IPv4 headers are 20 bytes each. The worst case protocol overhead percentage, therefore, is 40/41, or 98%. Since the maximum amount of data in an Ethernet frame is 1500 bytes, the best case protocol overhead percentage is 40/1500, less than 3%.</p>
<p>While the Nagle algorithm is causing the stack to wait for data to be ACKed by the remote peer, the local program can make more calls to send(). Because TCP is a stream protocol, it can coalesce the data in those send() calls into a single TCP packet, increasing the percentage of useful data.</p>
<p>Imagine a simple Telnet program: the bulk of a Telnet conversation consists of sending one character, and receiving an echo of that character back from the remote host. Without the Nagle algorithm, this results in TCP's worst case: one byte of user data wrapped in dozens of bytes of protocol overhead. With the Nagle algorithm enabled, the TCP stack won't send that one Telnet character out until the previous characters have all been acknowledged. By then, the user may well have typed another character or two, reducing the relative protocol overhead.</p>
<p>This simple optimization interacts with other features of the TCP protocol suite, too:</p>
<p>Most stacks implement the delayed ACK algorithm: this causes the remote stack to delay ACKs under certain circumstances, which allows the local stack a bit of time to "Nagle" some more bytes into a single packet. </p>
<p>The Nagle algorithm tends to improve the percentage of useful data in packets more on slow networks than on fast networks, because ACKs take longer to come back. </p>
<p>TCP allows an ACK packet to also contain data. If the local stack decides it needs to send out an ACK packet and the Nagle algorithm has caused data to build up in the output buffer, the enqueued data will go out along with the ACK packet. <br>The Nagle algorithm is on by default in Winsock, but it can be turned off on a per-socket basis with the TCP_NODELAY option of setsockopt(). This option should not be turned off except in a very few situations.</p>
<p>Beware of depending on the Nagle algorithm too heavily. send() is a kernel function, so every call to send() takes much more time than for a regular function call. Your application should coalesce its own data as much as is practical to minimize the number of calls to send().</p>
<p><br>--------------------------------------------------------------------------------------------------------------------------------------------------------<br><br>Sliding Window Acknowledgment System :<br><a href="http://www.tcpipguide.com/free/t_TCPSlidingWindowAcknowledgmentSystemForDataTranspo.htm">http://www.tcpipguide.com/free/t_TCPSlidingWindowAcknowledgmentSystemForDataTranspo.htm</a><br>--------------------------------------------------------------------------------------------<br>A basic technique for ensuring reliability in communications uses a rule that requires a <br>device to send back an acknowledgment each time it successfully receives a transmission. <br>If a transmission is not acknowledged after a period of time, it is retransmitted by its <br>sender. This system is called positive acknowledgment with retransmission (PAR). One<br>&nbsp;drawback with this basic scheme is that the transmitter cannot send a second message <br>until the first has been acknowledged.<br>--------------------------------------------------------------------------------------------</p>
<p><a href="http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html">http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html</a></p>
<p>The sliding window serves several purposes:<br>(1) it guarantees the reliable delivery of data<br>(2) it ensures that the data is delivered in order,<br>(3) it enforces flow control between the sender and the receiver.</p>
<p><br><br><br>------------------to be continued<br></p>
</font>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/47471.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-04-18 10:02 <a href="http://www.cppblog.com/kevinlynx/archive/2008/04/18/47471.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探究CRC32算法实现原理-why table-driven implemention</title><link>http://www.cppblog.com/kevinlynx/archive/2008/04/01/45952.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 01 Apr 2008 13:22:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/04/01/45952.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/45952.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/04/01/45952.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/45952.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/45952.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 探究CRC32算法实现原理-why table-driven implementionAuthor : Kevin Lynxemail&nbsp; : zmhn320@163.comPreface基于不重造轮子的原则，本文尽量不涉及网络上遍地都是的资料。What's CRC ?简而言之，CRC是一个数值。该数值被用于校验数据的正确性。CRC数值简单地说就是通过让你需要做处理的数...&nbsp;&nbsp;<a href='http://www.cppblog.com/kevinlynx/archive/2008/04/01/45952.html'>阅读全文</a><img src ="http://www.cppblog.com/kevinlynx/aggbug/45952.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-04-01 21:22 <a href="http://www.cppblog.com/kevinlynx/archive/2008/04/01/45952.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用dbghelp获取调用堆栈--release下的调试方法学</title><link>http://www.cppblog.com/kevinlynx/archive/2008/03/28/45628.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Fri, 28 Mar 2008 08:37:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/03/28/45628.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/45628.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/03/28/45628.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/45628.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/45628.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt">Author : Kevin Lynx</p>
<p style="FONT-SIZE: 10pt">当软件作为release模式被发布给用户时，当程序崩溃时我们很难去查找原因。常见的手法是输出LOG文件，根据LOG文件分析<br>程序崩溃时的运行情况。我们可以通过SEH来捕获程序错误，然后输出一些有用的信息作为我们分析错误的资料。一般我们需要<br>输出的信息包括：系统信息、CPU寄存器信息、堆栈信息、调用堆栈等。而调用堆栈则是最有用的部分，它可以直接帮我们定位<br>到程序崩溃时所处的位置(在何处崩溃)。(codeproject上关于这个专题的常见开场白 = =#)</p>
<p style="FONT-SIZE: 10pt">要获取call stack(所谓的调用堆栈)，就需要查看(unwind)stack的内容。We could conceivably attempt to unwind the <br>stack ourselves using inline assembly. But stack frames can be organized in different ways, depending on compiler <br>optimizations and calling conventions, so it could become complicated to do it that way.(摘自vld文档)要获取栈的<br>内容，我们可以自己使用内联汇编获取，但是考虑到兼容性，内联汇编并不是一个好的解决方案。我们可以使用微软的dbghelp<br>中的StackWalk64来获取栈的内容。</p>
<p style="FONT-SIZE: 10pt">StackWalk64声明如下：<br>BOOL StackWalk64(<br>&nbsp; DWORD MachineType,<br>&nbsp; HANDLE hProcess,<br>&nbsp; HANDLE hThread,<br>&nbsp; LPSTACKFRAME64 StackFrame,<br>&nbsp; PVOID ContextRecord,<br>&nbsp; PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,<br>&nbsp; PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,<br>&nbsp; PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,<br>&nbsp; PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress<br>);</p>
<p style="FONT-SIZE: 10pt">具体每个参数的含义可以参见MSDN。这里说下ContextRecord参数，该参数指定了CPU各个寄存器的内容。StackFrame指定了stack <br>frame的内容。stack frame是什么，我也不知道。(= =) StackWalk64函数需要用户指定当前frame的地址，以及当前程序的指令<br>地址。这两个信息都被填充进ContextRecord，然后传进StackWalk64函数。</p>
<p style="FONT-SIZE: 10pt">那么如何获取当前的stack frame地址和当前程序指令地址呢？如前所说，你可以使用内联汇编。(对于程序指令地址，因为要获取<br>EIP寄存器的内容，而该寄存器不能被软件访问)也可以使用GetThreadContext一次性获取当前线程当前运行情况下的CPU各个寄存器<br>内容。补充下，当前frame地址被放在EBP寄存器里，当前程序指令地址放在EIP寄存器里。但是，如同MSDN对GetThreadContext函数<br>的说明一样，该函数可能获取到错误的寄存器内容(You cannot get a valid context for a running thread)。</p>
<p style="FONT-SIZE: 10pt">另一种获取Context(包含EBP and EIP)的方法就是使用SEH(结构化异常处理)，在__except中使用GetExceptionInformation获取。</p>
<p style="FONT-SIZE: 10pt">GetExceptionInformation 传回一个LPEXCEPTION_POINTERS指针，该指针指向一个EXCEPTION_POINTERS结构，该结构里包含一个<br>Context的指针，即达到目标，可以使用StackWalk函数。</p>
<p style="FONT-SIZE: 10pt">补充一下，你可以直接使用StackWalk函数，StackWalk被define为StackWalk64(windows平台相关)。</p>
<p style="FONT-SIZE: 10pt">unwind栈后，可以进一步获取一个stack frame的内容，例如函数名。这里涉及到SymFromAddr函数，该函数可以根据一个地址返回<br>符号名(函数名)。还有一个有意思的函数：SymGetLineFromAddr，可以获取函数对应的源代码的文件名和行号。</p>
<p style="FONT-SIZE: 10pt">当然，这一切都依赖于VC产生的程序数据库文件(pdb)，以及提供以上API函数的dbghelp.dll。</p>
<p style="FONT-SIZE: 10pt">参考一段简单的代码：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img id=Codehighlighter1_0_11_Open_Image onclick="this.style.display='none'; Codehighlighter1_0_11_Open_Text.style.display='none'; Codehighlighter1_0_11_Closed_Image.style.display='inline'; Codehighlighter1_0_11_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_0_11_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_0_11_Closed_Text.style.display='none'; Codehighlighter1_0_11_Open_Image.style.display='inline'; Codehighlighter1_0_11_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span id=Codehighlighter1_0_11_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_0_11_Open_Text><span style="COLOR: #808080">///</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #808080">///</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top></span><span style="COLOR: #808080">///</span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">windows.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdio.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">dbghelp.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>#pragma&nbsp;comment(&nbsp;lib,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">dbghelp.lib</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;dump_callstack(&nbsp;CONTEXT&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">context&nbsp;)<br><img id=Codehighlighter1_153_1506_Open_Image onclick="this.style.display='none'; Codehighlighter1_153_1506_Open_Text.style.display='none'; Codehighlighter1_153_1506_Closed_Image.style.display='inline'; Codehighlighter1_153_1506_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_153_1506_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_153_1506_Closed_Text.style.display='none'; Codehighlighter1_153_1506_Open_Image.style.display='inline'; Codehighlighter1_153_1506_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_153_1506_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_153_1506_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;STACKFRAME&nbsp;sf;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;memset(&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">sf,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(&nbsp;STACKFRAME&nbsp;)&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;sf.AddrPC.Offset&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;context</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">Eip;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;sf.AddrPC.Mode&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;AddrModeFlat;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;sf.AddrStack.Offset&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;context</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">Esp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;sf.AddrStack.Mode&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;AddrModeFlat;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;sf.AddrFrame.Offset&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;context</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">Ebp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;sf.AddrFrame.Mode&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;AddrModeFlat;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;DWORD&nbsp;machineType&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;IMAGE_FILE_MACHINE_I386;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;HANDLE&nbsp;hProcess&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;GetCurrentProcess();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;HANDLE&nbsp;hThread&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;GetCurrentThread();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;;&nbsp;;&nbsp;)<br><img id=Codehighlighter1_563_1504_Open_Image onclick="this.style.display='none'; Codehighlighter1_563_1504_Open_Text.style.display='none'; Codehighlighter1_563_1504_Closed_Image.style.display='inline'; Codehighlighter1_563_1504_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_563_1504_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_563_1504_Closed_Text.style.display='none'; Codehighlighter1_563_1504_Open_Image.style.display='inline'; Codehighlighter1_563_1504_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</span><span id=Codehighlighter1_563_1504_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_563_1504_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">StackWalk(machineType,&nbsp;hProcess,&nbsp;hThread,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">sf,&nbsp;context,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;SymFunctionTableAccess,&nbsp;SymGetModuleBase,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;)&nbsp;)<br><img id=Codehighlighter1_681_695_Open_Image onclick="this.style.display='none'; Codehighlighter1_681_695_Open_Text.style.display='none'; Codehighlighter1_681_695_Closed_Image.style.display='inline'; Codehighlighter1_681_695_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_681_695_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_681_695_Closed_Text.style.display='none'; Codehighlighter1_681_695_Open_Image.style.display='inline'; Codehighlighter1_681_695_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;</span><span id=Codehighlighter1_681_695_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_681_695_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;sf.AddrFrame.Offset&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;)<br><img id=Codehighlighter1_733_747_Open_Image onclick="this.style.display='none'; Codehighlighter1_733_747_Open_Text.style.display='none'; Codehighlighter1_733_747_Closed_Image.style.display='inline'; Codehighlighter1_733_747_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_733_747_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_733_747_Closed_Text.style.display='none'; Codehighlighter1_733_747_Open_Image.style.display='inline'; Codehighlighter1_733_747_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;</span><span id=Codehighlighter1_733_747_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_733_747_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;BYTE&nbsp;symbolBuffer[&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(&nbsp;SYMBOL_INFO&nbsp;)&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1024</span><span style="COLOR: #000000">&nbsp;];<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;PSYMBOL_INFO&nbsp;pSymbol&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(&nbsp;PSYMBOL_INFO&nbsp;)&nbsp;symbolBuffer;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;pSymbol</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">SizeOfStruct&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(&nbsp;symbolBuffer&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;pSymbol</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">MaxNameLen&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1024</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;DWORD64&nbsp;symDisplacement&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;SymFromAddr(&nbsp;hProcess,&nbsp;sf.AddrPC.Offset,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;pSymbol&nbsp;)&nbsp;)<br><img id=Codehighlighter1_1036_1088_Open_Image onclick="this.style.display='none'; Codehighlighter1_1036_1088_Open_Text.style.display='none'; Codehighlighter1_1036_1088_Closed_Image.style.display='inline'; Codehighlighter1_1036_1088_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_1036_1088_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1036_1088_Closed_Text.style.display='none'; Codehighlighter1_1036_1088_Open_Image.style.display='inline'; Codehighlighter1_1036_1088_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;</span><span id=Codehighlighter1_1036_1088_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1036_1088_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Function&nbsp;:&nbsp;%s\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;pSymbol</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">Name&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_1099_1141_Open_Image onclick="this.style.display='none'; Codehighlighter1_1099_1141_Open_Text.style.display='none'; Codehighlighter1_1099_1141_Closed_Image.style.display='inline'; Codehighlighter1_1099_1141_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_1099_1141_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1099_1141_Closed_Text.style.display='none'; Codehighlighter1_1099_1141_Open_Image.style.display='inline'; Codehighlighter1_1099_1141_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;</span><span id=Codehighlighter1_1099_1141_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1099_1141_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">SymFromAdd&nbsp;failed!\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img id=Codehighlighter1_1171_1195_Open_Image onclick="this.style.display='none'; Codehighlighter1_1171_1195_Open_Text.style.display='none'; Codehighlighter1_1171_1195_Closed_Image.style.display='inline'; Codehighlighter1_1171_1195_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_1171_1195_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1171_1195_Closed_Text.style.display='none'; Codehighlighter1_1171_1195_Open_Image.style.display='inline'; Codehighlighter1_1171_1195_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;IMAGEHLP_LINE&nbsp;lineInfo&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span id=Codehighlighter1_1171_1195_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1171_1195_Open_Text><span style="COLOR: #000000">{&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(IMAGEHLP_LINE)&nbsp;}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;DWORD&nbsp;dwLineDisplacement;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;SymGetLineFromAddr(&nbsp;hProcess,&nbsp;sf.AddrPC.Offset,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">dwLineDisplacement,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">lineInfo&nbsp;)&nbsp;)<br><img id=Codehighlighter1_1318_1440_Open_Image onclick="this.style.display='none'; Codehighlighter1_1318_1440_Open_Text.style.display='none'; Codehighlighter1_1318_1440_Closed_Image.style.display='inline'; Codehighlighter1_1318_1440_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_1318_1440_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1318_1440_Closed_Text.style.display='none'; Codehighlighter1_1318_1440_Open_Image.style.display='inline'; Codehighlighter1_1318_1440_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;</span><span id=Codehighlighter1_1318_1440_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1318_1440_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">[Source&nbsp;File&nbsp;:&nbsp;%s]\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;lineInfo.FileName&nbsp;);&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">[Source&nbsp;Line&nbsp;:&nbsp;%u]\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,&nbsp;lineInfo.LineNumber&nbsp;);&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br><img id=Codehighlighter1_1451_1501_Open_Image onclick="this.style.display='none'; Codehighlighter1_1451_1501_Open_Text.style.display='none'; Codehighlighter1_1451_1501_Closed_Image.style.display='inline'; Codehighlighter1_1451_1501_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_1451_1501_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1451_1501_Closed_Text.style.display='none'; Codehighlighter1_1451_1501_Open_Image.style.display='inline'; Codehighlighter1_1451_1501_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;</span><span id=Codehighlighter1_1451_1501_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1451_1501_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">SymGetLineFromAddr&nbsp;failed!\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>DWORD&nbsp;excep_filter(&nbsp;LPEXCEPTION_POINTERS&nbsp;lpEP&nbsp;)<br><img id=Codehighlighter1_1557_1841_Open_Image onclick="this.style.display='none'; Codehighlighter1_1557_1841_Open_Text.style.display='none'; Codehighlighter1_1557_1841_Closed_Image.style.display='inline'; Codehighlighter1_1557_1841_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_1557_1841_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1557_1841_Closed_Text.style.display='none'; Codehighlighter1_1557_1841_Open_Image.style.display='inline'; Codehighlighter1_1557_1841_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_1557_1841_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1557_1841_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_1560_1580_Open_Image onclick="this.style.display='none'; Codehighlighter1_1560_1580_Open_Text.style.display='none'; Codehighlighter1_1560_1580_Closed_Image.style.display='inline'; Codehighlighter1_1560_1580_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_1560_1580_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1560_1580_Closed_Text.style.display='none'; Codehighlighter1_1560_1580_Open_Image.style.display='inline'; Codehighlighter1_1560_1580_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</span><span id=Codehighlighter1_1560_1580_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_1560_1580_Open_Text><span style="COLOR: #808080">///</span><span style="COLOR: #008000">&nbsp;init&nbsp;dbghelp.dll</span><span style="COLOR: #808080"></span></span><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;SymInitialize(&nbsp;GetCurrentProcess(),&nbsp;NULL,&nbsp;TRUE&nbsp;)&nbsp;)<br><img id=Codehighlighter1_1638_1676_Open_Image onclick="this.style.display='none'; Codehighlighter1_1638_1676_Open_Text.style.display='none'; Codehighlighter1_1638_1676_Closed_Image.style.display='inline'; Codehighlighter1_1638_1676_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_1638_1676_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1638_1676_Closed_Text.style.display='none'; Codehighlighter1_1638_1676_Open_Image.style.display='inline'; Codehighlighter1_1638_1676_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</span><span id=Codehighlighter1_1638_1676_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1638_1676_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Init&nbsp;dbghelp&nbsp;ok.\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;dump_callstack(&nbsp;lpEP</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ContextRecord&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;SymCleanup(&nbsp;GetCurrentProcess()&nbsp;)&nbsp;)<br><img id=Codehighlighter1_1762_1803_Open_Image onclick="this.style.display='none'; Codehighlighter1_1762_1803_Open_Text.style.display='none'; Codehighlighter1_1762_1803_Closed_Image.style.display='inline'; Codehighlighter1_1762_1803_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_1762_1803_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1762_1803_Closed_Text.style.display='none'; Codehighlighter1_1762_1803_Open_Image.style.display='inline'; Codehighlighter1_1762_1803_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</span><span id=Codehighlighter1_1762_1803_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1762_1803_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Cleanup&nbsp;dbghelp&nbsp;ok.\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;EXCEPTION_EXECUTE_HANDLER;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;func1(&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i&nbsp;)<br><img id=Codehighlighter1_1864_1888_Open_Image onclick="this.style.display='none'; Codehighlighter1_1864_1888_Open_Text.style.display='none'; Codehighlighter1_1864_1888_Closed_Image.style.display='inline'; Codehighlighter1_1864_1888_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_1864_1888_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1864_1888_Closed_Text.style.display='none'; Codehighlighter1_1864_1888_Open_Image.style.display='inline'; Codehighlighter1_1864_1888_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_1864_1888_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1864_1888_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;i;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;func2(&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i&nbsp;)<br><img id=Codehighlighter1_1911_1930_Open_Image onclick="this.style.display='none'; Codehighlighter1_1911_1930_Open_Text.style.display='none'; Codehighlighter1_1911_1930_Closed_Image.style.display='inline'; Codehighlighter1_1911_1930_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_1911_1930_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1911_1930_Closed_Text.style.display='none'; Codehighlighter1_1911_1930_Open_Image.style.display='inline'; Codehighlighter1_1911_1930_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_1911_1930_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1911_1930_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;func1(&nbsp;i&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;func3(&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i&nbsp;)<br><img id=Codehighlighter1_1953_1972_Open_Image onclick="this.style.display='none'; Codehighlighter1_1953_1972_Open_Text.style.display='none'; Codehighlighter1_1953_1972_Closed_Image.style.display='inline'; Codehighlighter1_1953_1972_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_1953_1972_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1953_1972_Closed_Text.style.display='none'; Codehighlighter1_1953_1972_Open_Image.style.display='inline'; Codehighlighter1_1953_1972_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_1953_1972_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1953_1972_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;func2(&nbsp;i&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;test(&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i&nbsp;)<br><img id=Codehighlighter1_1994_2013_Open_Image onclick="this.style.display='none'; Codehighlighter1_1994_2013_Open_Text.style.display='none'; Codehighlighter1_1994_2013_Closed_Image.style.display='inline'; Codehighlighter1_1994_2013_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_1994_2013_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1994_2013_Closed_Text.style.display='none'; Codehighlighter1_1994_2013_Open_Image.style.display='inline'; Codehighlighter1_1994_2013_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_1994_2013_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1994_2013_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;func3(&nbsp;i&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main()<br><img id=Codehighlighter1_2027_2171_Open_Image onclick="this.style.display='none'; Codehighlighter1_2027_2171_Open_Text.style.display='none'; Codehighlighter1_2027_2171_Closed_Image.style.display='inline'; Codehighlighter1_2027_2171_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_2027_2171_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_2027_2171_Closed_Text.style.display='none'; Codehighlighter1_2027_2171_Open_Image.style.display='inline'; Codehighlighter1_2027_2171_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_2027_2171_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_2027_2171_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;__try<br><img id=Codehighlighter1_2037_2054_Open_Image onclick="this.style.display='none'; Codehighlighter1_2037_2054_Open_Text.style.display='none'; Codehighlighter1_2037_2054_Closed_Image.style.display='inline'; Codehighlighter1_2037_2054_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_2037_2054_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_2037_2054_Closed_Text.style.display='none'; Codehighlighter1_2037_2054_Open_Image.style.display='inline'; Codehighlighter1_2037_2054_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</span><span id=Codehighlighter1_2037_2054_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_2037_2054_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;test(&nbsp;</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;__except(&nbsp;excep_filter(&nbsp;GetExceptionInformation()&nbsp;)&nbsp;)<br><img id=Codehighlighter1_2112_2157_Open_Image onclick="this.style.display='none'; Codehighlighter1_2112_2157_Open_Text.style.display='none'; Codehighlighter1_2112_2157_Closed_Image.style.display='inline'; Codehighlighter1_2112_2157_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_2112_2157_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_2112_2157_Closed_Text.style.display='none'; Codehighlighter1_2112_2157_Open_Image.style.display='inline'; Codehighlighter1_2112_2157_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;</span><span id=Codehighlighter1_2112_2157_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_2112_2157_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;printf(&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Some&nbsp;exception&nbsp;occures.\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p style="FONT-SIZE: 10pt"><br>以上代码在release模式下需要关掉优化，否则调用堆栈显示不正确(某些函数被去掉了?)，同时需要pdb文件。<br></p>
<font style="BACKGROUND-COLOR: #ffffff" size=2>参考资料：<br><a href="http://www.codeproject.com/KB/threads/StackWalker.aspx">http://www.codeproject.com/KB/threads/StackWalker.aspx</a><br><a href="http://www.cnblogs.com/protalfox/articles/84723.html">http://www.cnblogs.com/protalfox/articles/84723.html</a><br><a href="http://www.codeproject.com/KB/debug/XCrashReportPt1.aspx">http://www.codeproject.com/KB/debug/XCrashReportPt1.aspx</a><br><a href="http://www.codeproject.com/KB/applications/visualleakdetector.aspx">http://www.codeproject.com/KB/applications/visualleakdetector.aspx</a><br><br>ps,本文技术浅尝辄止，部分内容是否完全准确(正确)我个人都持保留态度，仅供参考。:D<br></font>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/45628.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-03-28 16:37 <a href="http://www.cppblog.com/kevinlynx/archive/2008/03/28/45628.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>