﻿<?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++博客-酱坛子</title><link>http://www.cppblog.com/sunraiing9/</link><description>专注C++技术  在这里写下自己的学习心得 感悟 
和大家讨论 共同进步（欢迎批评！！！）</description><language>zh-cn</language><lastBuildDate>Sun, 07 Sep 2008 09:59:03 GMT</lastBuildDate><pubDate>Sun, 07 Sep 2008 09:59:03 GMT</pubDate><ttl>60</ttl><item><title>内存文件映射应用举例『转』</title><link>http://www.cppblog.com/sunraiing9/archive/2008/04/03/46171.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Thu, 03 Apr 2008 07:04:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2008/04/03/46171.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/46171.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2008/04/03/46171.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/46171.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/46171.html</trackback:ping><description><![CDATA[<table style="TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div class=cnt id=blog_text><span>
            <p>下面这些函数被应用于文件内存映射中：<br><br><font color=#0000ff>1) CreateFileMapping <br>2) FlushViewOfFile <br>3) MapViewOfFile <br>4) MapViewOfFileEx <br>5) MapViewOfFileVlm <br>6) OpenFileMapping <br>7) UnmapViewOfFile&nbsp;&nbsp; <br>8) UnmapViewOfFileVlm</font> </p>
            <p><font color=#ff0000>函数详细说明：『见本页末』</font></p>
            <p><span>
            <p>例子：<br><font color=#0000ff>/*****************************************************/<br>/*&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>/*这个例子创建了文件映射视图，并通过这个映射&nbsp;&nbsp;&nbsp;&nbsp; */<br>/*视图来操作文件内容。&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>/*&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>/****************************************************/<br>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;windows.h&gt;</font></p>
            <p><font color=#0000ff>int main()<br>{<br>HFILE hFile;<br>OFSTRUCT opBuf;<br>HANDLE hMapfile;<br>HANDLE hMapview;<br>BYTE *recv;<br>hFile=OpenFile(".\\map.test",&amp;opBuf,OF_READWRITE);<br>if (hFile==HFILE_ERROR)<br>{<br>&nbsp;&nbsp; printf("open file failed!\n");<br>&nbsp;&nbsp; return 1;<br>}<br>hMapfile=CreateFileMapping((HANDLE)hFile,NULL,PAGE_READWRITE,0,0,"MapTest");<br>if(hMapfile==NULL)<br>{<br>&nbsp;&nbsp; printf("mapping file failed!\n");<br>&nbsp;&nbsp; return 1;<br>}<br>CloseHandle((HANDLE)hFile);<br>hFile=0;<br>hMapview=MapViewOfFile(hMapfile,FILE_MAP_WRITE,0,0,0);<br>if(hMapview==NULL)<br>{<br>&nbsp;&nbsp; printf("mapping view failed!\n");<br>&nbsp;&nbsp; return 1;<br>}<br>recv=(BYTE *)hMapview;<br>printf("Mapping view's content is :%.10s \n",recv);<br>recv[0]='s';<br>printf("Mapping view's content is :%.10s \n",recv);<br>UnmapViewOfFile(hMapview);<br>return 0;<br>}</font></p>
            </span>
            <p>&#160;</p>
            <p>一、<font color=#ff0000>CreateFileMapping</font> 为指定文件创建一个有名或无名的文件映象；<br>HANDLE CreateFileMapping(<br>HANDLE hFile,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 映射文件的句柄<br>LPSECURITY_ATTRIBUTES lpFileMappingAttributes,<br>&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>DWORD flProtect,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 对映射对象的保护<br>DWORD dwMaximumSizeHigh,&nbsp;&nbsp; // 对象最大长度的高32位<br>DWORD dwMaximumSizeLow,&nbsp;&nbsp;&nbsp; // 对象最大长度的低32位<br>LPCTSTR lpName&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 文件内存映射对象的名字<br>);</p>
            <p>注意：<br>hFile：映射文件的句柄，文件的打开模式必须与flProtect参数指定的相一致；如果这个参数值为0xFFFFFFFF，那 么必须在dwMaximumSizeHigh和dwMaximumSizeLow参数中指定映射对象的大小。并且将在操作系统虚拟内存页面替换文件中创建 文件映射对象，而不是使用磁盘文件，同时必须给出这个映射对象的大小。文件映射对象通过副本，遗传或名字来共享。<br>lpFileMappingAttributes：安全描述符指针，决定返回句柄是否能被子进程继承，如果是NULL，那么子进程不能继承。WinNt中，如果是NULL，那么文件映射对象得到一个默认的安全描述符。<br>flProtect：为得到的文件试图指定保护模式，可以被设置为下列值：<br>PAGE_READONLY ：只读属性，并且hFile对应的文件必须以GENERIC_READ形式打开。<br>PAGE_READWRITE：可读可写属性，并且hFile对应的文件必须以GENERIC_READ 和 GENERIC_WRITE形式打开。<br>PAGE_WRITECOPY：对可写区域复制后操作，并且hFile对应的文件必须以GENERIC_READ 和 GENERIC_WRITE形式打开。<br>dwMaximumSizeHigh，dwMaximumSizeLow：如果这两个参数为0，则文件映射对象的最大长度等于hFile指定的文件长度。<br>lpName： 文件映射对象的名字，如果这个名字已存在，则按照flProtect指定的来处理映射对象。如果此参数为空，则创建一个无名字的文件映射对象。如果此参数 的名字与系统事件的名字相同，则函数执行失败，GetLastError返回 ERROR_INVALID_HANDLE；</p>
            <p>返回值：函数调用成功返回文件映射对象的句柄，如果文件映射对象已经存在则返回原有映射对象的句柄，GetLastError返回ERROR_ALREADY_EXISTS。函数执行失败返回Null。</p>
            <p>二、<font color=#ff0000>FlushViewOfFile</font> 把文件映射视图中的修改的内容或全部写回到磁盘文件中<br>BOOL FlushViewOfFile(<br>LPCVOID lpBaseAddress,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 修改内容的起始地址<br>DWORD dwNumberOfBytesToFlush // 修改的字节数目<br>);<br>函数执行成功返回非零。</p>
            <p>三、<font color=#ff0000>MapViewOfFile</font> 在调用进程的地址空间映射一个文件视图<br>LPVOID MapViewOfFile(<br>HANDLE hFileMappingObject, // 已创建的文件映射对象句柄<br>DWORD dwDesiredAccess,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 访问模式<br>DWORD dwFileOffsetHigh,&nbsp;&nbsp;&nbsp;&nbsp; // 文件偏移的高32位<br>DWORD dwFileOffsetLow,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 文件偏移的低32位<br>DWORD dwNumberOfBytesToMap // 映射视图的大小<br>);<br>注意：<br>hFileMappingObject： 由CreateFileMapping 或 OpenFileMapping 返回的文件映射对象句柄。<br>dwDesiredAccess：映射视图的访问模式，与创建文件映射对象的保护模式flProtect有关，可以被设置为下列值：<br>FILE_MAP_WRITE：一个可读写属性的文件视图被创建，保护模式为PAGE_READWRITE <br>FILE_MAP_READ ：一个只读属性的文件视图被创建，保护模式为PAGE_READWRITE 或 PAGE_READONLY <br>FILE_MAP_ALL_ACCESS：与FILE_MAP_WRITE模式相同<br>FILE_MAP_COPY：保护模式为PAGE_WRITECOPY时，得到一个视图文件，当你对视图文件写操作时，页面自动交换，并且你所做的修改不会损坏原始数据资料。<br>dwNumberOfBytesToMap：映射文件部分的大小，如果为0，则映射整个文件。 <br>返回值：<br>如果成功返回返回映射视图的起始地址，如果失败返回NULL。</p>
            <p>四、<font color=#ff0000>MapViewOfFileEx</font> 在调用进程的地址空间映射一个文件视图，并且允许调用进程为映射视图指定特殊的内存地址 <br>LPVOID MapViewOfFileEx(<br>HANDLE hFileMappingObject, // 文件映射对象的句柄<br>DWORD dwDesiredAccess,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 访问模式<br>DWORD dwFileOffsetHigh,&nbsp;&nbsp;&nbsp;&nbsp; // 文件偏移的高32位<br>DWORD dwFileOffsetLow,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 文件偏移的低32位<br>DWORD dwNumberOfBytesToMap, // 映射视图的大小<br>LPVOID lpBaseAddress&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 指定映射视图的其实内存地址<br>);<br>注意：<br>与MapViewOfFile用法相同，但是如果指定的内存地址空间大小不够，则函数执行失败。<br>五、<font color=#ff0000>OpenFileMapping</font> 打开一个已命名的文件映射对象<br>HANDLE OpenFileMapping(<br>DWORD dwDesiredAccess, // 访问模式<br>BOOL bInheritHandle,&nbsp;&nbsp;&nbsp; // 继承标志<br>LPCTSTR lpName&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 文件映射对象名指针<br>);<br>注意：<br>dwDesiredAccess：访问模式与MapViewOfFile中的访问模式相同。<br>bInheritHandle：继承标志，是否可以被一个新的进程继承使用，如果为TRUE，就可以被一个新进程继承句柄。<br>返回值：<br>成功返回一个已命名的文件映射对象，失败返回NULL。</p>
            <p>六、<font color=#ff0000>UnmapViewOfFile</font> 删除文件的映射视图<br>BOOL UnmapViewOfFile(<br>LPCVOID lpBaseAddress&nbsp;&nbsp; // 映射视图起始地址<br>);<br>注意：<br>lpBaseAddress：映射视图起始地址，由 MapViewOfFile 函数 MapViewOfFileEx产生。 <br>返回值：<br>如果调用成功返回非零，并且所有指定地址内的脏页面会被写入硬盘。调用失败返回零。</p>
            </span></div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/sunraiing9/aggbug/46171.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2008-04-03 15:04 <a href="http://www.cppblog.com/sunraiing9/archive/2008/04/03/46171.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>#pragma once与#ifndef的区别</title><link>http://www.cppblog.com/sunraiing9/archive/2008/01/22/41652.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Tue, 22 Jan 2008 10:14:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2008/01/22/41652.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/41652.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2008/01/22/41652.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/41652.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/41652.html</trackback:ping><description><![CDATA[<p>为了避免同一个文件被include多次<br><br>1&nbsp;&nbsp; #ifndef方式<br>2&nbsp;&nbsp; #pragma once方式<br><br>在能够支持这两种方式的编译器上，二者并没有太大的区别，但是两者仍然还是有一些细微的区别。<br>&nbsp;&nbsp;&nbsp; 方式一： </p>
<p>&nbsp;&nbsp;&nbsp; #ifndef __SOMEFILE_H__<br>&nbsp;&nbsp;&nbsp; #define __SOMEFILE_H__<br>&nbsp;&nbsp;&nbsp; ... ... // 一些声明语句<br>&nbsp;&nbsp;&nbsp; #endif </p>
<p>&nbsp;&nbsp;&nbsp; 方式二： </p>
<p>&nbsp;&nbsp;&nbsp; #pragma once<br>&nbsp;&nbsp;&nbsp; ... ... // 一些声明语句 </p>
<p><br>&nbsp;&nbsp;&nbsp; #ifndef的方式依赖于宏名字不能冲突，这不光可以保证同一个文件不会被包含多次，也能保证内容完全相同的两个文件不会被不小心同时包含。当然，缺点就是如果不同头文件的宏名不小心&#8220;撞车&#8221;，可能就会导致头文件明明存在，编译器却硬说找不到声明的状况<br><br>&nbsp;&nbsp;&nbsp; #pragma once则由编译器提供保证：同一个文件不会被包含多次。注意这里所说的&#8220;同一个文件&#8221;是指物理上的一个文件，而不是指内容相同的两个文件。带来的好处是，你不必再费劲想个宏名了，当然也就不会出现宏名碰撞引发的奇怪问题。对应的缺点就是如果某个头文件有多份拷贝，本方法不能保证他们不被重复包含。当然，相比宏名碰撞引发的&#8220;找不到声明&#8221;的问题，重复包含更容易被发现并修正。<br><br>&nbsp;&nbsp; 方式一由语言支持所以移植性好，方式二 可以避免名字冲突。 <br>一般可以这样处理：<br>#infndef XX<br>#define XX<br>&nbsp;&nbsp;&nbsp;&nbsp;#if _MSC_VER &gt; 1000&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#pragma once&nbsp;<br>&nbsp;&nbsp;&nbsp; #endif<br>&nbsp;&nbsp;&nbsp;.<br>&nbsp;&nbsp;&nbsp;.<br>#endif<br>注意：&nbsp;&nbsp; _MSC_VER 是出于版本兼容性考虑，定义<br>&nbsp;&nbsp; Defines the compiler version. Defined as 1200 for Microsoft Visual C++ 6.0. Always defined.<br></p>
<img src ="http://www.cppblog.com/sunraiing9/aggbug/41652.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2008-01-22 18:14 <a href="http://www.cppblog.com/sunraiing9/archive/2008/01/22/41652.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在入口函数内加如该段代码 能对内存泄露进行输出</title><link>http://www.cppblog.com/sunraiing9/archive/2007/12/21/39227.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Fri, 21 Dec 2007 08:52:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/12/21/39227.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/39227.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/12/21/39227.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/39227.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/39227.html</trackback:ping><description><![CDATA[ 在入口函数内加如该段代码 能对内存泄露进行输出


	int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); 
	tmpFlag |= _CRTDBG_LEAK_CHECK_DF; 
	_CrtSetDbgFlag( tmpFlag );
