﻿<?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++博客-紫雨轩 C++-随笔分类-C++基础</title><link>http://www.cppblog.com/rick/category/2229.html</link><description>技术交流</description><language>zh-cn</language><lastBuildDate>Tue, 20 May 2008 03:10:47 GMT</lastBuildDate><pubDate>Tue, 20 May 2008 03:10:47 GMT</pubDate><ttl>60</ttl><item><title>naked 函数调用 </title><link>http://www.cppblog.com/rick/archive/2006/10/28/14318.html</link><dc:creator>紫雨轩 C++</dc:creator><author>紫雨轩 C++</author><pubDate>Sat, 28 Oct 2006 12:02:00 GMT</pubDate><guid>http://www.cppblog.com/rick/archive/2006/10/28/14318.html</guid><wfw:comment>http://www.cppblog.com/rick/comments/14318.html</wfw:comment><comments>http://www.cppblog.com/rick/archive/2006/10/28/14318.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/rick/comments/commentRss/14318.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/rick/services/trackbacks/14318.html</trackback:ping><description><![CDATA[转<br /><div>正常的情况下，我们写一个 C/C++ 函数，即使是一个空函数，编译器也为我们做了不少的工作，生成了一些“必要”的代码。请看下面的函数 (为了说明问题随便写的): <br /></div><br /><br /><div style="COLOR: #2222aa; BACKGROUND-COLOR: #f5f5f0"><pre>int Test()<br /> { <br />     int iReturn;<br />     char szTemp[33];<br />     <br />     szTemp[0] = 'A';<br />     szTemp[1] = '';<br />     iReturn = MessageBox(NULL, szTemp, szTemp, MB_OK);<br />     MessageBeep(iReturn);<br />     return iReturn;<br />  }</pre></div><br /><div>下面是用 VC6 在 Release 方式下编译后的的反汇编代码:</div><br /><br /><div style="COLOR: #2222aa; BACKGROUND-COLOR: #f5f5f0"><pre>00401000   sub         esp,24h     // 增加堆栈空间存放局部变量 (24H = 36D，4 字节对齐，注意这里没有为 iReturn 分配空间)<br /> 00401003   push        esi         // 保存要使用的重要寄存器<br /> 00401004   lea         eax,[esp+4] // 下面是传递 MessageBox() 要使用的参数<br /> 00401008   push        0<br /> 0040100A   lea         ecx,[esp+8] // 编译器愚蠢，根本不用 ECX，两个都是 szTemp，两次 PUSH EAX 不得了<br /> 0040100E   push        eax<br /> 0040100F   push        ecx<br /> 00401010   push        0<br /> 00401012   mov         byte ptr [esp+14h],41h<br /> 00401017   mov         byte ptr [esp+15h],0<br /> 0040101C   call        dword ptr ds:[40509Ch] // 调用 MessageBox()<br /> 00401022   mov         esi,eax     // 保存返回值到变量 iReturn 。靠！变量 iReturn 自动使用 ESI，编译器太聪明了：）<br /> 00401024   push        esi<br /> 00401025   call        dword ptr ds:[4050A0h] // 调用 MessageBeep()<br /> 0040102B   mov         eax,esi     // 把变量 iReturn 交给 EAX 作为返回值<br /> 0040102D   pop         esi         // 恢复要使用的重要寄存器<br /> 0040102E   add         esp,24h     // 减少堆栈空间<br /> 00401031   ret                     // 堆栈长度减 4 并返回<br /></pre></div><br /><div><br />这段代码虽然很精干 (都能自动使用寄存器来保存变量了)，但是有的时候我们并不需要编译器提供这些自作主张的代码 (比如写驱动程序的时候，不过我还没遇到过这种情况，呵呵~~)，我们希望整个全部函数都是自己亲手写进去的 (BT 呀^o^)。好，请出今天的主角 —— “naked”(怎么是裸体呀？)，欢迎！Visual C++ 的扩展关键字 naked 允许我们完全定制一个函数，废话不说了，看例子 (熬夜写的 Zzzzzz~~):</div><br /><br /><div style="COLOR: #2222aa; BACKGROUND-COLOR: #f5f5f0"><pre>__declspec(naked) int Test()<br /> { <br />     __asm<br />     { <br />         SUB         ESP,24H<br />         PUSH        ESI<br />         LEA         EAX,[ESP+4]<br />         PUSH        0                      <br />         PUSH        EAX                    <br />         PUSH        EAX                    <br />         PUSH        0                      <br />         MOV         BYTE PTR [ESP+14H],41H <br />         MOV         BYTE PTR [ESP+15H],0   <br />         CALL        DWORD PTR [MessageBoxA] <br />         MOV         ESI,EAX<br />         PUSH        ESI                    <br />         CALL        DWORD PTR [MessageBeep] <br />         MOV         EAX,ESI<br />         POP         ESI<br />         ADD         ESP,24H<br />         RET<br />      }<br />  }<br /></pre></div><br /><div>上面的代码是使用的 VC 的内联汇编，和 VC 编译后生成的代码完全是一样的 (很有完全控制的成就感吧^_^)。上面我们并没有又节省什么 (节省的 PUSH ECX 并不是 naked 的功劳)，但是有的时候确实需要的 (举不出例子来了，倒！)。最后随便说说注意事项: <br /><br />1.使用 naked 关键字需要自己构建 EBP 参数指针 (如果用到了 EBP 作为参数指针的话)；<br />2.必须自己使用 RET 或 RET n 指令返回 (除非你不返回)。<br /></div><img src ="http://www.cppblog.com/rick/aggbug/14318.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/rick/" target="_blank">紫雨轩 C++</a> 2006-10-28 20:02 <a href="http://www.cppblog.com/rick/archive/2006/10/28/14318.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>#pragma 指示符应用举例</title><link>http://www.cppblog.com/rick/archive/2006/10/28/14317.html</link><dc:creator>紫雨轩 C++</dc:creator><author>紫雨轩 C++</author><pubDate>Sat, 28 Oct 2006 12:01:00 GMT</pubDate><guid>http://www.cppblog.com/rick/archive/2006/10/28/14317.html</guid><wfw:comment>http://www.cppblog.com/rick/comments/14317.html</wfw:comment><comments>http://www.cppblog.com/rick/archive/2006/10/28/14317.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/rick/comments/commentRss/14317.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/rick/services/trackbacks/14317.html</trackback:ping><description><![CDATA[转<br />尽管 C 和 C++ 都已经有标准，但是几乎每个编译器 (广义，包含连接器等) 扩展一些 C/C++ 关键字。合理地应用这些关键字，有时候能使我们的工作非常方便。下面随便说说 Visual C++ 中 #pragma 指示符的使用。<br /><div><strong><font size="3">一、用#pragma导出DLL函数</font></strong></div><br /><div>传统的到出 DLL 函数的方法是使用模块定义文件 (.def)，Visual C++ 提供了更简洁方便的方法，那就是“__declspec()”关键字后面跟“dllexport”，告诉连接去要导出这个函数，例如：</div><br /><br /><div style="COLOR: #2222aa; BACKGROUND-COLOR: #f5f5f0"><pre>__declspec(dllexport) int __stdcall MyExportFunction(int iTest);</pre></div><br /><div>把“__declspec(dllexport)”放在函数声明的最前面，连接生成的 DLL 就会导出函数“_MyExportFunction@4”。</div><br /><div>上面的导出函数的名称也许不是我的希望的，我们希望导出的是原版的“MyExportFunction”。还好，VC 提供了一个预处理指示符“#pragma”来指定连接选项 (不仅仅是这一个功能，还有很多指示功能) ，如下：</div><br /><br /><div style="COLOR: #2222aa; BACKGROUND-COLOR: #f5f5f0"><pre>#pragma comment(linker,"/EXPORT:MyExportFunction=_MyExportFunction@4")</pre></div><br /><div>这下就天如人愿了：）。如果你想指定导出的顺序，或者只将函数导出为序号，没有 Entryname，这个预处理指示符 (确切地说是连接器) 都能够实现，看看 MSDN 的语法说明：</div><br /><br /><div style="COLOR: #2222aa; BACKGROUND-COLOR: #f5f5f0"><pre>/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]</pre></div><br /><div>@ordinal 指定顺序；NONAME 指定只将函数导出为序号；DATA 关键字指定导出项为数据项。</div><br /><div><font size="3"><strong>二、指示文件只包含一次</strong></font></div><br /><div>在头文件中，一般在整个工程中我们只要包含一次就够了，但是如果我在多个 .c/.cpp 文件中都要包含着个头文件，比如 Windows.h，那很多声明等等岂不是有两次了？解决这个问题的传统的方法是在头文件开始出用 #define 定义一个宏，比如 Windows.h 中:<br /></div><br /><pre><div style="COLOR: #2222aa; BACKGROUND-COLOR: #f5f5f0"><pre>#ifndef _WINDOWS_<br /> #define _WINDOWS_</pre></div><br /><br /><br /><div>   然后在文件结为加上 #endif，这样就可以避免被包含多次。但是这样的后果是代码的可读性较差 (个人观点)，VC 给我们提供了另外一个途径，那就是在文件的前面加上:</div><br /><br /><br /><br /><br /><br /><div style="COLOR: #2222aa; BACKGROUND-COLOR: #f5f5f0"><pre>#pragma once</pre></div><br /><br /><br /><br /><br /><div>    是不是很方便？</div><br /><br /><br /><br /><br /><div><strong><font size="3">三、使警告无效</font></strong></div><br /><div><strong><font size="3"><br /><div><strong><font size="3"><br /><br /><div>    有时候我们不得不对变量进行强制转换，由此引来编译器的一番警告，特别是 C++ 中，类型检查相对于 C 更为严格。这虽然不影响什么，但是看起来多不爽——我是故意要这样的，你警告什么！：）这时候你看到警告类型，比如“warning C4311: “类型转换” : 从“HHOOK”到“BOOL”的指针截断”，在前面加上:</div><br /><br /><br /><br /><br /><br /><div style="COLOR: #2222aa; BACKGROUND-COLOR: #f5f5f0"><pre>#pragma warning(disable: 4311)</pre></div><br /><br /><br /><div>   编译器就没话说了：）。</div><br /><br /><br /><br /><br /><div><font size="3"><strong>四、指定连接要使用的库</strong></font></div><br /><div><strong><font size="3"><br /><div><strong><font size="3"><br /><div><strong><font size="3"><br /><br /><div>    比如我们连接的时候用到了 WSock32.lib，你当然可以不辞辛苦地把它加入到你的工程中。但是我觉得更方便的方法是使用 #pragma 指示符，指定要连接的库:</div><br /><br /><br /><br /><br /><div style="COLOR: #2222aa; BACKGROUND-COLOR: #f5f5f0"><pre>#pragma comment(lib, "WSock32.lib")</pre></div><br /><br /><br /><div><strong><font size="3"><br /><div><strong><font size="3"><br /><div><strong><font size="3">五、显示编译消息</font></strong></div><br /><div><strong><font size="3"><br />     没多少用处，举个例子吧:<br /><br /><br /><br /><br /><br /><div style="COLOR: #2222aa; BACKGROUND-COLOR: #f5f5f0"><pre>#ifdef _DEBUG<br /> #pragma message("编译连接为调试模式...")<br /> #endif // _DEBUG</pre></div><br /><br /><br /></font></strong></div></font></strong></div></font></strong></div></font></strong></div></font></strong></div></font></strong></div></font></strong></div></font></strong></div></pre><img src ="http://www.cppblog.com/rick/aggbug/14317.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/rick/" target="_blank">紫雨轩 C++</a> 2006-10-28 20:01 <a href="http://www.cppblog.com/rick/archive/2006/10/28/14317.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>