<img src ="http://www.cppblog.com/sunraiing9/aggbug/39227.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2007-12-21 16:52 <a href="http://www.cppblog.com/sunraiing9/archive/2007/12/21/39227.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>哈希表（摘）</title><link>http://www.cppblog.com/sunraiing9/archive/2007/12/13/38448.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Thu, 13 Dec 2007 09:53:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/12/13/38448.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/38448.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/12/13/38448.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/38448.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/38448.html</trackback:ping><description><![CDATA[<strong><font size=4>哈希表与哈希函数</font></strong> <br>　　哈希查找因使用哈希 (Hash) 函数而得名，<strong>哈希函数</strong>又叫<strong>散列函数</strong>，它是一种能把关键字映射成记录存贮地址的函数。 <br><strong>1.哈希表</strong><br>①它是一种能把关键字映射成记录存贮地址的函数。<br>②假定数组 HT[0 ～ m-1] 为存贮记录的地址空间， m 为表长，哈希函数 H 以记录的关键字 K 为自变量，计算出对应的函数值 H(K) ，并以它作为关键字 K 所标识的记录在表 HT 中的 ( 相对 ) 地址或索引号，这样产生的记录表 HT 叫做对应于哈希函数 H 的<strong>哈希表</strong>。<br>③简言之，在哈希表中，关键字为 K 的记录，存贮在 HT[H(K)] 位置。<br>④哈希函数值 H(K) 称为 K 的哈希地址或散列地址。<br>&nbsp;&nbsp;&nbsp;&nbsp;
<object codeBase=http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0 height=281 width=436 classid=clsid:D27CDB6E-AE6D-11cf-96B8-444553540000>
<embed src="tupian/t9.16.swf" quality="high"
    pluginspage="http://www.macromedia.com/go/getflashplayer"
    type="application/x-shockwave-flash" width="436" height="281"></embed>
    </object><br><strong>3、哈希表的冲突现象</strong><br><font color=#0000ff>（1）冲突</font><br>&nbsp;&nbsp;&nbsp; 　不同的关键字值，具有相同的哈希地址，因而被映射到同一表位置上。该现象称为冲突(Collision)或碰撞。<br>&nbsp; 　【例】上图中的k<sub>2</sub>&#8800;k<sub>5</sub>，但h(k<sub>2</sub>)=h(k<sub>5</sub>)，故k<sub>2</sub>和K<sub>5</sub>所在的结点的存储地址相同。<br><br><font color=#0000ff>（2）安全避免冲突的条件</font><br>&nbsp;&nbsp;&nbsp; 如何避免冲突发生，则取决于哈希函数的构造。 <br>&nbsp;&nbsp;&nbsp; 使散列地址均匀地分布在哈希表的整个地址区间内，这样可以避免或减少发生冲突。<br>&nbsp;&nbsp;&nbsp; 哈希函数的构造，与关键字的长度、哈希表的大小、关键字的实际取值状况等许多因素有关，而且有的因素事前不能确定。所以，避免冲突这并非是件容易做到的事。<br><br><font color=#0000ff>（3）冲突不可能完全避免</font><br>&nbsp;&nbsp;&nbsp; 　由于关键字的值域往往比哈希表的个数大的多，所以哈希函数是一种压缩映射，碰撞是难免的。<br>&nbsp;&nbsp;&nbsp;【例】存贮 100 个学生记录，尽管安排 120 个地址空间，但由于学生名 ( 假设不超过 10 个英文字母 ) 的理论个数超过 2610 ，要找到一个哈希函数把 100 个任意的学生名映射成 [0 ， 119] 内的不同整数，实际上是不可能的。<br><strong>&nbsp;&nbsp;&nbsp;<font color=#ff0000>注意：</font>问题在于一旦发生了冲突应如何处理。<br></strong><br><br><strong><font size=4>构造哈希表</font></strong> <br>　　构造哈希函数的方法很多，这里只介绍一些常用的，计算简便的方法。<br><strong>1.平方取中法</strong><br>　　算出关键字值的平方，再取其中若干位作为哈希函数值 ( 散列地址 ) 。<br>【例】假定表中各关键字是由字母组成的，用二位数字的整数 01 ～ 26 表示对应的 26 个英文字母在计算机中的内部编码，则使用平方取中法计算 KEYA ， KEYB ， AKEY ， BKEY 的散列地址可得：<br>关键字 K &nbsp;&nbsp;&nbsp;&nbsp;K 的内部编码 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;K <sup>2</sup> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;H(K) <br>&nbsp;KEYA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 11052501 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;122157778355001 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;778<br>&nbsp;KEYB &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;11052502 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;122157800460004 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;800<br>&nbsp;AKEY &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;01110525 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;001233265775625 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;265 <br>&nbsp;BKEY &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;02110525 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;004454315775625 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;315 <br>平方之后，取左起第 7 ～ 9 位作为散列地址。<br><br><strong>2.除留余数法</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;这种方法是用模运算 (%) 得到的。设给出的关键字值为 K ，存储区单元数为 m ，则用一个小于 m 的质数 P 去除 K ，得到的余数为 R ，即： R ＝ K % P 。如果 R 落在存储区地址范围内，则 R 就取为哈希函数值 ( 散列地址 ) ；否则，再用一个线性数求出哈希函数值。<br>【例】有一组关键字从 000001 到 859999 ，指定的存储区地址为 1000000 ～ 1005999 ，即 m ＝ 6000 ，可选 P ＝ 599 ，若要转换关键字 K ＝ 172148 ，则有：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;R ＝ 172148 % 599 ＝ 4176 <br>因 R 不在指定的地址范围内，所以，取哈希函数为：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;H(K) ＝ 1000000 ＋ R<br>故有：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;H(K) ＝ H(172148) ＝ 1004176 <br>这样就把关键字 K 直接转换成存储地址了。<br><br><strong>3.数字分析法</strong><br>　　对各个关键字内部代码的各个码位进行分析。假设有 n 个 d 位的关键字，使用 s 个不同的符号 ( 如，对于十进制数，每一位可能出现的符号有 10 个，即 0 、 1 、 2 、&#8230;、 9) ，这 s 个不同的符号在各位上出现的频率不一定相同，它们可能在某些位上分布比较均匀，即每一个符号出现的次数都接近 n/s 次；而在另一些位上分布不均匀。这时，选取其中分布比较均匀的某些位作为哈希函数值 ( 散列地址 ) ，所选取的位数应视存储区地址范围而定，这就是数字分析法。<br><font color=#ff0000>注意：</font><br>　　这种方法适合于关键字值中各位字符分布为已知的情况。<br>例如，给定一组关键字：<br>K 1 ： 542482241 <br>K 2 ： 542813678<br>K 3 ： 532228171<br>K 4 ： 542389671 <br>K 5 ： 542541577 <br>K 6 ： 542985376<br>K 7 ： 542193552 <br><img height=43 src="http://sjjg.js.zwu.edu.cn/SFXX/sanlie/tupian/cc.gif" width=376><br>　　这里 n ＝ 7 ； d ＝ 9 ； s ＝ 10 。为了衡量各位上 s 个字符分布的均匀度，可采用度量标准： 式中 a ik 表示第 i 个字符在第 k 位上出现的 (k ＝ 1 ， 2 ，&#8230;， d) 次数。&#955; k 值越小，可认为分布越均匀。这里，自左向右，各位上字符的分布均匀度为：<br>&#955; １ ＝ (7 － 7/10) 2 ＋ 9 &#215; (0 － 7/10) 2 ＝ 44.1<br>&#955; ２ ＝ 44.1 <br>&#955; ３ ＝ 44.1 <br>&#955; ４ ＝ 7 &#215; (1-7/10) 2 ＋ 3 &#215; (0 － 7/10) 2 ＝ 2.1<br>&#955; ５ ＝ 4 &#215; (1-7/10) 2 ＋ (3 － 7/10) 2 ＋ 5 &#215; (0-7/10) 2 ＝ 8.1 <br>&#955; ６ ＝ 5 &#215; (1-7/10) 2 ＋ (2 － 7/10) 2 ＋ 4 &#215; (0-7/10) 2 ＝ 4.1 <br>&#955; ７ ＝ 3 &#215; (1-7/10) 2 ＋ 2 &#215; (2 － 7/10) 2 ＋ 5 &#215; (0-7/10) 2 ＝ 6.1<br>&#955; ８ ＝ 2 &#215; (1-7/10) 2 ＋ (5 － 7/10) 2 ＋ 7 &#215; (0-7/10) 2 ＝ 22.1 <br>&#955; ９ ＝ 4 &#215; (1-7/10) 2 ＋ (3 － 7/10) 2 ＋ 5 &#215; (0-7/10) 2 ＝ 8.1 <br>　　假定存储区地址为 000 ～ 999 ，则应取关键字的第 4 、 6 、 7 位作为哈希函数值 ( 散列地址 ) ，它们分别为 422 、 836 、 281 、 396 、 515 、 953 和 135 。由于数字分析法需预先知道各位上字符的分布情况，这就大大限制了它的实用性。 <br><br>　 　构造哈希函数除了上面介绍的几种常用方法外，还有截段法，即截取关键字中的某一段数码作为哈希函数；分段迭加法，即把关键字的机内代码分成几段，再进行迭加 ( 可以是算术加，也可以是按位加 ) 得到哈希函数值。对于各种构造哈希函数的方法，很难一概而论地评价其优劣，任何一种哈希函数都应当用实际数据去测试它的均匀性，才能做出正确的判断和结论。 <br><br><br><strong><font size=4>解决冲突的主要方法</font></strong> <br>　　虽然我们不希望发生冲突，但实际上发生冲突的可能性仍是存在的。当关键字值域远大于哈希表的长度，而且事先并不知道关键字的具体取值时。冲突就难免会发生。另外，当关键字的实际取值大于哈希表的长度时，而且表中已装满了记录，如果插入一个新记录，不仅发生冲突，而且还会发生溢出。因此，处理冲突和溢出是哈希技术中的两个重要问题。<br><strong>1、开放定址法</strong><br>&nbsp;&nbsp;&nbsp; 　用开放定址法解决冲突的做法是：当冲突发生时，使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列。沿此序列逐个单元地查找，直到找到给定的关键字，或者碰到一个开放的地址(即该地址单元为空)为止（若要插入，在探查到开放的地址，则可将待插入的新结点存人该地址单元）。查找时探查到开放的地址则表明表中无待查的关键字，即查找失败。<br>&nbsp; <font color=#ff0000>注意：</font><br>&nbsp;①用开放定址法建立散列表时，建表前须将表中所有单元(更严格地说，是指单元中存储的关键字)置空。<br>&nbsp;②空单元的表示与具体的应用相关。<br>&nbsp;&nbsp;&nbsp; 　按照形成探查序列的方法不同，可将开放定址法区分为线性探查法、线性补偿探测法、随机探测等。<br><font color=#0000ff>（1）线性探查法(Linear Probing)</font><br><strong>该方法的基本思想是：</strong><br>　&nbsp;&nbsp;&nbsp; 将散列表T[0..m-1]看成是一个循环向量，若初始探查的地址为d(即h(key)=d)，则最长的探查序列为：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d，d+l，d+2，&#8230;，m-1，0，1，&#8230;，d-1<br>&nbsp;&nbsp;&nbsp; 　即:探查时从地址d开始，首先探查T[d]，然后依次探查T[d+1]，&#8230;，直到T[m-1]，此后又循环到T[0]，T[1]，&#8230;，直到探查到T[d-1]为止。<br><strong>探查过程终止于三种情况：</strong><br>&nbsp;&nbsp;&nbsp; 　(1)若当前探查的单元为空，则表示查找失败（若是插入则将key写入其中）；<br>　&nbsp;&nbsp;&nbsp; (2)若当前探查的单元中含有key，则查找成功，但对于插入意味着失败；<br>&nbsp;&nbsp;&nbsp; 　(3)若探查到T[d-1]时仍未发现空单元也未找到key，则无论是查找还是插入均意味着失败(此时表满)。<br><strong>利用开放地址法的一般形式，线性探查法的探查序列为：</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h<sub>i</sub>=(h(key)+i)％m 0&#8804;i&#8804;m-1 //即d<sub>i</sub>=i<br><strong>用线性探测法处理冲突，思路清晰，算法简单，但存在下列缺点：</strong><br>　　① 处理溢出需另编程序。一般可另外设立一个溢出表，专门用来存放上述哈希表中放不下的记录。此溢出表最简单的结构是顺序表，查找方法可用顺序查找。<br>　　② 按上述算法建立起来的哈希表，删除工作非常困难。假如要从哈希表 HT 中删除一个记录，按理应将这个记录所在位置置为空，但我们不能这样做，而只能标上已被删除的标记，否则，将会影响以后的查找。<br>　　③ 线性探测法很容易产生堆聚现象。所谓堆聚现象，就是存入哈希表的记录在表中连成一片。按照线性探测法处理冲突，如果生成哈希地址的连续序列愈长 ( 即不同关键字值的哈希地址相邻在一起愈长 ) ，则当新的记录加入该表时，与这个序列发生冲突的可能性愈大。因此，哈希地址的较长连续序列比较短连续序列生长得快，这就意味着，一旦出现堆聚 ( 伴随着冲突 ) ，就将引起进一步的堆聚。<br>
<img src ="http://www.cppblog.com/sunraiing9/aggbug/38448.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2007-12-13 17:53 <a href="http://www.cppblog.com/sunraiing9/archive/2007/12/13/38448.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CL.exe的全部命令开关(摘)</title><link>http://www.cppblog.com/sunraiing9/archive/2007/11/26/37323.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Mon, 26 Nov 2007 07:20:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/11/26/37323.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/37323.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/11/26/37323.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/37323.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/37323.html</trackback:ping><description><![CDATA[CL.exe的全部命令开关
<p>&#160;</p>
<p>/C:在预处理输出中保留注释语句<br>/c:只编译，不连接，相当于在"Build"菜单下选择了"Compile"<br>/D:定义常量和宏，与源程序里的#define 有相同效果<br>/E:预处理C、C＋＋源文件，将源文件中所有的预编译指令及宏展开，将注释去掉，然后将预处理器的输出拷贝至标准输出设备输出，并且在每个文件的开头和末尾加入#line<br>/EH:指定编译器用何种异常处理模型<br>/EP:同/E,只是去掉了#line<br>/F:设置程序的堆栈大小<br>/FA:设置生成何种列表文件（汇编、汇编与机器码、汇编与源码、汇编与机器码以及源码）<br>/Fa:指定用/FA设置的列表文件的存放路径及（或）文件名<br>/FD:生成文件的相互依赖信息<br>/Fd:设置程序数据库文件（PDB）的存放路径及（或）文件名<br>/Fe:设置最终可执行文件的存放路径及（或）文件名<br>/FI:预处理指定的头文件，与源文件中的＃include有相同效果<br>/Fm:创建map文件<br>/Fo:设置编译后Obj文件的存放路径及（或）文件名<br>/Fp:设置预编译文件（pch）的存放路径及（或）文件名<br>/FR:生成浏览信息（sbr）文件<br>/Fr:同/FR,不同之处在于/Fr不包括局部变量信息<br>/G3:为80386处理器优化代码生成<br>/G4:为80486处理器优化代码生成<br>/G5:为Pentium处理器优化代码生成<br>/G6:为Pentium Pro处理器优化代码生成<br>/GA:为Windows应用程序作优化<br>/GB:为Pentium处理器优化代码生成，使用80386、80486、Pentium、Pentium Pro的混合指令集，是代码生成的默认选项（程序属性选项中Processor对应Blend）<br>/GD:为Windows动态库（dll）作优化，此开关在VC6中没有实现<br>/Gd:指定使用__cdecl的函数调用规则<br>/Ge:激活堆栈检测<br>/GF:消除程序中的重复的字符串，并将她放到只读的缓冲区中<br>/Gf:消除程序中的重复字符串<br>/Gh:在每个函数的开头调用钩子（hook）函数--penter<br>/Gi:允许渐进编译<br>/Gm:允许最小化rebuild<br>/GR:允许运行时类型信息(Run-Time Type Infomation)<br>/Gr:指定使用__fastcall的函数调用规则<br>/Gs:控制堆栈检测所用内存大小<br>/GT:支持用__declspec(thread)分配的数据的fier-safety<br>/GX:允许同步异常处理，与/EHsc开关等价<br>/Gy:允许编译器将每一个函数封装成COMDATs的形式，供连接器调用<br>/GZ:允许在Debug build 的时候捕捉Release build的错误<br>/Gz:指定使用__stdcall的函数调用规则<br>/H:限制外部名字的长度<br>/HELP:列出编译器的所有的命令开关<br>/I:指定头文件的搜索路径<br>/J:将char的缺省类型从signed char改成unsigned char<br>/LD:创建一个动态连接库<br>/LDd:创建一个Debug版本的动态链接库<br>/link:将指定的选项传给连接器<br>/MD:选择多线程、DLL版本的C Run－Time库<br>/MDd:选择多线程、DLL、Debug版本的C Run－Time库<br>/ML:选择单线程版本的C Run—Time库<br>/MLd:选择单线程、Debug版本的C Run—Time库<br>/MT:选择多线程版本的C Run-Time库<br>/MTd:选择多线程、Debug版本的C Run—Time库<br>/nologo:不显示程序的版权信息<br>/O1:优化使产生的可执行代码最小<br>/O2:优化使产生的可执行代码速度最快<br>/Oa:指示编译器程序里没有使用别名，可以提高程序的执行速度<br>/Ob:控制内联（inline）函数的展开<br>/Od:禁止代码优化<br>/Og:使用全局优化<br>/Oi:用内部函数去代替程序里的函数调用，可以使程序运行的更快，但程序的长度变长<br>/Op:提高浮点数比较运算的一致性<br>/Os:产生尽可能小的可执行代码<br>/Ot:产生尽可能块的可执行代码<br>/Ow:指示编译器在函数体内部没有使用别名<br>/Ox:组合了几个优化开关，达到尽可能多的优化<br>/Oy:阻止调用堆栈里创建帧指针<br>/Q1f:对核心级的设备驱动程序生成单独的调试信息<br>/QI0f:对Pentium 0x0f错误指令作修正<br>/Qifdiv:对Pentium FDIV错误指令作修正<br>/P:将预处理输出写到指定文件里，文件的后缀名为I<br>/TC:将命令行上的所有文件都当作C源程序编译，不管后缀名是否为.c<br>/Tc:将指定的文件当作C源程序编译，不管后缀名是否为.c<br>/TP:将命令行上的所有文件都当作C＋＋源程序编译，不管后缀名是否为.cpp<br>/Tp:将指定文件当作C＋＋源程序编译，不管后缀名是否为.cpp<br>/U:去掉一个指定的前面定义的符号或常量<br>/u:去掉所有前面定义的符号或常量<br>/V:在编译的obj文件里嵌入版本号<br>/vd:禁止/允许构造函数置换<br>/vmb:选择指针的表示方法，使用这个开关，在声明指向某个类的成员的指针之前，必须先定义这个类<br>/vmg:选择指针的表示方法，使用这个开关，在声明指向某个类的成员的指针之前，不必先定义这个类，但要首先指定这个类是使用何种继承方法<br>/vmm:设置指针的表示方法为Single Inheritance and Multiple Inheritance<br>/vms:设置指针的表示方法为Single Inheritance<br>/vmv:设置指针的表示方法为Any class<br>/W:设置警告等级<br>/w:禁止所有警告<br>/X:阻止编译器搜索标准的include 目录<br>/Yc:创建预编译头文件（pch）<br>/Yd:在所有的obj文件里写上完全的调试信息<br>/Yu:在build过程中使用指定的预编译头文件<br>/YX:指示编译器若预编译头文件存在，则使用它，若不存在，则创建一个<br>/Z7:生成MSC7.0兼容的调试信息<br>/Za:禁止语言扩展(Microsoft Extensions to C)<br>/Zd:调试信息只包含外部和全局的符号信息以及行号信息<br>/Ze:允许语言扩展(Microsoft Extensions to C)<br>/Zg:为源文件里面定义的每个函数生成函数原型<br>/ZI:生成程序库文件（Pdb）并支持Edit and Continue调试特性<br>/Zi:生成程序库文件（pdb），包含类型信息和符号调试信息<br>/ZL:从obj文件里去掉缺省的库文件名<br>/Zm:设置编译器的内存分配xianzhi<br>/Zn:禁止浏览信息文件里面的封装<br>/Zp:设置结构成员在内存里面的封装格式<br>/Zs:快速检查语法错误<br>－－－－－－－－－－－－－－－－－－－－－－－－－－<br>vc所支持的文件类型</p>
<p>DSW:全称是Developer Studio Workspace，最高级别的配置文件，记录了整个工作空间的配置信息，她是一个纯文本的文件，在vc创建新项目的时候自动生成<br>DSP:全称是Developer Studio Project，也是一个配置文件，不过她记录的是一个项目的所有配置信息，纯文本文件<br>OPT：与DSW、DSP配合使用的配置文件，她记录了与机器硬件有关的信息，同一个项目在不同的机器上的opt文件内容是不同的<br>CLW：记录了跟ClassWizard相关的信息，如果丢失了clw文件，那么在Class View面板里就没有类信息<br>PLG：实际上是一个超文本文件，可以用Internet Explorer打开，记录了Build的过程，是一个日志型文件<br>RC：资源描述文件，记录了所有的资源信息，在资源编辑器里作的修改，实际上都是对RC文件的修改<br>RC2：附加的资源描述文件，不能直接资源编辑器修改，只能手工添加，可以用来添加额外的资源<br>RES：经过资源编辑器编译之后的资源文件，以二进制方式存放<br>SBR：编译器生成的浏览信息文件，在代码导航的时候非常有用，她需要在编译时指定/FR或者/Fr开关<br>BSC：BSCMAKE.EXE将所有的SBR文件作为输入，经过处理之后输出一个BSC文件，在代码导航的时候实际用到的是BSC文件<br>ILK：当选定渐增型编译连接时，连接器自动生成ILK文件，记录连接信息<br>PDB：全称是Program DataBase，即程序数据库文件，用来记录调试信息，是一个相当重要的文件，没有他，程序无法正常调试<br>LIB：如果项目输出是Dll的话，一般会输出一个跟项目同名的Lib文件，记录输出的函数信息<br>EXP：同Lib，是跟Dll一起生成的输出文件<br>PCH：全称是PreCompiled Header，就是预先编译好的头文件，在编译时指定/Yu开关时编译器自动生成</p>
<img src ="http://www.cppblog.com/sunraiing9/aggbug/37323.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2007-11-26 15:20 <a href="http://www.cppblog.com/sunraiing9/archive/2007/11/26/37323.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>被误解的C++——优化variant实现(摘抄)</title><link>http://www.cppblog.com/sunraiing9/archive/2007/10/09/33854.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Tue, 09 Oct 2007 14:35:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/10/09/33854.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/33854.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/10/09/33854.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/33854.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/33854.html</trackback:ping><description><![CDATA[<div id=aTitle>
<h1>被误解的C++——优化variant实现</h1>
</div>
<div id=aInfo></div>
<div id=aContent>
<div id=googleJuXing_Ad>&nbsp;</div>
<div id=articleText>所属分类：C/C++ C++ 语言<br>-----------------------------------------<br><br>优化variant实现 <br>上一次，我大概制作了一个variant类型，并设法赋予这个类型同C++内置类型几乎一样的行为。但是，具体实现起来，倒是有点望而生畏。想想看，如果我的variant需要包容5种类型，那么单单一个操作符，就需要5&#215;5+1=26个操作符重载（那单独一个是variant类型操作数的重载）。所有二元操作符都是如此。 <br>通过蛮力来实现variant，尽管可能，但着实愚蠢。我们必须寻找更简单有效的实现途径，避免为了一个&#8220;屁眼大的&#8221;variant（请原谅我说粗话）写上几万行代码，而且这些代码就像一窝小猪仔那样相像。好在C++为我们提供了充足的现代武器，使我们拥有足够的火力摆平这些问题。 <br>让我们先从操作数都是variant的二元操作符入手： <br>variant&nbsp;&nbsp;operator+(&nbsp;const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2)&nbsp;{&#8230;} <br>&#8230; <br>简单起见，先考察operator+的实现，然后扩展到其他操作符。 <br>由于操作数是variant类型，那么它们可能代表不同的类型。我们必须知道操作数的实际类型，才能对其实施相应的+操作。最传统的办法就是使用switch： <br>variant&nbsp;operator+(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2)&nbsp;{ <br>switch(v1.get_type_code()) <br>{ <br>case&nbsp;vt_double: <br>switch(v2.get_type_code()) <br>{ <br>case&nbsp;vt_double: <br>&#8230;; <br>break; <br>&#8230; <br>} <br>case&nbsp;vt_int: <br>switch(v2.get_type_code()) <br>{ <br>case&nbsp;vt_double: <br>&#8230;; <br>break; <br>&#8230; <br>} <br>&#8230; <br>} <br>} <br>好家伙，又是一个组合爆炸。一步步来，我们先来处理这堆讨人嫌的switch&#8230;case&#8230;。一般而言，对于一个函数（操作符）内的的大量分派操作，可以使用包含函数指针的数组或者容器替代。如果标记值（这里的vt_...）是连续的，可以直接使用数组；如果标记值不连续，可以使用关联容器。这里vt_...是连续的，所以用数组比较方便： <br>typedef&nbsp;variant&nbsp;(*add_op_t)(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2); <br>add_op_t&nbsp;tbl_type_ops[3][3];//函数指针表，假设variant对应三种类型 <br>variant&nbsp;add_op_double_double(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2){&#8230;} <br>variant&nbsp;add_op_double_int(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2){&#8230;} <br>&#8230; <br>variant&nbsp;add_op_int_double(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2){&#8230;} <br>&#8230; <br>tbl_type_ops&nbsp;[vt_double][vt_double]=add_op_double_double; <br>tbl_type_ops&nbsp;[vt_double][vt_int]=add_op_double_int; <br>&#8230; <br>variant&nbsp;operator+(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2)&nbsp;{ <br>returntbl_type_ops&nbsp;[v1.get_type_code()][v2.get_type_code](v1,&nbsp;v2); <br>} <br>operator+的代码是简单了，但是它的代码实际上转嫁到每个专用操作函数add_op_...上去了。并没有简化多少。下一步，我们来处理这些add_op_...： <br>template&lt;typename&nbsp;VT1,&nbsp;typename&nbsp;VT2&gt; <br>variant&nbsp;add_op(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;v2)&nbsp;{ <br>throwexception(string(&#8220;cannot&nbsp;add&nbsp;type&nbsp;&#8221;)+typeid(VT1).typename() <br>+&#8221;to&#8221;+typeid(VT2).typename()); <br>}//主函数模板，对应不兼容类型的操作。抛出异常。 <br>template&lt;&gt; <br>variant&lt;double,&nbsp;double&gt;&nbsp;add_op(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;v2)&nbsp;{ <br>returnvariant(v1.dbval+v2.dbval); <br>}//针对double+double的操作 <br>&#8230; <br>tbl_type_ops&nbsp;[vt_double][vt_double]=add_op&lt;double,&nbsp;double&gt;; <br>tbl_type_ops&nbsp;[vt_double][vt_int]=add_op&lt;double,int&gt;; <br>&#8230; <br>利用函数模板，及其特化，消化掉一部分的冗余代码。利用主函数模板实现所有不能互操作的类型操作，而可操作的类型则使用特化的模板实现。当然，冗余代码还是存在，这部分我们一会儿再处理。先来看看tbl_type_ops的填充。这部分代码也存在组合爆炸。为消除这个问题，我请出了模板元编程（TMP）。当然，我没有那么好的本事去直接倒腾TMP，我&#8220;借用&#8221;了boost::mpl::vector来实现这步优化： <br>//使用mpl::vector存放variant包容的类型 <br>typedef&nbsp;boost::mpl::vector&lt;double,&nbsp;int,&nbsp;string&gt;op_types; <br>const&nbsp;int&nbsp;n_types=boost::mpl::size&lt;op_types&gt;::value; <br>//操作函数指针表 <br>typedef&nbsp;variant&nbsp;(*add_op_t)(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2); <br>add_op_t&nbsp;tbl_type_ops[n_types][n_types]; <br>//填充函数指针表单个元素 <br>template&lt;int&nbsp;m,&nbsp;int&nbsp;n&gt; <br>inline&nbsp;void&nbsp;set_tbl_type()&nbsp;{ <br>typedefmpl::deref&lt;mpl::advance&lt;mpl::begin&lt;op_types&gt;::type,&nbsp; <br>mpl::int_&lt;m&gt;&nbsp;&gt;::type&gt;::typetype_1; <br>typedefmpl::deref&lt;mpl::advance&lt;mpl::begin&lt;op_types&gt;::type,&nbsp; <br>mpl::int_&lt;n&gt;&nbsp;&gt;::type&gt;::typetype_2; <br><br>tbl_type_ops&nbsp;[m][n]=add_op&lt;type_1,&nbsp;type_2&gt;; <br>} <br>//填充函数指针表单元的函数对象类 <br>template&lt;int&nbsp;m,&nbsp;int&nbsp;n&gt; <br>struct&nbsp;fill_tbl_types_n <br>{ <br>void&nbsp;operator()()&nbsp;{ <br>set_tbl_type&lt;m-1,&nbsp;n-1&gt;();//填充函数指针单元 <br>fill_tbl_types_n&lt;m,&nbsp;n-1&gt;()();//递归 <br>} <br>}; <br>template&lt;int&nbsp;m&gt; <br>struct&nbsp;fill_tbl_types_n&lt;m,&nbsp;0&gt;//特化，递归结束 <br>{ <br>void&nbsp;operator()()&nbsp;{} <br>}; <br>//填充函数指针表行的函数对象类 <br>template&lt;int&nbsp;m,&nbsp;int&nbsp;n&gt; <br>struct&nbsp;fill_tbl_types_m <br>{ <br>void&nbsp;operator()()&nbsp;{ <br>fill_tbl_types_n&lt;m,&nbsp;n&gt;()();//创建并调用fill_tbl_types_n函数对象 <br>fill_tbl_types_m&lt;m-1,&nbsp;n&gt;()();//递归 <br>} <br>}; <br>template&lt;int&nbsp;n&gt; <br>struct&nbsp;fill_tbl_types_m&lt;0,&nbsp;n&gt;//特化，递归结束 <br>{ <br>void&nbsp;operator()()&nbsp;{} <br>}; <br>void&nbsp;fill_tbl_op()&nbsp;{ <br>fill_tbl_types_m&lt;n_types,&nbsp;n_types&gt;()(); <br>} <br>这里运用函数对象类模板的特化，构造了函数指针表的填充自动函数。在需要时，只需调用fill_tbl_op()函数即可。该函数中创建fill_tbl_types_m&lt;n_types,&nbsp;n_types&gt;函数对象，然后调用。这个函数对象的operator()首先创建并调用fill_tbl_types_n&lt;m,&nbsp;n&gt;函数对象。后者先调用set_tbl_type&lt;m-1,&nbsp;n-1&gt;模板函数，执行填充tbl_type_op数组的[m-1,&nbsp;n-1]单元格。然后递归调用fill_tbl_types_n&lt;m,&nbsp;n-1&gt;函数对象。直到n-1==0，编译器便会选择特化版本的fill_tbl_types_n&lt;m,&nbsp;0&gt;函数对象。该特化的operator()操作符重载是空的，因此递归结束。这样完成一行的填充。然后，fill_tbl_types_m&lt;m,&nbsp;n&gt;则递归调用fill_tbl_types_m&lt;m-1,&nbsp;n&gt;函数对象，填充下一行。直到调用fill_tbl_types_m&lt;0,&nbsp;n&gt;特化版本，结束递归。 <br>现在需要仔细看一下set_tbl_type&lt;&gt;函数模板。该模板上来就是两个typedef。这两个typedef创建了两个类型别名，分别用m和n做索引，从boost::mpl::vector&lt;double,&nbsp;int,&nbsp;string&gt;中取出相应的类型： <br>typedefmpl::deref&lt;mpl::advance&lt;mpl::begin&lt;op_types&gt;::type,&nbsp; <br>mpl::int_&lt;m&gt;&nbsp;&gt;::type&gt;::typetype_1; <br>&#8230; <br>头晕是吧。我的头还有点晕呢。这就是模板元编程，不停地鼓捣类型。具体的操作可以参考boost文档或《The&nbsp;Template&nbsp;Meta-programming》一书，我这里就不多说了，反正就是从一个存放类型的vector中取出所需的类型。 <br>这样获得的两个类型用来实例化add_op&lt;&gt;()模板函数，并且填充到tbl_type_ops[m][n]元素中。 <br>这样，利用TMP和GP两种强大的机制，消除了tbl_type_ops填充的组合爆炸问题。如果我们需要向variant中加入新的类型，那么只需在mpl::vector&lt;double,&nbsp;int,&nbsp;string&gt;中直接加入类型即可： <br>typedef&nbsp;mpl::vector&lt;double,&nbsp;int,&nbsp;string,&nbsp;bool,&nbsp;datetime&gt;op_types; <br>OK，下面回过头，来处理add_op&lt;&gt;中存在的组合爆炸。对于每一对可以直接或间接相加的类型，都需要做一个add_op&lt;&gt;的特化版本。这当然不够好。我们可以进一步抽象add_op，然后加以优化。我把整个add_op&lt;&gt;模板改写成如下代码： <br>template&lt;typename&nbsp;VT1,&nbsp;typename&nbsp;VT2&gt; <br>variant&nbsp;add_op(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2)&nbsp;{ <br>typedeftype_ret&lt;VT1,&nbsp;VT2&gt;::typeRetT; <br>returnvariant(v1.operator&nbsp;RetT()+v2.operator&nbsp;RetT()); <br>} <br>这里，我首先利用type_ret模板（模板元函数）获得两个操作数相加后应有的返回类型。这个模板一会说明。然后，调用variant上的类型转换操作符，将两个操作数转换成返回类型。最后相加，并创建返回variant对象。代码非常简单，没法再简单了。 <br>再来看看type_ret&lt;&gt;： <br>template&lt;typename&nbsp;T1,&nbsp;typename&nbsp;T2&gt; <br>struct&nbsp;type_ret <br>{ <br>typedefT1type; <br>}; <br>template&lt;&gt; <br>struct&nbsp;type_ret&lt;int,&nbsp;double&gt; <br>{ <br>typedefdoubletype; <br>}; <br>template&lt;&gt; <br>struct&nbsp;type_ret&lt;string,&nbsp;double&gt; <br>{ <br>typedefdoubletype; <br>}; <br>&#8230;//其他类型对的返回类型 <br>type_ret&lt;&gt;是典型的模板元函数，没有任何实际代码，只有编译时计算的typedef。主模板将第一个类型参数typedef出一个别名。其后的模板特化对于一些特殊的情况做出定义，如int和double相加返回第二个操作数类型double（即所谓的类型提升）。 <br>我们现在已经优化了variant+varint的代码。现在来看看如何优化variant类型和其他类型的加法： <br>template&lt;typename&nbsp;T&gt; <br>variant&nbsp;operator+(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;T&amp;&nbsp;v2)&nbsp;{ <br>returnv1+variant(v2); <br>} <br>template&lt;typename&nbsp;T&gt; <br>variant&nbsp;operator+(const&nbsp;T&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2)&nbsp;{ <br>returnvariant(v1)+v2; <br>} <br>这非常简单，直接利用了variant+variant，将其它类型的操作数转换成variant类型，然后相加。 <br><br><br>----------------------------------------------------------------------<br><br>好，加法完成了。但还有其他操作符。每个操作符都做那么一个函数指针表，也不见得高明到哪里去。现在需要整合优化这些操作符。这里，我想到了两种方法：一种是将函数指针表和填充操作整个地封装在一个模板中，模板参数采用int&nbsp;op形式。每一种操作符对应一个整数（或枚举值），并利用某种手段（如singleton）唯一生成一组全局的函数表，以此处理每一种操作。另一种方法是为函数指针表加一个维度（二维扩展到三维），新的维度对应不同的操作符。前一种方法灵活性强些，而且有利于性能优化；而后一种方法实现简单。这里我使用后一种方法： <br>enum <br>{ <br>vt_op_add=0, <br>vt_op_add_assign=1, <br>vt_op_equal=2, <br>vt_op_not_equal=3 <br>&#8230; <br>}; <br>const&nbsp;int&nbsp;vt_op_num=10; <br><br>template&lt;typename&nbsp;T,&nbsp;int&nbsp;op&gt; <br>struct&nbsp;var_op; <br><br>template&lt;typename&nbsp;T&gt; <br>struct&nbsp;var_op&lt;T,&nbsp;vt_op_add&gt; <br>{ <br>T&nbsp;operator()(const&nbsp;T&amp;&nbsp;v1,&nbsp;const&nbsp;T&amp;&nbsp;v2)&nbsp;{ <br>returnv1+v1; <br>} <br>} <br><br>template&lt;typename&nbsp;T&gt; <br>struct&nbsp;var_op&lt;T,&nbsp;vt_op_equal&gt; <br>{ <br>bool&nbsp;operator()(const&nbsp;T&amp;&nbsp;v1,&nbsp;const&nbsp;T&amp;&nbsp;v2)&nbsp;{ <br>returnv1==v1; <br>} <br>} <br>&#8230; <br>template&lt;typename&nbsp;VT1,&nbsp;typename&nbsp;VT2,&nbsp;int&nbsp;op&gt; <br>variant&nbsp;variant_op(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2)&nbsp;{ <br>typedeftype_ret&lt;VT1,&nbsp;VT2&gt;::typeRetT; <br>returnvariant(var_op&lt;RetT,op&gt;()(v1.operator&nbsp;RetT()+v2.operator&nbsp;RetT())); <br>} <br>我使用了一个函数对象模板var_op&lt;&gt;抽象各种算法（二元）。针对每一种运算符特化。抽象的variant_op函数模板实例化var_op&lt;&gt;，然后调用。获得相应的操作。 <br>观察variant_op的模板参数，会发现已经包含了一个操作的基本要素。（眼下这个形式正好符合逆波兰表达式）。 <br>接下来，只需将函数指针数组，及其填充算法加以扩展，便可大功告成： <br>add_op_t&nbsp;tbl_type_ops[n_types][n_types][vt_op_num]; <br>//填充函数指针表单个元素 <br>template&lt;int&nbsp;m,&nbsp;int&nbsp;n,&nbsp;int&nbsp;op&gt; <br>inline&nbsp;void&nbsp;set_tbl_type()&nbsp;{ <br>typedefmpl::deref&lt;mpl::advance&lt;mpl::begin&lt;op_types&gt;::type,&nbsp; <br>mpl::int_&lt;m&gt;&nbsp;&gt;::type&gt;::typetype_1; <br>typedefmpl::deref&lt;mpl::advance&lt;mpl::begin&lt;op_types&gt;::type,&nbsp; <br>mpl::int_&lt;n&gt;&nbsp;&gt;::type&gt;::typetype_2; <br><br>tbl_type_ops&nbsp;[m][n][op]=add_op&lt;type_1,&nbsp;type_2,&nbsp;op&gt;; <br>} <br><br>template&lt;int&nbsp;m,&nbsp;int&nbsp;n,&nbsp;int&nbsp;op&gt; <br>struct&nbsp;fill_tbl_types_op <br>{ <br>void&nbsp;operator()()&nbsp;{ <br>set_tbl_type&lt;m-1,&nbsp;n-1,&nbsp;op-1&gt;(); <br>fill_tbl_types_op&lt;m,&nbsp;n,&nbsp;op-1&gt;()();//递归 <br>} <br>}; <br>template&lt;int&nbsp;m,&nbsp;int&nbsp;n&gt; <br>struct&nbsp;fill_tbl_types_op&lt;m,&nbsp;n,&nbsp;0&gt;//特化，递归结束 <br>{ <br>void&nbsp;operator()(){} <br>} <br><br>template&lt;int&nbsp;m,&nbsp;int&nbsp;n,&nbsp;int&nbsp;op&gt; <br>struct&nbsp;fill_tbl_types_n <br>{ <br>void&nbsp;operator()()&nbsp;{ <br>fill_tbl_types_op&lt;m,&nbsp;n,&nbsp;op&gt;(); <br>fill_tbl_types_n&lt;m,&nbsp;n-1,&nbsp;op&gt;()();//递归 <br>} <br>}; <br>template&lt;int&nbsp;m,&nbsp;int&nbsp;op&gt; <br>struct&nbsp;fill_tbl_types_n&lt;m,&nbsp;0,&nbsp;op&gt;//特化，递归结束 <br>{ <br>void&nbsp;operator()()&nbsp;{} <br>}; <br><br>template&lt;int&nbsp;m,&nbsp;int&nbsp;n,&nbsp;int&nbsp;op&gt; <br>struct&nbsp;fill_tbl_types_m <br>{ <br>void&nbsp;operator()()&nbsp;{ <br>fill_tbl_types_n&lt;m,&nbsp;n,&nbsp;op&gt;()(); <br>fill_tbl_types_m&lt;m-1,&nbsp;n,&nbsp;op&gt;()();//递归 <br>} <br>}; <br>template&lt;int&nbsp;n,&nbsp;int&nbsp;op&gt; <br>struct&nbsp;fill_tbl_types_m&lt;0,&nbsp;n,&nbsp;op&gt;//特化，递归结束 <br>{ <br>void&nbsp;operator()()&nbsp;{} <br>}; <br><br>void&nbsp;fill_tbl_op()&nbsp;{ <br>fill_tbl_types_m&lt;n_types,&nbsp;n_types,&nbsp;vt_op_num&gt;()(); <br>} <br><br>template&lt;typename&nbsp;RetT,&nbsp;int&nbsp;op&gt; <br>struct&nbsp;var_oper <br>{ <br>RetT&nbsp;operator()(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2)&nbsp;{ <br>returntbl_type_ops&nbsp;[v1.get_type_code()][v2.get_type_code] <br>[op](v1,&nbsp;v2).operator&nbsp;RetT(); <br>} <br>template&lt;int&nbsp;op&gt; <br>struct&nbsp;var_oper&lt;variant,&nbsp;op&gt; <br>{ <br>variant&nbsp;operator()(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2)&nbsp;{ <br>returntbl_type_ops&nbsp;[v1.get_type_code()][v2.get_type_code] <br>[op](v1,&nbsp;v2); <br>} <br>于是操作符的实现，成了以下形式： <br>variant&nbsp;operator+(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2)&nbsp;{ <br>returnvar_oper&lt;variant,&nbsp;vt_op_add&gt;(v1,&nbsp;v2); <br>} <br>bool&nbsp;operator==(const&nbsp;variant&amp;&nbsp;v1,&nbsp;const&nbsp;variant&amp;&nbsp;v2)&nbsp;{ <br>returnvar_oper&lt;bool,&nbsp;vt_op_equal&gt;(v1,&nbsp;v2); <br>} <br>&#8230; <br>如果还觉得复杂，那么可以进一步使用宏做一些包装。 <br>好了，variant的优化基本上完成了。当然还会有一些方面值得我们去进一步地优化，比如可以利用boost的type&nbsp;traits和标准库的limit优化type_ret模板的实现和类型转换操作的实现等等。这里不再赘述。 <br>需要说明的是，整个优化仅仅针对代码，并未考虑性能问题。在优化的过程中，某些手法的使用实际上降低的性能。比如函数指针表存在间接调用，不如直接使用inline函数来的高效。而且，函数指针表要求所有指向的函数必须以相同的类型返回。为了兼容+、-等操作，我使用了值返回。但对于+=等操作符完全可以利用引用返回，以提升性能。如果要解决这种问题，需要用前面提到的模板封装函数指针表的方案，为每一个操作符创建一个函数指针表加以解决。 <br>另一个性能问题主要是在variant与其它类型的操作中，其它类型转换成variant类型然后再计算。比起直接使用目标类型计算慢不少。这个问题也可以利用GP和TMP消除，但代码会复杂不少。 <br>理论上，利用inline和编译器优化，可以消除大部分性能问题。但不是所有的，函数指针表的间接调用，是无论如何也优化不掉的。 <br>此外，我在实现函数指针表的构造算法时，没有使用函数模板，而是使用了函数对象模板（重载operator()的模板）。这是因为函数模板目前不能局部特化，而这里是必须的。另一方面，由于使用了递归，函数模板无法做到inline()，而使用函数对象模板则不会有此限制。表达式fill_tbl_types_m()();最终（优化）编译后的结果会是这样（伪码）： <br>tbl_type_ops&nbsp;[2][2][0]=add_op&lt;string,&nbsp;string,&nbsp;0&gt;; <br>tbl_type_ops&nbsp;[2][1][0]=add_op&lt;string,&nbsp;int,&nbsp;0&gt;; <br>&#8230; <br>tbl_type_ops&nbsp;[1][2][0]=add_op&lt;int,&nbsp;string,&nbsp;0&gt;; <br>&#8230; <br>递归和函数对象的调用没有了，完全inline化了。inline函数有时却无法做到这一点。而fill_tbl_types_op等模板实际上起到了代码生成器的作用。这也是GP的一个鲜为人知的功能。如果你有一大堆代码需要编写，而这些代码有很强的规律性和重复性，那么请优先考虑使用模板来为你生成代码，又快又好。 <br>该总结了。如果审视一些代码，会发现只要存在重复和规律性，我们总能利用一些技术和方法加以优化，减少代码量，简化代码结构，减少潜在错误，最终提高开发效率。这里，我使用了C++的泛型编程和模板元编程技术，大幅优化了variant类型中的大量冗余代码。并且为variant类型构建了一个灵活，而又易于扩充的结构。此类技术有很广的应用，不仅仅局限在variant这种底层构件中。相关的一个应用就是构造抽象类工厂，在《Modren&nbsp;C++&nbsp;Design》一书中，有很完整的案例。 <br>此外，这类技术对于调和运行时多态（OOP）和编译时多态（GP）的矛盾有很大的作用。variant只有在运行时方能确定其具体的类型，而C++的模板只能提供编译时的GP。我利用函数指针数组（当然在更复杂的应用中，可以利用OOP的动多态机制），实现运行时分派操作。而利用GP和TMP大幅简化函数指针数组、操作实现函数，以及操作符的构造。这些技术和方法可以在大多数需要运行时多态，但又存在大量重复或雷同代码的地方得以应用。 <br><br></div>
</div>
<img src ="http://www.cppblog.com/sunraiing9/aggbug/33854.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2007-10-09 22:35 <a href="http://www.cppblog.com/sunraiing9/archive/2007/10/09/33854.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>函数指针当参数传入的写法</title><link>http://www.cppblog.com/sunraiing9/archive/2007/10/08/33748.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Mon, 08 Oct 2007 03:28:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/10/08/33748.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/33748.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/10/08/33748.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/33748.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/33748.html</trackback:ping><description><![CDATA[<p>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>int F1(int N);<br>int F2(int N);<br>int MyF(int (*pF)(int), int);</p>
<p>void main()<br>{<br>&nbsp;cout&lt;&lt;"函数指针调用F1，此时的结果为："&lt;&lt;MyF(F1, 4)&lt;&lt;endl;<br>&nbsp;cout&lt;&lt;"函数指针调用F2，此时的结果为："&lt;&lt;MyF(F2, 4)&lt;&lt;endl;<br>}</p>
<p>//函数F1、F2、pF的定义<br>int F1(int N)<br>{<br>&nbsp;return N*N;<br>}</p>
<p>int F2(int N)<br>{<br>&nbsp;return N+N;<br>}</p>
<p>int MyF(int (*pF)(int), int N)<br>{<br>&nbsp;return pF(N);<br>}</p>
<img src ="http://www.cppblog.com/sunraiing9/aggbug/33748.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2007-10-08 11:28 <a href="http://www.cppblog.com/sunraiing9/archive/2007/10/08/33748.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Assert 宏</title><link>http://www.cppblog.com/sunraiing9/archive/2007/09/21/32595.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Thu, 20 Sep 2007 22:55:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/09/21/32595.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/32595.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/09/21/32595.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/32595.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/32595.html</trackback:ping><description><![CDATA[<div class=postbody>Assert 宏应该大家都知道是干什么用的吧， 可能大家一般都用来检查下指针为空啊。 <br>不过assert还有许多使用技巧的。 <br><br>1.基本用法 <br><br>void VectorNormalize(Vec* src, Vec* dat) <br>{ <br>float length; <br>assert(src!=0);//检查src向量必须不为空 <br>assert(dst!=0);//检查dst向量必须不为空 <br><br>.................. <br>.................. <br>} <br><br>2.让assert嵌入更多的信息 <br><br>void VectorNormalize(Vec* src, Vec* dst) <br>{ <br>float length; <br>assert(src!=0 &amp;&amp; "VectorNormalize: src vector pointer is Null"); <br>assert(dst!=0 &amp;&amp; "VectorNormalize: dst vector pointer is Null"); <br><br>................... <br>................... <br>} <br>有了这个直观的字符串提示，就可以告诉当前的函数名，错误原因。 <br></div>
<img src ="http://www.cppblog.com/sunraiing9/aggbug/32595.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2007-09-21 06:55 <a href="http://www.cppblog.com/sunraiing9/archive/2007/09/21/32595.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>虚拟继承</title><link>http://www.cppblog.com/sunraiing9/archive/2007/09/21/32594.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Thu, 20 Sep 2007 22:47:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/09/21/32594.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/32594.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/09/21/32594.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/32594.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/32594.html</trackback:ping><description><![CDATA[class &nbsp; shape &nbsp; { &nbsp; <br>&nbsp; public: &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; shape(int){} &nbsp; <br>&nbsp; }; &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; class &nbsp; circle: &nbsp; public &nbsp; shape &nbsp; { &nbsp; <br>&nbsp; public: &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; circle(int): &nbsp; shape(1){} &nbsp; <br>&nbsp; }; &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; class &nbsp; square: &nbsp; public &nbsp; shape &nbsp; { &nbsp; <br>&nbsp; public: &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; square(int): &nbsp; shape(1){} &nbsp; <br>&nbsp; }; &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; class &nbsp; bizzare: &nbsp; public &nbsp; circle, &nbsp; public &nbsp; square &nbsp; { &nbsp; <br>&nbsp; public: &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bizzare(): &nbsp; circle(1), &nbsp; square(1){} &nbsp; <br>&nbsp; }; &nbsp; <br>&nbsp; 这里用的就是一般的多重继承，没有虚拟继承的存在。在bizzare（之所以叫这个名字，因为&#8230;&#8230;从circle和square继承而来实在是够怪异的了，呵呵）类的member &nbsp; initialization &nbsp; list中，只需要写circle和square类的构造函数就可以了，后两者再分别调用它们的基类shape（分别的两个实例）的构造函数。 &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; 而如果是虚拟继承： &nbsp; <br>&nbsp; class &nbsp; shape &nbsp; { &nbsp; <br>&nbsp; public: &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; shape(int){} &nbsp; <br>&nbsp; }; &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; class &nbsp; circle: &nbsp; public &nbsp; virtual &nbsp; shape &nbsp; { &nbsp; <br>&nbsp; public: &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; circle(int): &nbsp; shape(1){} &nbsp; <br>&nbsp; }; &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; class &nbsp; square: &nbsp; public &nbsp; virtual &nbsp; shape &nbsp; { &nbsp; <br>&nbsp; public: &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; square(int): &nbsp; shape(1){} &nbsp; <br>&nbsp; }; &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; class &nbsp; bizzare: &nbsp; public &nbsp; circle, &nbsp; public &nbsp; square &nbsp; { &nbsp; <br>&nbsp; public: &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bizzare(): &nbsp; circle(1), &nbsp; square(1), &nbsp; shape(1){} &nbsp; <br>&nbsp; }; &nbsp; <br>&nbsp; 则必须在most &nbsp; derived的派生类中初始化虚拟基类。 &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; TC++PL上肯定有说的，在class &nbsp; hierarchy那章。<br><br><br><br><br>这是语言规定。因为只有一个sharp对象，从哪个基类构造都有问题（因为它们可能调用虚基类的不同构造函数，产生二义性），所以标准规定必须在most &nbsp; derived构造时显式或隐式（虚基类有缺省构造函数时）构造虚基类。楼主的例子里sharp没有缺省构造函数，所以必须显式构造。 &nbsp; <br>&nbsp; TC++PL里有： &nbsp; <br>&nbsp; The &nbsp; constructor &nbsp; of &nbsp; a &nbsp; virtual &nbsp; base &nbsp; is &nbsp; invoked &nbsp; (implicitly &nbsp; or &nbsp; explicitly) &nbsp; from &nbsp; the &nbsp; constructor &nbsp; for &nbsp; the &nbsp; complete &nbsp; object &nbsp; (the &nbsp; constructor &nbsp; for &nbsp; the &nbsp; most &nbsp; derived &nbsp; class).<br><br><br>虚拟继承：发生在多重继承上，比如：一个类继承两类，但是这两个类都继承一个类。（类图出现环），即&#8220;孙子&#8221;到&#8220;爷爷&#8221;的<nobr oncontextmenu="return false;" onmousemove=kwM(3); id=key3 onmouseover="kwE(event,3, this);" style="COLOR: #6600ff; BORDER-BOTTOM: 0px dotted; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">路径</nobr>超过一条时，使用，要不会有两个&#8220;爷爷&#8221; &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; 虚函数：与继承完成多态工作。是面向对象关键中的关键。由于面向对象的设计是自顶向下的，先功能定义在实现。继承主要有实现继承和接口继承，都需要多态&nbsp;&nbsp; <br>
<img src ="http://www.cppblog.com/sunraiing9/aggbug/32594.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2007-09-21 06:47 <a href="http://www.cppblog.com/sunraiing9/archive/2007/09/21/32594.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>可变参数函数设计</title><link>http://www.cppblog.com/sunraiing9/archive/2007/09/02/31378.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Sun, 02 Sep 2007 04:29:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/09/02/31378.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/31378.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/09/02/31378.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/31378.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/31378.html</trackback:ping><description><![CDATA[可变参数函数设计<br>
<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"><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">stdafx.h</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"><br>#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>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdarg.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;mul(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;num,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;data1,<img src="http://www.cppblog.com/Images/dot.gif">)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;total&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;data1;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;arg,i;<br>&nbsp;&nbsp;&nbsp;&nbsp;va_list&nbsp;ap;<br>&nbsp;&nbsp;&nbsp;&nbsp;va_start(ap,data1);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(i</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;i</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">num;i</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arg&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;va_arg(ap,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;total</span><span style="COLOR: #000000">*=</span><span style="COLOR: #000000">arg;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;va_end(ap);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;total;<br>}<br><br></span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;mul2(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i,<img src="http://www.cppblog.com/Images/dot.gif">)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">p,j;<br>&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">i</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">p指向参数列表下一个位置</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;s&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">p;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(j</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;j</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">i;j</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s&nbsp;</span><span style="COLOR: #000000">*=</span><span style="COLOR: #000000">&nbsp;p[j];<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;s;<br>}<br><br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">%d\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,mul(</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">));<br>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">%d\n</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,mul2(</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">));<br>&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>}</span></div>
<br><br><br>printf的设计<br>
<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"><span style="COLOR: #000000">#include&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">stdio.h</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"><br>#include&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">stdlib.h</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"><br>#include&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">stdarg.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br><br></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;myprintf(</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;fmt,&nbsp;)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">一个简单的类似于printf的实现，</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">参数必须都是int&nbsp;类型</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">char*&nbsp;pArg=NULL;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">等价于原来的va_list</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;va_list&nbsp;pArg;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;c;<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;pArg&nbsp;=&nbsp;(char*)&nbsp;&amp;fmt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">注意不要写成p&nbsp;=&nbsp;fmt&nbsp;!!因为这里要对参数取址，而不是取值<br>&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;pArg&nbsp;+=&nbsp;sizeof(fmt);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">等价于原来的va_start&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;va_start(pArg,fmt);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c&nbsp;</span><span style="COLOR: #000000">=*</span><span style="COLOR: #000000">fmt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(c&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;putchar(c);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">照原样输出字符</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">按格式字符输出数据</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">switch</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">*++</span><span style="COLOR: #000000">fmt)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">case</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">d</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">%d</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">((</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)pArg));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">case</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">x</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">%#x</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">((</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)pArg));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">case</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">f</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">%f</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">((</span><span style="COLOR: #0000ff">float</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)pArg));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">default</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">pArg&nbsp;+=&nbsp;sizeof(int);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">等价于原来的va_arg</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;va_arg(pArg,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">fmt;<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">fmt&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">\0</span><span style="COLOR: #000000">'</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">pArg&nbsp;=&nbsp;NULL;&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;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">等价于va_end</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;va_end(pArg);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">;<br>}<br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;argc,&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;argv[])<br>{<br>&nbsp;&nbsp;&nbsp;&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">1234</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;j&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">5678</span><span style="COLOR: #000000">;<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;myprintf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">the&nbsp;first&nbsp;test:i=%d</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,i,j);<br>&nbsp;&nbsp;&nbsp;&nbsp;myprintf(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">the&nbsp;secend&nbsp;test:i=%f;&nbsp;%x;j=%d;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,i,</span><span style="COLOR: #000000">0xabcd</span><span style="COLOR: #000000">,j);<br>&nbsp;&nbsp;&nbsp;&nbsp;system(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">pause</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br>&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>}</span></div>
<br><br><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">可变参数在编译器中的处理</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">&nbsp;<br><br></span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;我们知道</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">va_start,va_arg,va_end</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">是在</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">stdarg.h</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">中被定义成宏的</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">, </span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">由于</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">1)</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">硬件平台的不同</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana"> 2)</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">编译器的不同</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">所以定义的宏也有所不同</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">下面以</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">VC++</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">中</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">stdarg.h</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">里</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">x86</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">平台的宏定义摘录如下</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">(&#8217;"&#8217;</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">号表示折行</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">): <br>
<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 alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">typedef&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;va_list;&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;_INTSIZEOF(n)&nbsp;\&nbsp;</span><span style="COLOR: #000000"><br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>((</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(n)</span><span style="COLOR: #000000">+</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">&amp;~</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">)&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;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;va_start(ap,v)&nbsp;(&nbsp;ap&nbsp;=&nbsp;(va_list)&amp;v&nbsp;+&nbsp;_INTSIZEOF(v)&nbsp;)&nbsp;</span><span style="COLOR: #000000"><br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;va_arg(ap,t)&nbsp;\&nbsp;</span><span style="COLOR: #000000"><br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>(&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(t&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">)((ap&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;_INTSIZEOF(t))&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;_INTSIZEOF(t))&nbsp;)&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;va_end(ap)&nbsp;(&nbsp;ap&nbsp;=&nbsp;(va_list)0&nbsp;)&nbsp;</span><span style="COLOR: #000000"><br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p class=MsoNormal><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;定义</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">_INTSIZEOF(n)</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">主要是为了某些需要内存的对齐的系统</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">.C</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">语言的函数是从右向左压入堆栈的</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">图</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">(1)</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">是函数的参数在堆栈中的分布位置</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">.</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">我们看到</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">va_list</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">被定义成</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">char*,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">有一些平台或操作系统定义为</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">void*.</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">再看</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">va_start</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">的定义</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">定义为</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">&amp;v+_INTSIZEOF(v),</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">而</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">&amp;v</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">是固定参数在堆栈的地址</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">所以我们运行</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">va_start(ap, v)</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">以后</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,ap</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">指向第一个可变参数在堆栈的地址</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">如图</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">: <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 alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">高地址</span><span style="COLOR: #000000">|-----------------------------|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">函数返回地址&nbsp;</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|-----------------------------|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|</span><span style="COLOR: #000000"><img alt="" src="http://www.cnblogs.com/Images/dot.gif"><img alt="" src="http://www.cnblogs.com/Images/dot.gif">.&nbsp;</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|-----------------------------|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">第n个参数(第一个可变参数)&nbsp;</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|-----------------------------|&lt;--</span><span style="COLOR: #000000">va_start后ap指向&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">第n</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1个参数(最后一个固定参数)</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>低地址</span><span style="COLOR: #000000">|-----------------------------|&lt;--</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">v&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>图(&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;)&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p class=MsoNormal><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;然后</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">我们用</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">va_arg()</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">取得类型</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">t</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">的可变参数值</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">以上例为</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">int</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">型为例</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">我们看一下</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">va_arg</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">取</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">int</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">型的返回值</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">: j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) ); <br></span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">首先</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">ap+=sizeof(int),</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">已经指向下一个参数的地址了</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">.</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">然后返回</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">ap-sizeof(int)</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">的</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">int*</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">指针</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">这正是第一个可变参数在堆栈里的地址</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">(</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">图</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">2).</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">然后用</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">*</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">取得这个地址的内容</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">(</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">参数值</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">)</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体">赋给</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">j. <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 alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">高地址</span><span style="COLOR: #000000">|-----------------------------|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">函数返回地址&nbsp;</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|-----------------------------|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|</span><span style="COLOR: #000000"><img alt="" src="http://www.cnblogs.com/Images/dot.gif"><img alt="" src="http://www.cnblogs.com/Images/dot.gif">.&nbsp;</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|-----------------------------|&lt;--</span><span style="COLOR: #000000">va_arg后ap指向&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">第n个参数(第一个可变参数)&nbsp;</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|-----------------------------|&lt;--</span><span style="COLOR: #000000">va_start后ap指向&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">第n</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1个参数(最后一个固定参数)</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>低地址</span><span style="COLOR: #000000">|-----------------------------|&lt;--</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">v&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>图(&nbsp;</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">&nbsp;)&nbsp;<br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><br><img alt="" src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p class=MsoNormal><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;最后要说的是</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">va_end</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">宏的意思</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,x86</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">平台定义为</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">ap=(char*)0;</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">使</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">ap</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">不再指向堆栈</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">而是跟</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">NULL</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">一样</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">.</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">有些直接定义为</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">((void*)0),</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">这样编译器不会为</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">va_end</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">产生代码</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">例如</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">gcc</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">在</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">linux</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">x86</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">平台就是这样定义的</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">.</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">在这里大家要注意一个问题</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">:</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">由于参数的地址用于</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">va_start</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">宏</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">所以参数不能声明为寄存器变量或作为函数或数组类型</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">.</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">关于</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">va_start, va_arg, va_end</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">的描述就是这些了</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">我们要注意的是不同的操作系统和硬件平台的定义有些不同</span><span lang=EN-US style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">,</span><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">但原理却是相似的</span></p>
</span></span></span>
<img src ="http://www.cppblog.com/sunraiing9/aggbug/31378.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2007-09-02 12:29 <a href="http://www.cppblog.com/sunraiing9/archive/2007/09/02/31378.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MFC  RTTI的三个宏</title><link>http://www.cppblog.com/sunraiing9/archive/2007/08/29/31158.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Wed, 29 Aug 2007 07:18:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/08/29/31158.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/31158.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/08/29/31158.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/31158.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/31158.html</trackback:ping><description><![CDATA[<br><br>mfc的三个宏 &nbsp; &nbsp; <br>&nbsp; DECLARE_DYNAMIC &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 支持RTTI &nbsp; <br>&nbsp; DECLARE_DYNCREATE &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 支持RTTI &nbsp; 类动态创建 &nbsp; <br>&nbsp; DECLARE_SERIAL &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 支持RTTI &nbsp; 类动态创建 &nbsp; 及序列化&nbsp;&nbsp;&nbsp;&nbsp; <br>
<img src ="http://www.cppblog.com/sunraiing9/aggbug/31158.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2007-08-29 15:18 <a href="http://www.cppblog.com/sunraiing9/archive/2007/08/29/31158.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线程安全</title><link>http://www.cppblog.com/sunraiing9/archive/2007/08/15/30046.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Wed, 15 Aug 2007 00:09:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/08/15/30046.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/30046.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/08/15/30046.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/30046.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/30046.html</trackback:ping><description><![CDATA[MFC对象不要跨线程使用，因为MFC不是线程安全的。比如CWnd对象不要跨线程使用,可以用窗口句柄（HWND）代替。CSocket/CAsyncSocket对象不要跨线程使用,用SOCKET句柄代替.那么到底什么是线程安全呢?什么时候需要考虑?如果程序涉及到多线程的话，就应该考虑线程安全问题。比如说设计的接口，将来需要在多线程环境中使用，或者需要跨线程使用某个对象时，这个就必须考虑了。关于线程安全也没什么权威定义。在这里我只说说我的理解：所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
<p>&nbsp;&nbsp;&nbsp; 一般而言&#8220;线程安全&#8221;由多线程对共享资源的访问引起。如果调用某个接口时需要我们自己采取同步措施来保护该接口访问的共享资源,则这样的接口不是线程安全的.MFC和STL都不是线程安全的. 怎样才能设计出线程安全的类或者接口呢?如果接口中访问的数据都属于私有数据,那么这样的接口是线程安全的.或者几个接口对共享数据都是只读操作,那么这样的接口也是线程安全的.如果多个接口之间有共享数据,而且有读有写的话,如果设计者自己采取了同步措施，调用者不需要考虑数据同步问题，则这样的接口是线程安全的，否则不是线程安全的</p>
<img src ="http://www.cppblog.com/sunraiing9/aggbug/30046.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2007-08-15 08:09 <a href="http://www.cppblog.com/sunraiing9/archive/2007/08/15/30046.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>_stdcall(WINAPI) 与 _cdecl的区别</title><link>http://www.cppblog.com/sunraiing9/archive/2007/08/15/30045.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Tue, 14 Aug 2007 23:56:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/08/15/30045.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/30045.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/08/15/30045.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/30045.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/30045.html</trackback:ping><description><![CDATA[_stdcall是新标准C/C++函数的调用方法.从底层上说,使用这种调用方法参数的进栈顺序和标准C调用(_cdecl方法)是一样的,都是从右到左,但是_stdcall采用自动清栈的方式,而_cdecl是手工清栈.<br><br>windows规定,凡事有它来负责调用的函数必须定义为_stdcall类型.<br><br>比如回调函数.<br><br>如果没有显试声明的话,函数的调用方法默认是_cdecl.
<img src ="http://www.cppblog.com/sunraiing9/aggbug/30045.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sunraiing9/" target="_blank">@王一伟</a> 2007-08-15 07:56 <a href="http://www.cppblog.com/sunraiing9/archive/2007/08/15/30045.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>EXPORTER LAB</title><link>http://www.cppblog.com/sunraiing9/archive/2007/08/14/29995.html</link><dc:creator>@王一伟</dc:creator><author>@王一伟</author><pubDate>Tue, 14 Aug 2007 08:35:00 GMT</pubDate><guid>http://www.cppblog.com/sunraiing9/archive/2007/08/14/29995.html</guid><wfw:comment>http://www.cppblog.com/sunraiing9/comments/29995.html</wfw:comment><comments>http://www.cppblog.com/sunraiing9/archive/2007/08/14/29995.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sunraiing9/comments/commentRss/29995.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sunraiing9/services/trackbacks/29995.html</trackback:ping><description><![CDATA[<h2>EXPORTER LAB</h2>
<p>Overview: This lab will work through the process of creating a simple &#8220;Exporter&#8221; MAX Plug-in. The goal of the lab will be to familiarize the developer with the basics of creating a MAX plug-in, and the basics of exporting 3ds max scene data.</p>
<p>This lab is composed of 5 phases. At the beginning of each phase you will find a link to a .zip file that contains source code for the exporter that is relevant to the topic of that phase. Phase 0 provides the basic Microsoft Visual C++ v6 project skeleton. Each phase builds upon what was discussed in the previous ones, so simply replace and add the files in the source of the current phase to the previous ones.</p>
<blockquote>
<h3>Table of Contents</h3>
<h5>PHASE 0: STARTING OUT</h5>
<h5>PHASE 1 : GEOMETRY</h5>
<h5>PHASE 2 : TEXTURES AND LIGHTING</h5>
<h5>PHASE 3: ANIMATION AND MODIFIERS</h5>
<h5>PHASE 4: CUSTOM DATA</h5>
<h5>OTHER STRATEGIES, FUTURE DIRECTIONS</h5>
</blockquote>
<hr>
</hr>
<h3>Legend</h3>
<p>In order to help in identifying the various entries in this document the following legend will be applied:</p>
<p>MaxSDK Classes are in this format: IDerivedObject</p>
<p>MaxSDK Methods are in this format: DoExport()</p>
<p>Filenames are in this format: export.cpp</p>
<p>Source code that is only relevant to this tutorial are in this format: PerFaceData</p>
<hr>
</hr>
<h3>PHASE 0: STARTING OUT</h3>
<p>Source code for Phase 0: phase0.zip</p>
<p>We&#8217;ll start with the process of creating our own &#8220;skeleton&#8221; plug-in from scratch.</p>
<p>In general, when creating plug-ins, I try the following steps in the following order:
<p>
<ol>
    <li>If a skeleton for the plug-in types exists, I start with that
    <li>If not, I use the AppWizard, if the wizard supports the plug-in type
    <li>Otherwise, I do the following </li>
</ol>
<p>Create a new Win32 DLL project in MSDEV. I usually put the plug-in project under the MAXSDK directory.</p>
<p>I setup the project configs to build the plugin DLL, with the appropriate extension, into the MAXSDK\PLUGIN directory. I use relative paths and add ..\include to the include path and ..\lib to the lib path. I setup the C Runtime settings as appropriate (Debug Multithreaded DLL for Debug and Multithreaded DLL for Release), and create the Hybrid project configuration (by copying the Debug config, but setting the C Runtime to release-mode Multithreaded DLL)</p>
<p>I usually add 2 CPPs ([PLUGINNAME].CPP and DLLENTRY.CPP), the former will hold the various implementation code for the actual plugin-type derived class(es), the latter will hold the requirement C-style plugin DLL exports. Likewise, I usually add a PLUGINNAME.H that has the class definitions of the plugin-type derived class(es).</p>
<p>I add the appropriate [PLUGINNAME].DEF file, with the appropriate 3ds max DLL exports. The def file looks something like:</p>
<blockquote>
<pre>LIBRARY MyPlugIn
EXPORTS
LibDescription		@1
LibNumberClasses	@2
LibClassDesc		@3
LibVersion		@4
SECTIONS
.data READ WRITE
</pre>
</blockquote>
<p>I add a string table to the resources, along with a version resource. If the plug-in has a dialog, I add one. For this simple exporter lab, there is no additional dialog (well, there&#8217;s the about box, which doesn&#8217;t really count).</p>
<h4>Plug-in Requirements</h4>
<p>I then proceed to add the basic classes that all plug-ins need. I start with coding the DLL exports, which are more or less boilerplate, into DLLENTRY.CPP. This in turn (via LibClassDesc) leads directly to the creation of the required plug-in ClassDesc.</p>
<p>The DLL exports are fairly self-explanatory. DLLMain (not an export per se) initializes some custom controls. LibDescription will return the localized string that describes the plugin. LibNumberClasses returns the number of plug-in type classes the plug-in DLL provides (for this example, we only provide 1). LibClassDesc returns the ClassDesc for 3ds max to use to identify and create plug-in type objects. LibVersion returns the version of MAX the plug-in works with, which is always defined in the 3ds max headers as VERSION_3DSMAX.</p>
<p>LibClassDesc often just returns the address of a single static ClassDesc. For this tutorial, I derived from ClassDesc2. In general, for those familiar with this process, I recommend working with ClassDesc2 and ParamBlock2 instead of their predecessors. In general, the previous versions will be phased out in the future.</p>
<p>As discussed earlier, the ClassDesc acts mainly as a class factory for the main derived plug-in class. In this case, the main plug-in class we will derive from SceneExport. In our plugin we declare derivations of both ClassDesc2 and SceneExport. Typically, a single static instance of the derived ClassDesc is created and returned by LibClassDesc. The derivation also returns category, class ID, and name information about our derivation of SceneExport. The SDK has a tool (GENCID.EXE) that provides an easy way to create unique Class Ids.</p>
<p>This brings us to the plug-in type object derivation. We provide a derivation for SceneExport in our header (MyExporter). The basic required overrides for SceneExport are briefly explained as follows:</p>
<blockquote>int ExtCount() : Returns the number of file extensions that the exporter provides (for our example, we provide 1, which I arbitrarily decided would be &#8220;MEP&#8221;).<br>const TCHAR * Ext(int n) : Returns the 3-letter file extension string for the given extension index (0-based).<br>const TCHAR * LongDesc() : Provides the &#8220;long&#8221; description of the file export format.<br>const TCHAR * ShortDesc() : Provides the &#8220;short&#8221; description of the file export format, shown in the file export dialog drop-down.<br>const TCHAR * AuthorName() : Provides the string that describes the author and/or company of the exporter plugin.<br>const TCHAR * CopyrightMessage() : Provides the string that shows the copyright for the given exporter and/or exporter format.<br>const TCHAR * OtherMessage1() : not used<br>const TCHAR * OtherMessage2() : not used<br>unsigned int Version() : Somewhat redundant, but returns a version number for the exporter.<br>void ShowAbout() : Shows an about box, accessible by the user from the main MAX export dialog.<br>int DoExport() : The actual export method. BOOL SupportsOptions() : Returns TRUE if the exporter supports some custom export options. Currently, only one option (export selected versus export entire scene) exists.<br></blockquote>
<p>Most of the methods are basic returns of localized strings from the string table, or constants/defines. The real &#8220;meat&#8221; of an exporter plugin is the DoExport method, which we&#8217;ll expand in the next sections. This example is simple and therefore has no custom exporter options. You may want to take a look at the exporter samples, and the skeleton exporter, for a simple way to provide a special &#8220;before export&#8221; dialog that has exporter-specific options.</p>
<p>I added some protected methods (DoHeader, DoNodes, DoTailer) in my basic plan of exporting scene-global information, followed by per-node information, followed by any &#8220;end delimiter&#8221; file information (if nececssary). I also added some cached pointer member variables, and a cached FILE pointer to open/write to during the actual export.</p>
<blockquote>
<ul>
    <li>Note: Some of the above can be read about in more detail in a section of the SDK helpfile titled &#8220;Creating a New Plug-In Project&#8221;.
    <li>Note 2: You may note that in the SDK samples, many different styles of code organization are used. Feel free to use the style you feel most comfortable with. </li>
</ul>
</blockquote>
<blockquote>
<h4>Try it</h4>
<ol>
    <li>Create a sub-directory off your MAXSDK directory called &#8220;Exporter&#8221;
    <li>Un-zip / Copy Phase0 project/source files into Exporter directory
    <li>Start MSDEV, and open Exporter project
    <li>Examine the Project and Build settings. Notice the extra Hybrid Build configuration. Notice the relative lib paths and the added MAX libraries, the output destination, and the C-Runtime type (Multithreaded DLL or Debug Multithreaded DLL)
    <li>Examine the project files. First, look at the EXPORTER.DEF file and notice the four DLL exports
    <li>Look at the DLLENTRY.CPP file, and examine each of the DLL exports closely, including the DLLMain. This CPP is complete, and we will not need to modify it for the remainder of the session
    <li>Look at EXPORTER.H. Notice the #define MYEXP_CLASSID , and the MyExporter declaration.
    <li>Look at EXPORTER.CPP. Notice the MyExporterClassDesc declaration and implementation. Notice the (mostly empty at this point) implementation of MyExporter.
    <li>Examine the Project resources, which at this point include a generic About Box Dialog resource, a generic Version resource, and a String Table resource.
    <li>If you have the compiler and 3ds max handy, feel free to go ahead and compile the plugin (suggested you use Hybrid Build Configuration at this point) and try using it. In 3ds max, you can either doa File-&gt;Export orFile-&gt;Export Selected (if there are objects selected), and get the File Export Dialog. In that dialog, if the plug-in is properly loaded, you should find the &#8220;*.MEP&#8221; MyExporter format. Naturally, at this point, if you export to this format, you&#8217;ll get an empty file. </li>
</ol>
</blockquote><!-- end of phase 0 ********************************************************************** -->
<hr>
</hr>
<h3>PHASE 1 : GEOMETRY</h3>
<p>Source code for Phase 1: phase1.zip</a></p>
<p>For the first actual 3ds max scene contents we export, we&#8217;ll concentrate on just the geometry of geometric scene objects.</p>
<p>To facilitate working with nodes in the 3ds max scene, we first need to create a node enumerator method that walks over and processes the nodes in the MAX scene. There are various ways to do this: provide our own method that walks the scene manually, or utilize IScene and TreeEnum callbacks via ExpInterface. For this example, we&#8217;ll code our own enumerator from scratch.</p>
<p>For some brief backgrounder information on 3ds max's Scene hierarchy, the basic layout is of a scene tree, with a single &#8220;root&#8221; Node, whose children are all the nodes in the scene. If a node has a heirarchy (via a 3ds max &#8220;link&#8221;, or perhaps based on the layout of the object, e.g. bones), this will be represented in the &#8220;tree&#8221; as well. The Schematic view gives a pretty good visual idea of what this more or less looks like.</p>
<p>The Root node is somewhat special, and can be accessed via the Interface::GetRootNode utility method. The Root node doesn&#8217;t represent any actual geometry or the like.</p>
<p>For our node walker, the basic pseudo-code looks something like:</p>
<blockquote>
<pre>nodeEnum(Node ptr)
If the ptr isn&#8217;t valid, exit
If the ptr isn&#8217;t selected, and we&#8217;re exporting only selected nodes, exit
If the user canceled (usually via ESC), exit
Otherwise
Evaluate the object, determine what type of object it is, and call
a specific helper method that exports that type of object
For each child of the current ptr Node
nodeEnum(child)
</pre>
</blockquote>
<p>The code that will call the &#8220;nodeEnum&#8221; will be in MyExporter::DoNodes, and look something like:</p>
<blockquote>
<pre>Get a count of the children of the Root Node
For each child of the Root Node
Check for cancel and break if cancelled
nodeEnum(child)
</pre>
</blockquote>
<blockquote>
<h4>Try it</h4>
<ol>
    <li>Based on the above pseudo-code, write the given portions of code. You&#8217;ll need to look at INode ( ::Selected(), ::NumberOfChildren(), ::GetChildNode(), ::EvalWorldState()), Interface ( ::GetRootNode(), ::GetCancel()) and possibly ObjectState and Object (::SuperClassID()) in the SDK docs
    <li>Compare your code with the code snippet from Phase 1 exporter-nodeenum.cpp </li>
</ol>
</blockquote>
<p>ObjectState and Node::EvalWorldState are probably worth a bit more explanation. As you may already know, the 3ds max geometry pipeline maintains a &#8220;Object&#8221; that flows up the pipeline, and can essentially be represented by different, animated states, starting with a &#8220;Base&#8221; object, then possibly into a &#8220;Derived&#8221; object with a modifier, and so on.</p>
<p>EvalWorldState tells the node to take it&#8217;s Object &#8220;stack&#8221; and effectively make a collapsed copy, such that the resulting ObjectState contains the final end-result of the stack. This is also what is effectively used at Render time.</p>
<blockquote>
<ul>
    <li>Note: I also added some code to MyExporter::DoHeader that exports some basic header file information (date/time created, title string, etc). Feel free to examine. </li>
</ul>
</blockquote>
<h4>Meshes</h4>
<p>3ds max Meshes are triangle polygonal meshes. Every geometric (renderable) object in a 3ds max scene must be able to convert itself to a triangle mesh. Anything in the scene collapsed to a editable mesh will already be in tri-mesh form, however primitives (modified or not) may not be. To get the triangle mesh, we use the Object::ConvertToType API and request a TRIOBJ_CLASS_ID.</p>
<p>When calling ConvertToType, it&#8217;s important to check the returned result pointer, and see if it&#8217;s the same as the original Object pointer. If so, this implies the Object didn&#8217;t generate any new stuff, and thus we shouldn&#8217;t delete the result. Otherwise, the Object did generate a new mesh, and we, the caller, are responsible for freeing the mesh.</p>
<p>Once we have a mesh, we extract and dump out the number of vertices and number of faces. This information is available as APIs off the Mesh class.</p>
<p>As the mesh data is in Object Space, we need to get the Node Transform Matrix (for the current frame). We export this first in generic row-major format.</p>
<p>We then export the world-space point of each mesh vertex, multiplying the vertices by the object TM.</p>
<p>We then export the faces, described by three indices into the vertex array. The vertices, taken in counter-clockwise order, describe the &#8220;front&#8221; (positive normal) of the face. We also export the &#8220;edge visibility&#8221;, which describes, for a given edgeA-&gt;B, if 3ds max draws a highlight line in the viewport or not, and also if in edit-mesh-edge mode, if the edge can be selected. Finally, we export the smoothing group for each face, which could be used with the vertex normals (which we don&#8217;t export for now, left as an exercise) to smooth the mesh via Phong or other shading method.</p>
<p>For this phase, we do not export any mesh material or UV information. This will be done in Phase 2. </p>
<blockquote>
<h4>Try it</h4>
<ol>
    <li>In the last part, we had nodeEnum call a utility method to export GeomObjects (in the code sample this is MyExporter::ExportGeomObject). Try implementing ExportGeomObject to export the Node Transform Matrix, and if the Node can convert itself to a TriObject, try exporting the Mesh information. You&#8217;ll need to look at Inode::GetNodeTM() and the Matrix3/Point3 classes in the SDK docs for getting the matrix information. You&#8217;ll need to look at the Object::ConvertToType() and TriObject and Mesh class in the docs for exporting the mesh information.
    <li>Compare the code with the code fragment exporter-geom.cpp. The code fragment has both the mesh and the patch (next) exporter code &#8211; the mesh exporter code is the second &#8220;chunk&#8221; in ExportGeomObject. </li>
</ol>
</blockquote>
<h4>Patches</h4>
<p>For patches, we provide an additional code path in ExportGeomObject that looks for PATCHOBJ_CLASS_ID and exports the patch i