﻿<?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++博客-Tommy的技术博客-文章分类-C++</title><link>http://www.cppblog.com/tommyyan/category/1335.html</link><description>&lt;br&gt;C++/web技术/设计模式/LINUX/MYSQL/P2P交流/嵌入式系统&lt;br&gt;
</description><language>zh-cn</language><lastBuildDate>Fri, 31 Dec 2010 12:54:09 GMT</lastBuildDate><pubDate>Fri, 31 Dec 2010 12:54:09 GMT</pubDate><ttl>60</ttl><item><title>cdecl、stdcall、fastcall函数调用约定区别</title><link>http://www.cppblog.com/tommyyan/articles/137307.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Thu, 23 Dec 2010 09:10:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/137307.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/137307.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/137307.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/137307.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/137307.html</trackback:ping><description><![CDATA[<h2><strong>论函数调用约定</strong></h2>
</td>
</tr>
<tr>
    <td width="100%">
    <p>　　在C语言中，假设我们有这样的一个函数：<br>　　<br>　　int function(int a,int b)<br>　　<br>　　调用时只要用result = function(1,2)这样的方式就可以使用这个函数。但是，当高级语言被编译成计算机可以识别的机器码时，有一个问题就凸现出来：在CPU中，计算机没有办法知道一个函数调用需要多少个、什么样的参数，也没有硬件可以保存这些参数。也就是说，计算机不知道怎么给这个函数传递参数，传递参数的工作必须由函数调用者和函数本身来协调。为此，计算机提供了一种被称为栈的数据结构来支持参数传递。</p>
    <p>　　栈是一种先进后出的数据结构，栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项（被称为栈顶）。用户可以在栈顶上方向栈中加入数据，这个操作被称为压栈(Push)，压栈以后，栈顶自动变成新加入数据项的位置，栈顶指针也随之修改。用户也可以从堆栈中取走栈顶，称为弹出栈(pop)，弹出栈后，栈顶下的一个元素变成栈顶，栈顶指针随之修改。</p>
    <p>　　函数调用时，调用者依次把参数压栈，然后调用函数，函数被调用以后，在堆栈中取得数据，并进行计算。函数计算结束以后，或者调用者、或者函数本身修改堆栈，使堆栈恢复原装。</p>
    <p>　　在参数传递中，有两个很重要的问题必须得到明确说明：<br>　　<br>　　当参数个数多于一个时，按照什么顺序把参数压入堆栈 <br>　　函数调用后，由谁来把堆栈恢复原装 <br>　　在高级语言中，通过函数调用约定来说明这两个问题。常见的调用约定有： </p>
    <p>　　stdcall <br>　　cdecl <br>　　fastcall <br>　　thiscall <br>　　naked call</p>
    <p>&#160;</p>
    <p>　　stdcall调用约定<br>　　stdcall很多时候被称为pascal调用约定，因为pascal是早期很常见的一种教学用计算机程序设计语言，其语法严谨，使用的函数调用约定就是stdcall。在Microsoft C++系列的C/C++编译器中，常常用PASCAL宏来声明这个调用约定，类似的宏还有WINAPI和CALLBACK。</p>
    <p>　　stdcall调用约定声明的语法为(以前文的那个函数为例）：<br>　　<br>　　int __stdcall function(int a,int b)<br>　　<br>　　stdcall的调用约定意味着：1）参数从右向左压入堆栈，2）函数自身修改堆栈 3)函数名自动加前导的下划线，后面紧跟一个@符号，其后紧跟着参数的尺寸</p>
    <p>　　以上述这个函数为例，参数b首先被压栈，然后是参数a，函数调用function(1,2)调用处翻译成汇编语言将变成：</p>
    <p>　　push 2　　　　　　　 第二个参数入栈<br>　　push 1　　　　　　　 第一个参数入栈<br>　　call function　　　　调用参数，注意此时自动把cs:eip入栈</p>
    <p>　　而对于函数自身，则可以翻译为： <br>　　push ebp　　　　　　 保存ebp寄存器，该寄存器将用来保存堆栈的栈顶指针，可以在函数退出时恢复<br>　　mov　ebp, esp　　　　保存堆栈指针<br>　　mov　eax,[ebp + 8H]　堆栈中ebp指向位置之前依次保存有ebp, cs:eip, a, b, ebp +8指向a<br>　　add　eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b<br>　　mov　esp, ebp　　　　恢复esp<br>　　pop　ebp<br>　　ret　8</p>
    <p>　　而在编译时，这个函数的名字被翻译成<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#95;&#102;&#117;&#110;&#99;&#116;&#105;&#111;&#110;&#64;&#56;"><font color=#336699><u>_function@8</u></font></a> </p>
    <p>　　注意不同编译器会插入自己的汇编代码以提供编译的通用性，但是大体代码如此。其中在函数开始处保留esp到ebp中，在函数结束恢复是编译器常用的方法。</p>
    <p>　　从函数调用看，2和1依次被push进堆栈，而在函数中又通过相对于ebp(即刚进函数时的堆栈指针）的偏移量存取参数。函数结束后，ret 8表示清理8个字节的堆栈，函数自己恢复了堆栈。</p>
    <p>　　<br>　　cdecl调用约定<br>　　cdecl调用约定又称为C调用约定，是C语言缺省的调用约定，它的定义语法是：</p>
    <p>　　int function (int a ,int b)　//不加修饰就是C调用约定<br>　　int __cdecl function(int a,int b)//明确指出C调用约定</p>
    <p>　　在写本文时，出乎我的意料，发现cdecl调用约定的参数压栈顺序是和stdcall是一样的，参数首先由右向左压入堆栈。所不同的是，函数本身不清理堆栈，调用者负责清理堆栈。由于这种变化，C调用约定允许函数的参数的个数是不固定的，这也是C语言的一大特色。对于前面的function函数，使用 cdecl后的汇编码变成： </p>
    <p>　　调用处<br>　　push 1<br>　　push 2<br>　　call function<br>　　add　esp, 8　　　　　注意：这里调用者在恢复堆栈</p>
    <p>　　被调用函数_function处<br>　　push ebp　　　　　　 保存ebp寄存器，该寄存器将用来保存堆栈的栈顶指针，可以在函数退出时恢复<br>　　mov　ebp,esp　　　　 保存堆栈指针<br>　　mov　eax,[ebp + 8H]　堆栈中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a<br>　　add　eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b<br>　　mov　esp,ebp　　　　 恢复esp<br>　　pop　ebp<br>　　ret　　　　　　　　　注意，这里没有修改堆栈</p>
    <p>　　MSDN中说，该修饰自动在函数名前加前导的下划线，因此函数名在符号表中被记录为_function，但是我在编译时似乎没有看到这种变化。</p>
    <p>　　由于参数按照从右向左顺序压栈，因此最开始的参数在最接近栈顶的位置，因此当采用不定个数参数时，第一个参数在栈中的位置肯定能知道，只要不定的参数个数能够根据第一个后者后续的明确的参数确定下来，就可以使用不定参数，例如对于CRT中的sprintf函数，定义为： <br>　　int sprintf(char* buffer,const char* format,...)<br>　　由于所有的不定参数都可以通过format确定，因此使用不定个数的参数是没有问题的。</p>
    <p>　　fastcall<br>　　fastcall调用约定和stdcall类似，它意味着： <br>　　<br>　　函数的第一个和第二个DWORD参数（或者尺寸更小的）通过ecx和edx传递，其他参数通过从右向左的顺序压栈 <br>　　被调用函数清理堆栈 <br>　　函数名修改规则同stdcall <br>　　其声明语法为：int fastcall function(int a, int b)</p>
    <p>　　thiscall<br>　　thiscall是唯一一个不能明确指明的函数修饰，因为thiscall不是关键字。它是C++类成员函数缺省的调用约定。由于成员函数调用还有一个this指针，因此必须特殊处理，thiscall意味着： </p>
    <p>　　参数从右向左入栈 <br>　　如果参数个数确定，this指针通过ecx传递给被调用者；如果参数个数不确定，this指针在所有参数压栈后被压入堆栈。对参数个数不定的，调用者清理堆栈，否则函数自己清理堆栈为了说明这个调用约定，定义如下类和使用代码： </p>
    <p>　　class A<br>　　{<br>　　public:<br>　　　 int function1(int a,int b);<br>　　　 int function2(int a,...);<br>　　};</p>
    <p>　　int A::function1 (int a,int b)<br>　　{<br>　　　 return a+b;<br>　　}</p>
    <p>　　#include &lt;stdarg.h&gt;<br>　　int A::function2(int a,...)<br>　　{<br>　　　 va_list ap;<br>　　　 va_start(ap,a);<br>　　　 int i;<br>　　　 int result = 0;<br>　　　 for(i = 0 ; i &lt; a ; i ++)<br>　　　 {<br>　　　　　result += va_arg(ap,int);<br>　　　 }<br>　　　 return result;<br>　　}</p>
    <p>　　void callee()<br>　　{<br>　　　 A a;<br>　　　 a.function1(1, 2);<br>　　　 a.function2(3, 1, 2, 3);<br>　　}</p>
    <p>callee函数被翻译成汇编后就变成： <br>　　//函数function1调用<br>　　00401C1D　 push　　　　2<br>　　00401C1F　 push　　　　1<br>　　00401C21　 lea　　　　 ecx,[ebp-8]<br>　　00401C24　 call　　　　function1　　　　　注意，这里this没有被入栈</p>
    <p>　　//函数function2调用<br>　　00401C29　 push　　　　3<br>　　00401C2B　 push　　　　2<br>　　00401C2D　 push　　　　1<br>　　00401C2F　 push　　　　3<br>　　00401C31　 lea　　　　 eax, [ebp-8]　　　 这里引入this指针<br>　　00401C34　 push　　　　eax<br>　　00401C35　 call　　　　function2<br>　　00401C3A　 add　　　　 esp, 14h<br>　　<br>　　可见，对于参数个数固定情况下，它类似于stdcall，不定时则类似cdecl</p>
    <p>　　naked call<br>　　这是一个很少见的调用约定，一般程序设计者建议不要使用。编译器不会给这种函数增加初始化和清理代码，更特殊的是，你不能用return返回返回值，只能用插入汇编返回结果。这一般用于实模式驱动程序设计，假设定义一个求和的加法程序，可以定义为： </p>
    <p>　　__declspec(naked) int　add(int a,int b)<br>　　{<br>　　　 __asm mov eax,a<br>　　　 __asm add eax,b<br>　　　 __asm ret <br>　　}</p>
    <p>　　注意，这个函数没有显式的return返回值，返回通过修改eax寄存器实现，而且连退出函数的ret指令都必须显式插入。上面代码被翻译成汇编以后变成： </p>
    <p>　　mov eax,[ebp+8]<br>　　add eax,[ebp+12]<br>　　ret 8</p>
    <p>　 注意这个修饰是和__stdcall及cdecl结合使用的，前面是它和cdecl结合使用的代码，对于和stdcall结合的代码，则变成： </p>
    <p>　　__declspec(naked) int __stdcall function(int a,int b)<br>　 {<br>　　　　__asm mov eax,a<br>　　　　__asm add eax,b<br>　　　　__asm ret 8　　　　//注意后面的8<br>　　}</p>
    <p>　　至于这种函数被调用，则和普通的cdecl及stdcall调用函数一致。</p>
    <p>　　函数调用约定导致的常见问题<br>　　如果定义的约定和使用的约定不一致，则将导致堆栈被破坏，导致严重问题，下面是两种常见的问题： </p>
    <p>　　函数原型声明和函数体定义不一致 <br>　　DLL导入函数时声明了不同的函数约定 <br>　　以后者为例，假设我们在dll种声明了一种函数为： </p>
    <p>　　__declspec(dllexport) int func(int a,int b);//注意，这里没有stdcall，使用的是cdecl<br>　　使用时代码为： </p>
    <p>　　typedef int (*WINAPI DLLFUNC)func(int a,int b);<br>　　hLib = LoadLibrary(...);</p>
    <p>　　DLLFUNC func = (DLLFUNC)GetProcAddress(...)//这里修改了调用约定<br>　　result = func(1,2);//导致错误</p>
    <p>　　由于调用者没有理解WINAPI的含义错误的增加了这个修饰，上述代码必然导致堆栈被破坏，MFC在编译时插入的checkesp函数将告诉你，堆栈被破坏</p>
    </td>
</tr>
<img src ="http://www.cppblog.com/tommyyan/aggbug/137307.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2010-12-23 17:10 <a href="http://www.cppblog.com/tommyyan/articles/137307.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows下 POSIX 线程编程</title><link>http://www.cppblog.com/tommyyan/articles/98587.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Wed, 14 Oct 2009 07:52:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/98587.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/98587.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/98587.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/98587.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/98587.html</trackback:ping><description><![CDATA[<h3><strong>1. POSIX 标准</strong></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POSIX是Portable Operating System Interface of Unix的缩写。由IEEE（Institute of Electrical and Electronic Engineering）开发，由ANSI和ISO标准化。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POSIX的诞生和Unix的发展是密不可分的，Unix于70年代诞生于Bell lab，并于80年代向美各大高校分发V7版的源码以做研究。UC Berkeley在V7的基础上开发了BSD Unix。后来很多商业厂家意识到Unix的价值也纷纷以Bell Lab的System V或BSD为基础来开发自己的Unix，较著名的有Sun OS，AIX，VMS。由于各厂家对Unix的开发各自为政，造成了Unix的版本相当混乱，给软件的可移植性带来很大困难，对Unix的发展极为不利。为结束这种局面，IEEE开发了POSIX，POSIX在源代码级别上定义了一组最小的Unix(类Unix)操作系统接口。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POSIX 表示可移植操作系统接口（Portable Operating System Interface ，缩写为 POSIX 是为了读音更像 UNIX）。电气和电子工程师协会（Institute of Electrical and Electronics Engineers，IEEE）最初开发 POSIX 标准，是为了提高 UNIX 环境下应用程序的可移植性。然而，POSIX 并不局限于 UNIX。许多其它的操作系统，例如 DEC OpenVMS 和 Microsoft Windows NT，都支持 POSIX 标准，尤其是 IEEE Std. 1003.1-1990（1995 年修订）或 POSIX.1，POSIX.1 提供了源代码级别的 C 语言应用编程接口（API）给操作系统的服务程序，例如读写文件。POSIX.1 已经被国际标准化组织（International Standards Organization，ISO）所接受，被命名为 ISO/IEC 9945-1:1990 标准。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POSIX 现在已经发展成为一个非常庞大的标准族，某些部分正处在开发过程中。表 1-1 给出了 POSIX 标准的几个重要组成部分。POSIX 与 IEEE 1003 和 2003 家族的标准是可互换的。除 1003.1 之外，1003 和 2003 家族也包括在表中。 </p>
<p><strong>表1：标准的重要组成部分</strong><br>1003.0 <br>管理 POSIX 开放式系统环境（OSE）。IEEE 在 1995 年通过了这项标准。 ISO 的版本是 ISO/IEC 14252:1996。 <br>1003.1 <br>被广泛接受、用于源代码级别的可移植性标准。1003.1 提供一个操作系统的 C 语言应用编程接口（API）。IEEE 和 ISO 已经在 1990 年通过了这个标准，IEEE 在 1995 年重新修订了该标准。 <br>1003.1b <br>一个用于实时编程的标准（以前的 P1003.4 或 POSIX.4）。这个标准在 1993 年被 IEEE 通过，被合并进 ISO/IEC 9945-1。 <br>1003.1c <br>一个用于线程（在一个程序中当前被执行的代码段）的标准。以前是 P1993.4 或 POSIX.4 的一部分，这个标准已经在 1995 年被 IEEE 通过，归入 ISO/IEC 9945-1:1996。 <br>1003.1g <br>一个关于协议独立接口的标准，该接口可以使一个应用程序通过网络与另一个应用程序通讯。 1996 年，IEEE 通过了这个标准。 <br>1003.2 <br>一个应用于 shell 和 工具软件的标准，它们分别是操作系统所必须提供的命令处理器和工具程序。 1992 年 IEEE 通过了这个标准。ISO 也已经通过了这个标准（ISO/IEC 9945-2:1993）。 <br>1003.2d <br>改进的 1003.2 标准。 <br>1003.5 <br>一个相当于 1003.1 的 Ada 语言的 API。在 1992 年，IEEE 通过了这个标准。并在 1997 年对其进行了修订。ISO 也通过了该标准。 <br>1003.5b <br>一个相当于 1003.1b（实时扩展）的 Ada 语言的 API。IEEE 和 ISO 都已经通过了这个标准。ISO 的标准是 ISO/IEC 14519:1999。 <br>1003.5c <br>一个相当于 1003.1q（协议独立接口）的 Ada 语言的 API。在 1998 年， IEEE 通过了这个标准。ISO 也通过了这个标准。 <br>1003.9 <br>一个相当于 1003.1 的 FORTRAN 语言的 API。在 1992 年，IEEE 通过了这个标准，并于 1997 年对其再次确认。ISO 也已经通过了这个标准。 <br>1003.10 <br>一个应用于超级计算应用环境框架（Application Environment Profile，AEP）的标准。在 1995 年，IEEE 通过了这个标准。 <br>1003.13 <br>一个关于应用环境框架的标准，主要针对使用 POSIX 接口的实时应用程序。在 1998 年，IEEE 通过了这个标准。 <br>1003.22 <br>一个针对 POSIX 的关于安全性框架的指南。 <br>1003.23 <br>一个针对用户组织的指南，主要是为了指导用户开发和使用支持操作需求的开放式系统环境（OSE）框架 <br>2003 <br>针对指定和使用是否符合 POSIX 标准的测试方法，有关其定义、一般需求和指导方针的一个标准。在 1997 年，IEEE 通过了这个标准。 <br>2003.1 <br>这个标准规定了针对 1003.1 的 POSIX 测试方法的提供商要提供的一些条件。在 1992 年，IEEE 通过了这个标准。 <br>2003.2 <br>一个定义了被用来检查与 IEEE 1003.2（shell 和 工具 API）是否符合的测试方法的标准。在 1996 年，IEEE 通过了这个标准。 </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 除了 1003 和 2003 家族以外，还有几个其它的 IEEE 标准，例如 1224 和 1228，它们也提供开发可移植应用程序的 API。要想得到关于 IEEE 标准的最新信息，可以访问 IEEE 标准的主页，网址是 <a href="http://standards.ieee.org/"><u><font color=#0000ff>http://standards.ieee.org/</font></u></a>。有关 POSIX 标准的概述信息，请访问 Web 站点 <a href="http://standards.ieee.org/reading/ieee/stad_public/description/posix/"><u><font color=#0000ff>http://standards.ieee.org/reading/ieee/stad_public/description/posix/</font></u></a>。</p>
<h3><strong>2. Liniux下的线程编程</strong></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Linux系统下的多线程遵循POSIX线程接口，称为pthread。从上面的描述不难知道，<strong>POSIX线程接口是POSIX众多标准中的一个（<a href="http://www.unix-systems.org/version3/ieee_std.html"><u><font color=#0000ff>POSIX 1003.1-2001</font></u></a>）</strong>。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编写Linux下的多线程程序，需要使用头文件pthread.h，连接时需要使用库libpthread.a。顺便说一下，Linux下pthread 的实现是通过系统调用 clone() 来实现的。clone() 是Linux所特有的系统调用，它的使用方式类似fork，关于 clone() 的详细情况，有兴趣的读者可以去查看有关文档说明。</p>
<p>下面是一个 POSIX 线程的简单示例程序(thread1.c)： </p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230); PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: border; moz-background-origin: padding; moz-background-inline-policy: continuous">
<div><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top><span style="COLOR: rgb(0,0,0)">#include&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">pthread.h</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top>#include&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">stdlib.h</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top>#include&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">unistd.h</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><img id=_95_198_Open_Image onclick="this.style.display='none'; document.getElementById('_95_198_Open_Text').style.display='none'; document.getElementById('_95_198_Closed_Image').style.display='inline'; document.getElementById('_95_198_Closed_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056210.gif" align=top><img id=_95_198_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_95_198_Closed_Text').style.display='none'; document.getElementById('_95_198_Open_Image').style.display='inline'; document.getElementById('_95_198_Open_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056233.gif" align=top>&nbsp;</span><span style="COLOR: rgb(0,0,255)">void</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">*</span><span style="COLOR: rgb(0,0,0)">thread_function(</span><span style="COLOR: rgb(0,0,255)">void</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">*</span><span style="COLOR: rgb(0,0,0)">arg)&nbsp;</span><span id=_95_198_Closed_Text style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">...</span><span id=_95_198_Open_Text><span style="COLOR: rgb(0,0,0)">{<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)">&nbsp;i;<br><img id=_130_181_Open_Image onclick="this.style.display='none'; document.getElementById('_130_181_Open_Text').style.display='none'; document.getElementById('_130_181_Closed_Image').style.display='inline'; document.getElementById('_130_181_Closed_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056227.gif" align=top><img id=_130_181_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_130_181_Closed_Text').style.display='none'; document.getElementById('_130_181_Open_Image').style.display='inline'; document.getElementById('_130_181_Open_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056512.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">for</span><span style="COLOR: rgb(0,0,0)">&nbsp;(&nbsp;i</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">0</span><span style="COLOR: rgb(0,0,0)">;&nbsp;i</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">20</span><span style="COLOR: rgb(0,0,0)">;&nbsp;i</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">)&nbsp;</span><span id=_130_181_Closed_Text style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">...</span><span id=_130_181_Open_Text><span style="COLOR: rgb(0,0,0)">{<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">Thread&nbsp;says&nbsp;hi! </span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;sleep(</span><span style="COLOR: rgb(0,0,0)">1</span><span style="COLOR: rgb(0,0,0)">);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056186.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)">&nbsp;NULL;<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056497.gif" align=top>}</span></span><span style="COLOR: rgb(0,0,0)"><br><img id=_215_472_Open_Image onclick="this.style.display='none'; document.getElementById('_215_472_Open_Text').style.display='none'; document.getElementById('_215_472_Closed_Image').style.display='inline'; document.getElementById('_215_472_Closed_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056210.gif" align=top><img id=_215_472_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_215_472_Closed_Text').style.display='none'; document.getElementById('_215_472_Open_Image').style.display='inline'; document.getElementById('_215_472_Open_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056233.gif" align=top></span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)">&nbsp;main(</span><span style="COLOR: rgb(0,0,255)">void</span><span style="COLOR: rgb(0,0,0)">)&nbsp;</span><span id=_215_472_Closed_Text style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">...</span><span id=_215_472_Open_Text><span style="COLOR: rgb(0,0,0)">{<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;pthread_t&nbsp;mythread;<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;<br><img id=_307_362_Open_Image onclick="this.style.display='none'; document.getElementById('_307_362_Open_Text').style.display='none'; document.getElementById('_307_362_Closed_Image').style.display='inline'; document.getElementById('_307_362_Closed_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056227.gif" align=top><img id=_307_362_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_307_362_Closed_Text').style.display='none'; document.getElementById('_307_362_Open_Image').style.display='inline'; document.getElementById('_307_362_Open_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056512.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">if</span><span style="COLOR: rgb(0,0,0)">&nbsp;(&nbsp;pthread_create(&nbsp;</span><span style="COLOR: rgb(0,0,0)">&amp;</span><span style="COLOR: rgb(0,0,0)">mythread,&nbsp;NULL,&nbsp;thread_function,&nbsp;NULL)&nbsp;)&nbsp;</span><span id=_307_362_Closed_Text style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">...</span><span id=_307_362_Open_Text><span style="COLOR: rgb(0,0,0)">{<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">error&nbsp;creating&nbsp;thread.</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;abort();<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056186.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: rgb(0,0,0)"><br><img id=_405_459_Open_Image onclick="this.style.display='none'; document.getElementById('_405_459_Open_Text').style.display='none'; document.getElementById('_405_459_Closed_Image').style.display='inline'; document.getElementById('_405_459_Closed_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056227.gif" align=top><img id=_405_459_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_405_459_Closed_Text').style.display='none'; document.getElementById('_405_459_Open_Image').style.display='inline'; document.getElementById('_405_459_Open_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056512.gif" align=top>&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">if</span><span style="COLOR: rgb(0,0,0)">&nbsp;(&nbsp;pthread_join&nbsp;(&nbsp;mythread,&nbsp;NULL&nbsp;)&nbsp;)&nbsp;</span><span id=_405_459_Closed_Text style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">...</span><span id=_405_459_Open_Text><span style="COLOR: rgb(0,0,0)">{<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">error&nbsp;joining&nbsp;thread.</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;abort();<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056186.gif" align=top>&nbsp;&nbsp;}</span></span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;exit(</span><span style="COLOR: rgb(0,0,0)">0</span><span style="COLOR: rgb(0,0,0)">);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056497.gif" align=top>}</span></span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top></span></div>
</div>
<p>&nbsp;要编译这个程序，只需先将程序存为 thread1.c，然后输入：</p>
<div>$ gcc thread1.c -o thread1 -lpthread</div>
<p>运行则输入：</p>
<div>$ ./thread1</div>
<h3><strong>3. Windows下POSIX线程编程</strong></h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Windows本身没有提供对POSIX的支持。但有一个叫 <a href="http://sources.redhat.com/pthreads-win32/"><u><font color=#0000ff>POSIX Threads for Win32</font></u></a> 的开源项目给出了一个功能比较完善的Windows下pthreads API的实现。目前的最新版本是Pthreads-w32 release 2.8.0 (2006-12-22)。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我没有测试过这个最新版本，这里只给出2.7.0版的链接：<a title=ftp://sources.redhat.com/pub/pthreads-win32/pthreads-w32-2-7-0-release.exe href="ftp://sources.redhat.com/pub/pthreads-win32/pthreads-w32-2-7-0-release.exe"><u><font color=#0000ff>ftp://sources.redhat.com/pub/pthreads-win32/pthreads-w32-2-7-0-release.exe</font></u></a>。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 关于该开源项目的详细介绍见：<a title=http://sources.redhat.com/pthreads-win32/ href="http://sources.redhat.com/pthreads-win32/"><u><font color=#0000ff>http://sources.redhat.com/pthreads-win32/</font></u></a>。</p>
<h4><strong>3.1 简单使用</strong></h4>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上面的exe文件是一个自解压文件，解压后，Pre-built.2目录中有编译所需要的头文件（include子目录）和库文件（lib子目录）。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个简单的测试程序(main.cpp)：</p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230); PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: border; moz-background-origin: padding; moz-background-inline-policy: continuous">
<div><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top><span style="COLOR: rgb(0,0,0)">#include&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">stdio.h</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top>#include&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">pthread.h</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top>#include&nbsp;</span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">assert.h</span><span style="COLOR: rgb(0,0,0)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top></span><span style="COLOR: rgb(0,0,255)">void</span><span style="COLOR: rgb(0,0,0)">*</span><span style="COLOR: rgb(0,0,0)">&nbsp;Function_t(</span><span style="COLOR: rgb(0,0,255)">void</span><span style="COLOR: rgb(0,0,0)">*</span><span style="COLOR: rgb(0,0,0)">&nbsp;Param)<br><img id=_91_214_Open_Image onclick="this.style.display='none'; document.getElementById('_91_214_Open_Text').style.display='none'; document.getElementById('_91_214_Closed_Image').style.display='inline'; document.getElementById('_91_214_Closed_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056210.gif" align=top><img id=_91_214_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_91_214_Closed_Text').style.display='none'; document.getElementById('_91_214_Open_Image').style.display='inline'; document.getElementById('_91_214_Open_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056233.gif" align=top></span><span id=_91_214_Closed_Text style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">...</span><span id=_91_214_Open_Text><span style="COLOR: rgb(0,0,0)">{<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">I&nbsp;am&nbsp;a&nbsp;thread!&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_t&nbsp;myid&nbsp;</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">&nbsp;pthread_self();<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">thread&nbsp;ID=%d&nbsp;</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">,&nbsp;myid);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)">&nbsp;NULL;<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056497.gif" align=top>}</span></span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top></span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)">&nbsp;main()<br><img id=_228_596_Open_Image onclick="this.style.display='none'; document.getElementById('_228_596_Open_Text').style.display='none'; document.getElementById('_228_596_Closed_Image').style.display='inline'; document.getElementById('_228_596_Closed_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056210.gif" align=top><img id=_228_596_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; document.getElementById('_228_596_Closed_Text').style.display='none'; document.getElementById('_228_596_Open_Image').style.display='inline'; document.getElementById('_228_596_Open_Text').style.display='inline';" alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056233.gif" align=top></span><span id=_228_596_Closed_Text style="BORDER-RIGHT: rgb(128,128,128) 1px solid; BORDER-TOP: rgb(128,128,128) 1px solid; DISPLAY: none; BORDER-LEFT: rgb(128,128,128) 1px solid; BORDER-BOTTOM: rgb(128,128,128) 1px solid; BACKGROUND-COLOR: rgb(255,255,255)">...</span><span id=_228_596_Open_Text><span style="COLOR: rgb(0,0,0)">{<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_t&nbsp;pid;<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_attr_t&nbsp;attr;<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_attr_init(</span><span style="COLOR: rgb(0,0,0)">&amp;</span><span style="COLOR: rgb(0,0,0)">attr);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_attr_setscope(</span><span style="COLOR: rgb(0,0,0)">&amp;</span><span style="COLOR: rgb(0,0,0)">attr,&nbsp;PTHREAD_SCOPE_PROCESS);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_attr_setdetachstate(</span><span style="COLOR: rgb(0,0,0)">&amp;</span><span style="COLOR: rgb(0,0,0)">attr,&nbsp;PTHREAD_CREATE_DETACHED);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_create(</span><span style="COLOR: rgb(0,0,0)">&amp;</span><span style="COLOR: rgb(0,0,0)">pid,&nbsp;</span><span style="COLOR: rgb(0,0,0)">&amp;</span><span style="COLOR: rgb(0,0,0)">attr,&nbsp;Function_t,&nbsp;NULL);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">========================================&nbsp;</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;getchar();<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;pthread_attr_destroy(</span><span style="COLOR: rgb(0,0,0)">&amp;</span><span style="COLOR: rgb(0,0,0)">attr);<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056264.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)">&nbsp;</span><span style="COLOR: rgb(0,0,0)">1</span><span style="COLOR: rgb(0,0,0)">;<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056497.gif" align=top>}</span></span><span style="COLOR: rgb(0,0,0)">&nbsp;</span></div>
</div>
<p>使用 cl.exe 编译（不熟悉 cl.exe 的请参考：<a title=http://blog.csdn.net/liuyongjin1984/archive/2008/01/07/2029405.aspx href="http://blog.csdn.net/liuyongjin1984/archive/2008/01/07/2029405.aspx"><u><font color=#0000ff>http://blog.csdn.net/liuyongjin1984/archive/2008/01/07/2029405.aspx</font></u></a> 或者参见下面3.2部分）：</p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230); PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: border; moz-background-origin: padding; moz-background-inline-policy: continuous">
<div><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top>》<span style="COLOR: rgb(0,0,0)">rem&nbsp;cl.bat<br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top>》cl.exe&nbsp;main.cpp &nbsp;</span><span style="COLOR: rgb(0,0,0)">/</span><span style="COLOR: rgb(0,0,0)">c&nbsp; </span><span style="COLOR: rgb(0,0,0)">/</span><span style="COLOR: rgb(0,0,0)">I</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">c:pthreads-w32-2-7-0-releasePre-built.2include</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)"><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top><br><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056480.gif" align=top>》link.exe&nbsp; </span><span style="COLOR: rgb(0,0,0)">/</span><span style="COLOR: rgb(0,0,255)">out</span><span style="COLOR: rgb(0,0,0)">:main_cl.exe&nbsp; main.obj&nbsp; </span><span style="COLOR: rgb(0,0,0)">/</span><span style="COLOR: rgb(0,0,0)">LIBPATH:</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">c:pthreads-w32-2-7-0-releasePre-built.2lib</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp; pthreadVC2.lib</span></div>
</div>
<p><font size=3>&nbsp;<strong>3.2 使用VC++ 6.0或Visual Studio 2005来运行上面的程序</strong></font></p>
<p>关键有两点：</p>
<p>1. 是将<strong>头文件（include子目录）和库文件（lib子目录）</strong>中的内容添加到VC++ 6.0或Visual Studio 2005开发环境对应的include和lib目录下。</p>
<p>具体来说（<strong>以添加include目录为例，添加lib目录类似</strong>）：</p>
<p><strong>图1：VC++ 6.0（添加include目录：工具--》选项--》目录）</strong></p>
<p><a href="http://p.blog.csdn.net/images/p_blog_csdn_net/liuyongjin1984/WindowsLiveWriter/WindowsPOSIX_14824/1.png"></a><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056899.bmp">&nbsp;</p>
<p>&nbsp;</p>
<p><strong>图2：Visual Studio 2005(添加include目录：tools--》options)</strong></p>
<p><a href="http://p.blog.csdn.net/images/p_blog_csdn_net/liuyongjin1984/WindowsLiveWriter/WindowsPOSIX_14824/2.png"></a><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056941.bmp">&nbsp;</p>
<p>&nbsp;</p>
<p><strong>2. 指定link时要连接的库的名称（pthreadVC2.lib）</strong></p>
<p><strong>图3：VC++ 6.0（工程--》设置--》连接）</strong></p>
<p><strong><a href="http://p.blog.csdn.net/images/p_blog_csdn_net/liuyongjin1984/WindowsLiveWriter/WindowsPOSIX_14824/3.png"></a></strong></p>
<p><strong><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056613.bmp"></strong></p>
<p><strong></strong></p>
<p><strong>图4：Visual Studio 2005(project--&gt;* property pages)</strong></p>
<p><a href="http://p.blog.csdn.net/images/p_blog_csdn_net/liuyongjin1984/WindowsLiveWriter/WindowsPOSIX_14824/4.png"></a><img alt="" src="http://bookpic.newbooks.com.cn/pic/200801/179364/20080109010056694.bmp">&nbsp;</p>
<strong></strong>
<h3>4. 书籍推荐<br><font size=3>&lt;&lt; POSIX Multithread Programming Primer &gt;&gt;:</font> <a href="http://download.csdn.net/source/237125"><u><font color=#0000ff>http://download.csdn.net/source/237125</font></u></a></h3>
<img src ="http://www.cppblog.com/tommyyan/aggbug/98587.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2009-10-14 15:52 <a href="http://www.cppblog.com/tommyyan/articles/98587.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ 仿函数(functor)</title><link>http://www.cppblog.com/tommyyan/articles/97478.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Mon, 28 Sep 2009 10:16:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/97478.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/97478.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/97478.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/97478.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/97478.html</trackback:ping><description><![CDATA[<div class=tit>C++ 仿函数(functor)</div>
<div class=date>2007-04-06 10:11</div>
<table class=FCK__ShowTableBorders style="TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div class=cnt>
            <p>　　所谓的仿函数(functor)，是通过重载()运算符模拟函数形为的类。<br>　　因此，这里需要明确两点：<br>　　1　仿函数不是函数，它是个类；<br>　　2　仿函数重载了()运算符，使得它的对你可以像函数那样子调用(代码的形式好像是在调用</p>
            <p>函数)。</p>
            <p>　　看下面的实例：</p>
            <p>#include &lt;iostream&gt;<br>using namespace std;</p>
            <p>const int CMP_LES = -1;<br>const int CMP_EQU = 0;<br>const int CMP_BIG = 1;</p>
            <p>class Comparer<br>{<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Comparer(int cmpType)<br>&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; m_cmpType = cmpType;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#000080>bool</font> <font color=#ff0000>operator</font> <font color=#ff00ff>()</font>(<font color=#000080>int</font> num1, <font color=#000080>int</font> num2) <font color=#ff0000>const</font><br>&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; bool res;</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; switch(m_cmpType)<br>&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; case CMP_LES:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; res = num1 &lt; num2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case CMP_EQU:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; res = num1 == num2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case CMP_BIG:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; res = num1 &gt; num2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; res = false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return res;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
            <p>private:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int m_cmpType;<br>};</p>
            <p>void Swap(int <font color=#ff0000>&amp;</font>num1, int <font color=#ff0000>&amp;</font>num2)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int temp = num1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; num1 = num2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; num2 = temp;<br>}</p>
            <p>void SortArray(int array[], int size, <font color=#ff0000>const</font> Comparer <font color=#ff0000>&amp;</font><font color=#800000>cmp</font>)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; size - 1; ++i)<br>&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; int indx = i;</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = i + 1; j &lt; size; ++j)<br>&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;<font color=#ff0000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (cmp(array[indx], array[j]))</font><br>&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; indx = j;<br>&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; }</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (indx != i)<br>&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; Swap(array[i], array[indx]);<br>&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; }<br>}</p>
            <p>void ListArray(int array[], int size)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; size; ++i)<br>&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; cout &lt;&lt; array[i] &lt;&lt; " ";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>}</p>
            <p>#define ARY_SIZE 10</p>
            <p>int main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int array[ARY_SIZE] = {10, 12, 9, 31, 93, 34, 98, 9, 1, 20};</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; "The initial array is : ";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ListArray(array, ARY_SIZE);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; endl;</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#ff0000>SortArray(array, ARY_SIZE, <strong>Comparer(CMP_BIG));</strong><br></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; "The ascending sorted array is :";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ListArray(array, ARY_SIZE);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; endl;</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#ff0000>SortArray(array, ARY_SIZE, <strong>Comparer(CMP_LES)</strong>);<br></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; "The descending sorted array is : ";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ListArray(array, ARY_SIZE);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; endl;</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>}</p>
            <p><font color=#ff0000>运行结果：</font></p>
            <p><strong>The initial array is : 10 12 9 31 93 34 98 9 1 20<br>The ascending sorted array is :1 9 9 10 12 20 31 34 93 98<br>The descending sorted array is : 98 93 34 31 20 12 10 9 9 1</strong></p>
            <p>　　程序中定义了一个仿函数Comparer，它重重载了()运算符：<br>　　Comparer::<font color=#000080>bool</font> <font color=#ff0000>operator</font> <font color=#ff00ff>()</font>(<font color=#000080>int</font> num1, <font color=#000080>int</font> num2) <font color=#ff0000>const</font>;<br>　　这里温习一下运算符重载的方式：<br>　　<font color=#000080>ret_type</font> <font color=#ff0000>operator</font> <font color=#ff00ff>opt</font>(array_list);<br>　　其中，ret_type为运算符重载后返回值的类型，operator为c++运算符重载专用关健字，opt为所要重载的运算符，如+, -, *, /, [], ()...<br>　　于是我们可以解读Comparer::bool operator ()(int num1, int num2) const的意义：<br>　　bool限定了()的返回值为布尔类型，(int num1, int num2)指定了运算符()的参数形式，const使得应该运算符可被它的const对象调用。()运算符中根据m_cmpType值返回不同方式下两整数的比较值。</p>
            <p>　　函数<font color=#ff0000><font color=#000080>void</font> </font><strong>SortArray</strong>(<font color=#000080>int</font> array[], <font color=#000080>int</font> size, <font color=#ff0000>const</font> <font color=#000080>Comparer</font> <font color=#ff0000>&amp;</font>cmp) 用于给数组排序。其中，array[]指定所要排序的数组对象，size限定数组元素个数，cmp为Comparer对象的引用，用作对元素的比较使用，前面使用const修饰是向函数调用都声明，在函数内不会有修改该对象任何数据的形为。注意SortArray中的代码：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (cmp(array[indx], array[j]))<br>&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; indx = j;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><font color=#ff0000><font color=#000000>　　</font>其中，cmp为Comparer类的一个对象，但这里的用法好像它是某个函数的样子。这就是仿函数的真谛。</font></p>
            <p>　　别外，void Swap(int &amp;num1, int &amp;num2)完成交换num1与num2值的功能。int &amp;num1表示函数参数使用的引用，用久了c的朋友也许更习惯了void Swap(int *num1, int *num2)，但在c++中这个习惯要改了，引用和指针一样高效，但引用要比指针更直观。下面是指针版的Swap函数：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void Swap(int *num1, int *num2)<br>&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; int temp = *num1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *num1 = *num2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *num2 = temp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>　　实现的功能与程序中使用的一模一样，替换掉程序照样正常工作。仔细比较引用版与指针版的Swap()函数，我相信大多数人会爱上C++的引用版。</p>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/tommyyan/aggbug/97478.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2009-09-28 18:16 <a href="http://www.cppblog.com/tommyyan/articles/97478.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++回调函数(callback)与仿函数(functor)的异同</title><link>http://www.cppblog.com/tommyyan/articles/97477.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Mon, 28 Sep 2009 10:15:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/97477.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/97477.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/97477.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/97477.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/97477.html</trackback:ping><description><![CDATA[<div id=digest>　回调函数(callback)与仿函数(functor)很多时候从用途上来看很相似，以致于我们经常将它们相提并论。例如：</div>
<!--正文-->
<div id=endtext><!--判断阅读权限--><!--判断是否已经扣点-->
<p>　回调函数(callback)与仿函数(functor)很多时候从用途上来看很相似，以致于我们经常将它们相提并论。例如：</p>
<p>inline bool compare(int a, int b)<br>{<br>　　 return a &gt; b;<br>}<br>　<br>struct comparer {<br>　 bool operator()(int a, int b) const {<br>　　　　 return a &gt; b;<br>　 }<br>};<br>　<br>void main()<br>{<br>　　 std::vector&lt;int&gt; vec, vec2;<br>　　 std::sort(vec.begin(), vec.end(), compare);<br>　　 std::sort(vec2.begin(), vec2.end(), comparer());<br>}</p>
<p>　　仿函数(functor)之所以称为仿函数，是因为这是一种利用某些类对象支持operator()的特性，来达到模拟函数调用效果的技术。</p>
<p>　　如果这里vec, vec2这两个vector的内容一样，那么从执行结果看，使用回调函数compare与使用仿函数comparer是一样的。</p>
<p>　　那么，我们应该用回调，还是用仿函数？</p>
<p>　　很多人都说用仿函数吧，回调函数是丑陋的，代码不太象C++风格。</p>
<p>　　但其实问题的本质不是在代码风格上，仿函数与回调函数各有利弊，不能一概而论。</p>
<p>　　仿函数(functor)的优点</p>
<p>　　我的建议是，如果可以用仿函数实现，那么你应该用仿函数，而不要用回调。原因在于：</p>
<p>　　仿函数可以不带痕迹地传递上下文参数。而回调技术通常使用一个额外的void*参数传递。这也是多数人认为回调技术丑陋的原因。</p>
<p>　　更好的性能。</p>
<p>　　仿函数技术可以获得更好的性能，这点直观来讲比较难以理解。你可能说，回调函数申明为inline了，怎么会性能比仿函数差？我们这里来分析下。我们假设某个函数func（例如上面的std::sort）调用中传递了一个回调函数（如上面的compare），那么可以分为两种情况：</p>
<p>　　func是内联函数，并且比较简单，func调用最终被展开了，那么其中对回调函数的调用也成为一普通函数调用（而不是通过函数指针的间接调用），并且如果这个回调函数如果简单，那么也可能同时被展开。在这种情形下，回调函数与仿函数性能相同。</p>
<p>　　func是非内联函数，或者比较复杂而无法展开（例如上面的std::sort，我们知道它是快速排序，函数因为存在递归而无法展开）。此时回调函数作为一个函数指针传入，其代码亦无法展开。而仿函数则不同。虽然func本身复杂不能展开，但是func函数中对仿函数的调用是编译器编译期间就可以确定并进行inline展开的。因此在这种情形下，仿函数比之于回调函数，有着更好的性能。并且，这种性能优势有时是一种无可比拟的优势（对于 std::sort就是如此，因为元素比较的次数非常巨大，是否可以进行内联展开导致了一种雪崩效应）。</p>
<p>　　仿函数(functor)不能做的？</p>
<p>　　话又说回来了，仿函数并不能完全取代回调函数所有的应用场合。例如，我在std::AutoFreeAlloc中使用了回调函数，而不是仿函数，这是因为AutoFreeAlloc要容纳异质的析构函数，而不是只支持某一种类的析构。这和模板（template）不能处理在同一个容器中支持异质类型，是一个道理。</p>
</div>
<img src ="http://www.cppblog.com/tommyyan/aggbug/97477.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2009-09-28 18:15 <a href="http://www.cppblog.com/tommyyan/articles/97477.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C中的预编译宏定义</title><link>http://www.cppblog.com/tommyyan/articles/82040.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Wed, 06 May 2009 07:29:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/82040.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/82040.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/82040.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/82040.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/82040.html</trackback:ping><description><![CDATA[在将一个C源程序转换为可执行程序的过程中, 编译预处理是最初的步骤. 这一步骤是由预处理器(preprocessor)来完成的. 在源流程序被编译器处理之前, 预处理器首先对源程序中的"宏(macro)"进行处理. <br><br>C初学者可能对预处理器没什么概念, 这是情有可原的: 一般的C编译器都将预处理, 汇编, 编译, 连接过程集成到一起了. 编译预处理往往在后台运行. 在有的C编译器中, 这些过程统统由一个单独的程序来完成, 编译的不同阶段实现这些不同的功能. 可以指定相应的命令选项来执行这些功能. 有的C编译器使用分别的程序来完成这些步骤. 可单独调用这些程序来完成. 在gcc中, 进行编译预处理的程序被称为CPP, 它的可执行文件名为cpp. <br><br>编译预处理命令的语法与C语言的语法是完全独立的. 比如: 你可以将一个宏扩展为与C语法格格不入的内容, 但该内容与后面的语句结合在一个若能生成合法的C语句, 也是可以正确编译的.<br><br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(255,1,2)">(一) 预处理命令简介</span><br><br>预处理命令由#(hash字符)开头, 它独占一行, #之前只能是空白符. 以#开头的语句就是预处理命令, 不以#开头的语句为C中的代码行. 常用的预处理命令如下:<br><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">#define</span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 定义一个预处理宏<br><span style="FONT-WEIGHT: bold">#undef&nbsp;</span>&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 取消宏的定义<br><br><span style="FONT-WEIGHT: bold">#include </span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 包含文件命令<br><span style="FONT-WEIGHT: bold">#include_next</span>&nbsp;&nbsp; 与#include相似, 但它有着特殊的用途<br><br><span style="FONT-WEIGHT: bold">#if</span> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编译预处理中的条件命令, 相当于C语法中的if语句<br><span style="FONT-WEIGHT: bold">#ifdef</span> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 判断某个宏是否被定义, 若已定义, 执行随后的语句<br><span style="FONT-WEIGHT: bold">#ifndef</span> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 与#ifdef相反, 判断某个宏是否未被定义<br><span style="FONT-WEIGHT: bold">#elif</span> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 若#if, #ifdef, #ifndef或前面的#elif条件不满足, 则执行#elif之后的语句, 相当于C语法中的else-if<br><span style="FONT-WEIGHT: bold">#else</span> &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 与#if, #ifdef, #ifndef对应, 若这些条件不满足, 则执行#else之后的语句, 相当于C语法中的else<br><span style="FONT-WEIGHT: bold">#endif </span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #if, #ifdef, #ifndef这些条件命令的结束标志.<br><span style="FONT-WEIGHT: bold">defined&nbsp;&nbsp;</span> &nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; 与#if, #elif配合使用, 判断某个宏是否被定义<br><br><span style="FONT-WEIGHT: bold">#line</span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 标志该语句所在的行号<br><span style="FONT-WEIGHT: bold">#</span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 将宏参数替代为以参数值为内容的字符窜常量<br><span style="FONT-WEIGHT: bold">##</span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 将两个相邻的标记(token)连接为一个单独的标记<br><span style="FONT-WEIGHT: bold">#pragma</span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;说明编译器信息<br><br><span style="FONT-WEIGHT: bold">#warning</span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 显示编译警告信息<br><span style="FONT-WEIGHT: bold">#error&nbsp;</span>&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; 显示编译错误信息<br><br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(255,1,2)">(二) 预处理的文法</span><br><br>预处理并不分析整个源代码文件, 它只是将源代码分割成一些标记(token), 识别语句中哪些是C语句, 哪些是预处理语句. 预处理器能够识别C标记, 文件名, 空白符, 文件结尾标志.<br><br>预处理语句格式:&nbsp;&nbsp;&nbsp; <span style="FONT-WEIGHT: bold">#command name(...) token(s)</span><br><br>1, command预处理命令的名称, 它之前以#开头, #之后紧随预处理命令, 标准C允许#两边可以有空白符, 但比较老的编译器可能不允许这样. 若某行中只包含#(以及空白符), 那么在标准C中该行被理解为空白. 整个预处理语句之后只能有空白符或者注释, 不能有其它内容.<br>2, name代表宏名称, 它可带参数. 参数可以是可变参数列表(C99).<br>3, 语句中可以利用"\"来换行.<br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,2)">e.g.</span><br><span style="COLOR: rgb(73,73,73)">#&nbsp; define&nbsp; ONE 1 /* ONE == 1 */</span><br>等价于: <span style="COLOR: rgb(73,73,73)">#define ONE　１</span><br><br><span style="COLOR: rgb(73,73,73)">#define err(flag, msg) if(flag) \</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">&nbsp;&nbsp; &nbsp;printf(msg)</span><br>等价于: <span style="COLOR: rgb(73,73,73)">#define err(flag, msg) if(flag) printf(msg)</span><br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(255,1,2)">(三) 预处理命令详述</span><br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">1, #define</span><br>#define命令定义一个宏:<br><span style="FONT-WEIGHT: bold">#define MACRO_NAME(args) tokens(opt)</span><br>之后出现的MACRO_NAME将被替代为所定义的标记(tokens). 宏可带参数, 而后面的标记也是可选的.<br><br><span style="COLOR: rgb(0,1,255)">对象宏</span><br>不带参数的宏被称为"对象宏(objectlike macro)"<br><br>#define经常用来定义常量, 此时的宏名称一般为大写的字符串. 这样利于修改这些常量.<br><span style="FONT-WEIGHT: bold">e.g.</span><br><span style="COLOR: rgb(73,73,73)">#define MAX 100</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">int a[MAX];</span><br style="COLOR: rgb(73,73,73)"><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#ifndef __FILE_H__</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#define __FILE_H__</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#include "file.h"</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#endif</span><br>#define __FILE_H__ 中的宏就不带任何参数, 也不扩展为任何标记. 这经常用于包含头文件.<br><br>要调用该宏, 只需在代码中指定宏名称, 该宏将被替代为它被定义的内容.<br><br><span style="COLOR: rgb(0,1,255)">函数宏</span><br>带参数的宏也被称为"函数宏". 利用宏可以提高代码的运行效率: 子程序的调用需要压栈出栈, 这一过程如果过于频繁会耗费掉大量的CPU运算资源. 所以一些代码量小但运行频繁的代码如果采用带参数宏来实现会提高代码的运行效率.<br><br><span style="TEXT-DECORATION: underline">函数宏的参数是固定的情况</span><br><br>函数宏的定义采用这样的方式: #define name( args ) tokens<br>其中的args和tokens都是可选的. 它和对象宏定义上的区别在于宏名称之后不带括号.<br><br>注意, name之后的左括号(必须紧跟name, 之间不能有空格, 否则这就定义了一个对象宏, 它将被替换为 以(开始的字符串. 但在调用函数宏时, name与(之间可以有空格.<br><br><span style="FONT-WEIGHT: bold">e.g.</span><br><span style="COLOR: rgb(73,73,73)">#define mul(x,y) ((x)*(y))</span><br><br>注意, 函数宏之后的参数要用括号括起来, 看看这个例子:<br>e.g.<br><span style="COLOR: rgb(73,73,73)">#define mul(x,y) x*y</span><br>"mul(1, 2+2);" 将被扩展为: 1*2 + 2<br>同样, 整个标记串也应该用括号引用起来:<br><span style="FONT-WEIGHT: bold">e.g.</span><br><span style="COLOR: rgb(73,73,73)">#define mul(x,y) (x)*(y)</span><br>sizeof mul(1,2.0) 将被扩展为 sizeof 1 * 2.0<br><br>调用函数宏时候, 传递给它的参数可以是函数的返回值, 也可以是任何有意义的语句:<br>e.g.<br><span style="COLOR: rgb(73,73,73)">mul (f(a,b), g(c,d));</span><br><br>e.g.<br><span style="COLOR: rgb(73,73,73)">#define insert(stmt) stmt</span><br>insert ( a=1; b=2;)&nbsp; 相当于在代码中加入 a=1; b=2 .<br>insert ( a=1, b=2;)&nbsp; 就有问题了: 预处理器会提示出错: 函数宏的参数个数不匹配. 预处理器把","视为参数间的分隔符. &nbsp;<br>insert ((a=1, b=2;)) 可解决上述问题.<br><br>在定义和调用函数宏时候, 要注意一些问题:<br>1, 我们经常用{}来引用函数宏被定义的内容, 这就要注意调用这个函数宏时的";"问题.<br>example_3.7:<br>#define swap(x,y) { unsigned long _temp=x; x=y; y=_tmp}<br>如果这样调用它: "swap(1,2);" 将被扩展为: { unsigned long _temp=1; 1=2; 2=_tmp}; <br>明显后面的;是多余的, 我们应该这样调用: swap(1,2)<br>虽然这样的调用是正确的, 但它和C语法相悖, 可采用下面的方法来处理被{}括起来的内容:<br><br>#define swap(x,y) \<br>&nbsp;&nbsp; &nbsp;do { unsigned long _temp=x; x=y; y=_tmp} while (0)<br>swap(1,2); 将被替换为:<br>do { unsigned long _temp=1; 1=2; 2=_tmp} while (0);<br>在Linux内核源代码中对这种do-while(0)语句有这广泛的应用.<br><br>2, 有的函数宏是无法用do-while(0)来实现的, 所以在调用时不能带上";", 最好在调用后添加注释说明.<br>eg_3.8:<br>#define incr(v, low, high) \<br>&nbsp;&nbsp; &nbsp;for ((v) = (low),; (v) &lt;= (high); (v)++)<br>只能以这样的形式被调用: incr(a, 1, 10)&nbsp; /* increase a form 1 to 10 */<br><br><span style="TEXT-DECORATION: underline">函数宏中的参数包括可变参数列表的情况</span><br>C99标准中新增了可变参数列表的内容. 不光是函数, 函数宏中也可以使用可变参数列表.<br><br><span style="FONT-WEIGHT: bold">#define name(args, ...) tokens</span><br><span style="FONT-WEIGHT: bold">#define name(...) tokens</span><br>"..."代表可变参数列表, 如果它不是仅有的参数, 那么它只能出现在参数列表的最后. 调用这样的函数宏时, 传递给它的参数个数要不少于参数列表中参数的个数(多余的参数被丢弃). <br>通过__VA_ARGS__来替换函数宏中的可变参数列表. 注意__VA_ARGS__只能用于函数宏中参数中包含有"..."的情况.<br><br>e.g.<br><span style="COLOR: rgb(73,73,73)">#ifdef DEBUG</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#define my_printf(...) fprintf(stderr, __VA_ARGS__)</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#else</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#define my_printf(...) printf(__VA_ARGS__)</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#endif</span><br><br>tokens中的__VA_ARGS__被替换为函数宏定义中的"..."可变参数列表. <br><br>注意在使用#define时候的一些常见错误:<br>#define MAX = 100<br>#define MAX 100;<br>=, ; 的使用要值得注意. 再就是调用函数宏是要注意, 不要多给出";".<br><br>　
<table id=table1 style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid" align=center>
    <tbody>
        <tr>
            <td>注意: 函数宏对参数类型是不敏感的, 你不必考虑将何种数据类型传递给宏. 那么, 如何构建对参数类型敏感的宏呢? 参考本章的第九部分, 关于"##"的介绍.<br>　</td>
        </tr>
    </tbody>
</table>
<br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">关于定义宏的另外一些问题</span><br>(1) 宏可以被多次定义, 前提是这些定义必须是相同的. 这里的"相同"要求先后定义中空白符出现的位置相同, 但具体的空白符类型或数量可不同, 比如原先的空格可替换为多个其他类型的空白符: 可为tab, 注释...<br><span style="FONT-WEIGHT: bold">e.g.</span><br><span style="COLOR: rgb(73,73,73)">#define NULL 0</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#define NULL　/* null pointer */&nbsp;&nbsp;&nbsp;&nbsp; 0</span><br>上面的重定义是相同的, 但下面的重定义不同:<br><span style="COLOR: rgb(73,73,73)">#define fun(x) x+1</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#define fun(x) x + 1 或: #define fun(y) y+1</span><br>如果多次定义时, 再次定义的宏内容是不同的, gcc会给出"NAME redefined"警告信息.<br><br>应该避免重新定义函数宏, 不管是在预处理命令中还是C语句中, 最好对某个对象只有单一的定义. 在gcc中, 若宏出现了重定义, gcc会给出警告.<br><br>(2) 在gcc中, 可在命令行中指定对象宏的定义:<br><span style="FONT-WEIGHT: bold">e.g.</span><br>$ <span style="COLOR: rgb(0,1,255)">gcc -Wall -DMAX=100 -o tmp tmp.c</span><br>相当于在tmp.c中添加" #define MAX 100".<br><br>那么, 如果原先tmp.c中含有MAX宏的定义, 那么再在gcc调用命令中使用-DMAX, 会出现什么情况呢?<br>---若-DMAX=1, 则正确编译.<br>---若-DMAX的值被指定为不为1的值, 那么gcc会给出MAX宏被重定义的警告, MAX的值仍为1.<br><br>注意: 若在调用gcc的命令行中不显示地给出对象宏的值, 那么gcc赋予该宏默认值(1), 如: -DVAL == -DVAL=1<br><br>(3) #define所定义的宏的作用域<br>宏在定义之后才生效, 若宏定义被#undef取消, 则#undef之后该宏无效. 并且字符串中的宏不会被识别<br><span style="FONT-WEIGHT: bold">e.g.</span><br><span style="COLOR: rgb(73,73,73)">#define ONE 1</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">sum = ONE + TWO&nbsp;&nbsp;&nbsp; /* sum = 1 + TWO&nbsp; */</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#define TWO 2</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">sum = ONE + TWO&nbsp;&nbsp;&nbsp; /* sum = 1 + 2&nbsp;&nbsp;&nbsp; */ &nbsp;</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#undef ONE</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">sum = ONE + TWO&nbsp;&nbsp;&nbsp; /* sum = ONE + 2&nbsp; */</span><br style="COLOR: rgb(73,73,73)"><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">char c[] = "TWO"&nbsp;&nbsp; /* c[] = "TWO", NOT "2"! */</span><br style="COLOR: rgb(73,73,73)"><br>(4) 宏的替换可以是递归的, 所以可以嵌套定义宏.<br><span style="FONT-WEIGHT: bold">e.g.</span><br><span style="COLOR: rgb(73,73,73)"># define ONE NUMBER_1</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)"># define NUMBER_1 1</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">int a = ONE&nbsp; /* a = 1 */</span><br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">2, #undef</span><br>#undef用来取消宏定义, 它与#define对立:<br><span style="FONT-WEIGHT: bold">#undef name</span><br>如够被取消的宏实际上没有被#define所定义, 针对它的#undef并不会产生错误.<br>当一个宏定义被取消后, 可以再度定义它. <br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">3, #if, #elif, #else, #endif</span><br>#if, #elif, #else, #endif用于条件编译:<br><br><span style="FONT-WEIGHT: bold">#if 常量表达式1</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">&nbsp;&nbsp; &nbsp;语句...</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">#elif 常量表达式2</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">&nbsp;&nbsp; &nbsp;语句...</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">#elif 常量表达式3</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">&nbsp;&nbsp; &nbsp;语句...</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">...</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">#else</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">&nbsp;&nbsp; &nbsp;语句...</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">#endif</span><br><br>#if和#else分别相当于C语句中的if, else. 它们根据常量表达式的值来判别是否执行后面的语句. #elif相当于C中的else-if. 使用这些条件编译命令可以方便地实现对源代码内容的控制.<br>else之后不带常量表达式, 但若包含了常量表达式, gcc只是给出警告信息.<br><br>使用它们可以提升代码的可移植性---针对不同的平台使用执行不同的语句. 也经常用于大段代码注释.<br><span style="FONT-WEIGHT: bold">e.g.</span><br><span style="COLOR: rgb(73,73,73)">#if 0</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">{</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">&nbsp;&nbsp; &nbsp;一大段代码;</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">}</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#endif</span><br><br>常量表达式可以是包含宏, 算术运算, 逻辑运算等等的合法C常量表达式, 如果常量表达式为一个未定义的宏, 那么它的值被视为0.<br><span style="COLOR: rgb(73,73,73)">#if MACRO_NON_DEFINED</span>&nbsp; == #if 0<br>在判断某个宏是否被定义时, 应当避免使用#if, 因为该宏的值可能就是被定义为0. 而应当使用下面介绍的#ifdef或#ifndef.<br><br>注意: #if, #elif, #else之后的宏只能是对象宏. 如果name为名的宏未定义, 或者该宏是函数宏. 那么在gcc中使用"-Wundef"选项会显示宏未定义的警告信息.<br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">4, #ifdef, #ifndef, defined.</span><br>#ifdef, #ifndef, defined用来测试某个宏是否被定义<br>#ifdef name&nbsp; 或 #ifndef name<br><br>它们经常用于避免头文件的重复引用:<br><span style="COLOR: rgb(73,73,73)">#ifndef __FILE_H__</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#define __FILE_H__</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#include "file.h"</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">#endif</span><br><br>defined(name): 若宏被定义,则返回1, 否则返回0.<br>它与#if, #elif, #else结合使用来判断宏是否被定义, 乍一看好像它显得多余, 因为已经有了#ifdef和#ifndef. defined用于在一条判断语句中声明多个判别条件:<br><br><span style="COLOR: rgb(73,73,73)">#if defined(VAX) &amp;&amp; defined(UNIX) &amp;&amp; !defined(DEBUG) </span><br><br>和#if, #elif, #else不同, #indef, #ifndef, defined测试的宏可以是对象宏, 也可以是函数宏. 在gcc中使用"-Wundef"选项不会显示宏未定义的警告信息.<br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">5, #include , #include_next</span><br>#include用于文件包含. 在#include 命令所在的行不能含有除注释和空白符之外的其他任何内容.<br><span style="FONT-WEIGHT: bold">#include "headfile"</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">#include &lt;headfile&gt;</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">#include 预处理标记</span><br>前面两种形式大家都很熟悉, "#include 预处理标记"中, 预处理标记会被预处理器进行替换, 替换的结果必须符合前两种形式中的某一种.<br><br>实际上, 真正被添加的头文件并不一定就是#include中所指定的文件. #include"headfile"包含的头文件当然是同一个文件, 但#include &lt;headfile&gt;包包含的"系统头文件"可能是另外的文件. 但这不值得被注意. 感兴趣的话可以查看宏扩展后到底引入了哪些系统头文件.<br><br>关于#include "headfile"和#include &lt;headfile&gt;的区别以及如何在gcc中包含头文件的详细信息, 参考本blog的GCC笔记.<br><br>相对于#include, 我们对#include_next不太熟悉. #include_next仅用于特殊的场合. 它被用于头文件中(#include既可用于头文件中, 又可用于.c文件中)来包含其他的头文件. 而且包含头文件的路径比较特殊: 从当前头文件所在目录之后的目录来搜索头文件.<br>比如: 头文件的搜索路径一次为A,B,C,D,E. #include_next所在的当前头文件位于B目录, 那么#include_next使得预处理器从C,D,E目录来搜索#include_next所指定的头文件.<br><br>可参考<a href="http://gcc.gnu.org/onlinedocs/" target=_blank><u><font color=#0000ff>cpp手册</font></u></a>进一步了解#include_next<br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">6, 预定义宏</span><br>标准C中定义了一些对象宏, 这些宏的名称以"__"开头和结尾, 并且都是大写字符. 这些预定义宏可以被#undef, 也可以被重定义.<br><br>下面列出一些标准C中常见的预定义对象宏(其中也包含gcc自己定义的一些预定义宏:<br><span style="FONT-WEIGHT: bold">__LINE__&nbsp;</span>&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; 当前语句所在的行号, 以10进制整数标注.<br><span style="FONT-WEIGHT: bold">__FILE__&nbsp;</span>&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; 当前源文件的文件名, 以字符串常量标注.<br><span style="FONT-WEIGHT: bold">__DATE__&nbsp;&nbsp;</span> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp; 程序被编译的日期, 以"Mmm dd yyyy"格式的字符串标注.<br><span style="FONT-WEIGHT: bold">__TIME__&nbsp;&nbsp;</span> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;程序被编译的时间, 以"hh:mm:ss"格式的字符串标注, 该时间由asctime返回.<br><br><span style="FONT-WEIGHT: bold">__STDC__</span>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;如果当前编译器符合ISO标准, 那么该宏的值为1<br><span style="FONT-WEIGHT: bold">__STDC_VERSION__</span>&nbsp;&nbsp; &nbsp;如果当前编译器符合C89, 那么它被定义为199409L, 如果符合C99, 那么被定义为199901L. <br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;我用gcc, 如果不指定-std=c99, 其他情况都给出__STDC_VERSION__未定义的错误信息, 咋回事呢?<br><span style="FONT-WEIGHT: bold">__STDC_HOSTED__&nbsp;</span>&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;如果当前系统是"本地系统(hosted)", 那么它被定义为1. 本地系统表示当前系统拥有完整的标准C库.<br><br><br>gcc定义的预定义宏:<br><span style="FONT-WEIGHT: bold">__OPTMIZE__&nbsp;</span>&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;如果编译过程中使用了优化, 那么该宏被定义为1.<br><span style="FONT-WEIGHT: bold">__OPTMIZE_SIZE__</span>&nbsp;&nbsp; &nbsp;同上, 但仅在优化是针对代码大小而非速度时才被定义为1.<br><span style="FONT-WEIGHT: bold">__VERSION__&nbsp;</span>&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;显示所用gcc的版本号.<br>可参考"GCC the complete reference".<br>要想看到gcc所定义的所有预定义宏, 可以运行: $ cpp -dM /dev/null<br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">7, #line</span><br>#line用来修改__LINE__和__FILE__. <br><span style="FONT-WEIGHT: bold">e.g.</span><br>&nbsp; <span style="COLOR: rgb(0,1,2)">printf("line: %d, file: %s\n", __LINE__, __FILE__);</span><br style="COLOR: rgb(0,1,2)"><span style="COLOR: rgb(0,1,2)">#line 100 "haha"</span><br style="COLOR: rgb(0,1,2)"><span style="COLOR: rgb(0,1,2)">&nbsp; printf("line: %d, file: %s\n", __LINE__, __FILE__);</span><br style="COLOR: rgb(0,1,2)"><span style="COLOR: rgb(0,1,2)">&nbsp; printf("line: %d, file: %s\n", __LINE__, __FILE__);</span><br><br>显示:<br><span style="COLOR: rgb(73,73,73)">line: 34, file: 1.c</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">line: 100, file: haha</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">line: 101, file: haha</span> <br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">8, #pragma, _Pragma</span><br>#pragma用编译器用来添加新的预处理功能或者显示一些编译信息. #pragma的格式是各编译器特定的, gcc的如下:<br><span style="FONT-WEIGHT: bold">#pragma GCC name token(s)</span><br><br>#pragma之后有两个部分: GCC和特定的pragma name. 下面分别介绍gcc中常用的.<br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,2)">(1) #pragma GCC dependency</span><br>dependency测试当前文件(既该语句所在的程序代码)与指定文件(既#pragma语句最后列出的文件)的时间戳. 如果指定文件比当前文件新, 则给出警告信息. <br><span style="FONT-WEIGHT: bold">e.g.</span><br>在demo.c中给出这样一句:<br><span style="COLOR: rgb(0,1,2)">#pragma GCC dependency "temp-file"</span><br>然后在demo.c所在的目录新建一个更新的文件: $ <span style="COLOR: rgb(0,1,255)">touch temp-file</span>, 编译: $ <span style="COLOR: rgb(0,1,255)">gcc demo.c</span> 会给出这样的警告信息:&nbsp; <span style="COLOR: rgb(73,73,73)">warning: current file is older than temp-file</span><br>如果当前文件比指定的文件新, 则不给出任何警告信息.<br><br>还可以在在#pragma中给添加自定义的警告信息.<br><span style="FONT-WEIGHT: bold">e.g.</span><br><span style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(0,1,2)">#pragma GCC dependency "temp-file" "demo.c needs to be updated!"</span><br>1.c:27:38: warning: extra tokens at end of #pragma directive</span><br style="COLOR: rgb(73,73,73)"><span style="COLOR: rgb(73,73,73)">1.c:27:38: warning: current file is older than temp-file</span><br><span style="COLOR: rgb(0,1,2)">注意: 后面新增的警告信息要用""引用起来, 否则gcc将给出警告信息.</span><br><br><span style="FONT-WEIGHT: bold">(2) #pragma GCC poison token(s)</span><br>若源代码中出现了#pragma中给出的token(s), 则编译时显示警告信息. 它一般用于在调用你不想使用的函数时候给出<span style="FONT-WEIGHT: bold">出错信息</span>.<br>e.g.<br>#pragma GCC poison scanf<br>scanf("%d", &amp;a); <br><span style="COLOR: rgb(73,73,73)">warning: extra tokens at end of #pragma directive</span><br><span style="COLOR: rgb(73,73,73)">error: attempt to use poisoned "scanf"</span><br>注意, 如果调用了poison中给出的标记, 那么编译器会给出的是出错信息. 关于第一条警告, 我还不知道怎么避免, 用""将token(s)引用起来也不行.<br><br><span style="FONT-WEIGHT: bold">(3) #pragma GCC system_header</span><br>从#pragma GCC system_header直到文件结束之间的代码会被编译器视为系统头文件之中的代码. 系统头文件中的代码往往不能完全遵循C标准, 所以头文件之中的警告信息往往不显示. (除非用 #warning显式指明). <br>(这条#pragma语句还没发现用什么大的用处<img height=19 src="http://www.uml.org.cn/c%2B%2B/images/033.gif" width=19>)<br><br>由于#pragma不能用于宏扩展, 所以gcc还提供了<span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">_Pragma</span>:<br>e.g.<br>#define PRAGMA_DEP #pragma GCC dependency "temp-file"<br>由于预处理之进行一次宏扩展, 采用上面的方法会在编译时引发错误, 要将#pragma语句定义成一个宏扩展, 应该使用下面的_Pragma语句:<br>#define PRAGMA_DEP _Pragma("GCC dependency \"temp-file\"")<br>注意, ()中包含的""引用之前引该加上\转义字符.<br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">9, #, ##</span><br>#和##用于对字符串的预处理操作, 所以他们也经常用于printf, puts之类的字符串显示函数中.<br><span style="FONT-WEIGHT: bold">#</span>用于在宏扩展之后将tokens转换为以tokens为内容的字符串常量.<br><span style="FONT-WEIGHT: bold">e.g.</span><br>#define TEST(a,b) printf( #a "&lt;" #b "=%d\n", (a)&lt;(b));<br>注意: #只针对紧随其后的token有效!<br><span style="FONT-WEIGHT: bold">##</span>用于将它前后的两个token组合在一起转换成以这两个token为内容的字符串常量. 注意##前后必须要有token.<br><span style="FONT-WEIGHT: bold">e.g.</span><br>#define TYPE(type, n) type n<br><br>之后调用:&nbsp; <br>TYPE(int, a) = 1;<br>TYPE(long, b) = 1999;<br>将被替换为:<br>int a = 1;<br>long b = 1999;<br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">(10) #warning, #error</span><br>#warning, #error分别用于在编译时显示警告和错误信息, 格式如下:<br><span style="FONT-WEIGHT: bold">#warning tokens</span><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">#error tokens</span><br><span style="FONT-WEIGHT: bold">e.g.</span><br>#warning "some warning"<br>注意, #error和#warning后的token要用""引用起来!<br>(在gcc中, 如果给出了warning, 编译继续进行, 但若给出了error, 则编译停止. 若在命令行中指定了 -Werror, 即使只有警告信息, 也不编译. 
<img src ="http://www.cppblog.com/tommyyan/aggbug/82040.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2009-05-06 15:29 <a href="http://www.cppblog.com/tommyyan/articles/82040.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用 C99 进行开放源代码的开发</title><link>http://www.cppblog.com/tommyyan/articles/30394.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Sun, 19 Aug 2007 16:38:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/30394.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/30394.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/30394.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/30394.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/30394.html</trackback:ping><description><![CDATA[<table id=content-table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr vAlign=top>
            <td width="100%">
            <table cellSpacing=0 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr vAlign=top>
                        <td width="100%">
                        <p id=subtitle><em>您的 C 代码符合标准吗？</em></p>
                        <img class=display-img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=1></td>
                        <td class=no-print width=192><img height=18 alt=developerWorks src="http://www.ibm.com/developerworks/i/dw.gif" width=192></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr vAlign=top>
            <td width=10><img height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></td>
            <td width="100%">
            <p>级别： 初级</p>
            <p><a href="http://www.ibm.com/developerworks/cn/linux/l-c99/index.html#author"><u><font color=#0000ff>Peter Seebach</font></u></a> (<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#100;&#101;&#118;&#101;&#108;&#111;&#112;&#101;&#114;&#119;&#111;&#114;&#107;&#115;&#64;&#115;&#101;&#101;&#98;&#115;&#46;&#112;&#108;&#101;&#116;&#104;&#111;&#114;&#97;&#46;&#110;&#101;&#116;&#63;&#115;&#117;&#98;&#106;&#101;&#99;&#116;&#61;&#37;&#69;&#55;&#37;&#57;&#52;&#37;&#65;&#56;&#37;&#50;&#48;&#67;&#57;&#57;&#37;&#50;&#48;&#37;&#69;&#56;&#37;&#66;&#70;&#37;&#57;&#66;&#37;&#69;&#56;&#37;&#65;&#49;&#37;&#56;&#67;&#37;&#69;&#53;&#37;&#66;&#67;&#37;&#56;&#48;&#37;&#69;&#54;&#37;&#57;&#52;&#37;&#66;&#69;&#37;&#69;&#54;&#37;&#66;&#65;&#37;&#57;&#48;&#37;&#69;&#52;&#37;&#66;&#66;&#37;&#65;&#51;&#37;&#69;&#55;&#37;&#65;&#48;&#37;&#56;&#49;&#37;&#69;&#55;&#37;&#57;&#65;&#37;&#56;&#52;&#37;&#69;&#53;&#37;&#66;&#67;&#37;&#56;&#48;&#37;&#69;&#53;&#37;&#56;&#70;&#37;&#57;&#49;"><u><font color=#0000ff>developerworks@seebs.plethora.net</font></u></a>), 自由作家<br></p>
            <p>2004 年 4 月 01 日</p>
            <blockquote>C99 是什么？谁需要它？它可用了吗？Poter Seebach 讨论了 ISO C 标准的 1999 年修订版，着重于 Linux 和 BSD 系统上新特性的可用性。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
            <p>开放源代码操作系统所带的 gcc 发行版本并不支持 C99 的所有新特性，不过现在已经有足够多的新特性普遍可用，因此有理由开始认真考虑在新的开发中采用 C99 特性，尤其是用在它们使得效率和清晰度本质上发生变化的那些地方。</p>
            <p>本文回顾了近来发布的 Linux 和 BSD 上的 C99 语言和库特性的可用性。由于这些特性很多是 gcc 的标准特性，所以新版本的 gcc 在大部分其他平台上可以做同样的事情。当然，各个发行版本或者各个 OS 之间的库支持是不同的。</p>
            <p><a name=IDAMUUDD><span class=atitle>以语言标准调用 gcc</span></a></p>
            <p>GNU C 编译器支持许多不同版本的 C 编程语言。可以在命令行上通过 <code>-std</code> 选项来选择所使用的 C 标准的版本。默认选择的不是任何版本的标准，而是&#8220;GNU C&#8221;语言，这门语言有其自己的扩展集。 C 标准的常见版本用下面的选项选择： </p>
            <table cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
                <tbody>
                    <tr>
                        <td width=10><img height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></td>
                        <td>
                        <table cellSpacing=0 cellPadding=5 width="100%" border=1>
                            <tbody>
                                <tr>
                                    <td bgColor=#eeeeee><a name=IDAYUUDD><strong>C-ninety-what?</strong></a><br>
                                    <p>C99 标准是 ISO C 标准的最新修订版本。或许应该先介绍一些历史背景。在早期，C 语言的开发没有组织，经历了很多变化。最后，大部分厂家都接受了 Kernighan 和 Ritchie 的 <em>The C Programming Language</em> 第一版 (1978) 中描述的语言，但是扩展还是司空见惯。ANSI 开始致力于基于此书和现有实际应用之上的标准，到 1989-1990 时，一个标准得到了广泛的使用。这个标准就是广泛流传的&#8220;C89&#8221;；有些人戏称在 K&amp;R 的 1978 版中描述的语言为&#8220;C78&#8221;。在接下来的十年，编译器厂商不断开发新扩展和新特性，并在 1999 年发布了修订的标准，这个标准描述了多年来所做的对众多最有用和广为支持的新特性所进行的标准化工作。这个标准经常被叫做&#8220;C99&#8221;标准。 </p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <ul>
                <li><strong><code>-std=c89</code> </strong>或 <strong><code>-std=iso9899:1990</code> </strong><br>最初的 C89 标准
                <li><strong><code>-std=iso9899:199409</code> </strong><br>C89，增加了 Normative Addendum 1 的变化
                <li><strong><code>-std=c99</code> </strong>or <strong><code>-std=iso9899:1999</code> </strong><br>C99 修订版标准 </li>
            </ul>
            <p>使用 <code>-pedantic</code> 选项来强制遵从某个版本的标准。这个选项主要用于设法确保您的代码迁移到其他编译器时仍可用；例如，如果您正在与不使用 gcc 的人共享一个代码库 (codebase)，您可能希望它在任何时候都能用。注意， <code>-pedantic</code> 标记偶而将会得到给定的标准错误的一些详细信息；例如，它可能试图在 C99 程序上强制执行 C89 规则，或者可能会在强制执行模糊的规则时失败。还是值得用它来做测试。如果您正在尝试编写可移植代码，应该好好研究一下 <code>-std=c99 -pedantic -Wall</code> 。 </p>
            <p>C89 标准引入了一个新概念；&#8220;独立的 (freestanding)&#8221;和&#8220;托管的 (hosted)&#8221;环境之间的区别。多数人都很熟悉托管的环境；它提供了完整的标准库，并总是从 <code>main()</code> 开始执行。如果您需要独立环境所包含的稍有不同的警告与行为集合，那么使用 <code>-ffreestanding</code> 选项。默认地是假定为托管的环境。为了解决常见的 FAQ，gcc 会故意在 <code>main()</code> 声明使用的参数或返回类型不是标准中所列出的类型时给出警告；而 C99 标准允许实现提供另外的声明，但是这些实现是永远不可移植的。尤其是，通常以 <code>void</code> 为返回类型声明 <code>main()</code> 的习惯是完全错误的。(这就是 NetBSD 内核使用 <code>-ffreestanding</code> 标记来编译的原因。) </p>
            <br>
            <table cellSpacing=0 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
                    </tr>
                </tbody>
            </table>
            <table class=no-print cellSpacing=0 cellPadding=0 align=right>
                <tbody>
                    <tr align=right>
                        <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
                        <table cellSpacing=0 cellPadding=0 border=0>
                            <tbody>
                                <tr>
                                    <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                                    <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-c99/index.html#main"><strong><u><font color=#0000ff>回页首</font></u></strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br><br>
            <p><a name=IDARRUDD><span class=atitle>语言特性</span></a></p>
            <p>C 编程语言有两个容易混淆的部分：语言和库。以前，有很多通用工具代码，大家都倾向于重复使用，这些代码最后被标准化为标准 C 库。最初这一区别非常容易理解：如果要进行编译，那么就是语言，如果是在附加代码中，那么就是库。事易时移，这个区别变得模糊了。例如，一些编译器会生成对外部库的调用以进行 64 位运算，同时一些库函数可能会由编译器不可思议地处理。本文遵循标准的术语进行区分，即来自标准的&#8220;库&#8221;部分的特性是库特性，在文章的下一节将讨论这些特性。本节讨论除此之外的所有内容。 </p>
            <p>C99 语言引入了许多可能会引起软件开发人员兴趣的新特性。这些特性中有很多类似于 C 扩展的 GNU C 集的特性；不幸的是，在一些情况下，它们并不是很兼容。</p>
            <p>已经增加了一些在 C++ 中常见的特性。具体来说，// 注释、混合声明和混合代码已经成为 C99 的标准特性。这些在 GNU C 中一直都有，应该在每一个平台都可用。不过，总的来说，C 和 C++ 仍是单独的语言；C99 与 C++ 的兼容性比 C89 要稍差一些。无论何时，试图去编写混合的代码都不是好主意。好的 C 代码将是不好的 C++ 代码。</p>
            <p>C99 增加了一些对 Unicode 字符（既包括字符串文字内的字符，也包括标识符内的字符）的支持，实际上，大部分用户并不需要系统对此的支持；现在还不要期望使用这种字符的源代码可以对其他人可用。一般而言，宽字符和 unicode 支持主要体现在编译器中，但是文本处理工具还没有达到标准。</p>
            <p>新的变长数组（variable-length array，VLA）已经部分可用。可以用简单的 VLA。不过，这纯粹是一个巧合；实际上，GNU C 有其自己的变长数组支持。结果，虽然使用变长数组的简单代码将可以工作，但是大量的代码会遇到旧的 GNU C 对 VLA 的支持与 C99 定义之间在在差异的麻烦。可以声明长度为本地变量的数组，但是就到此为止吧。</p>
            <p>复合文字和指定的初始化程序是非常好的代码可维护性特性。比较下面两个代码片断：</p>
            <a name=IDAZRUDD><strong>清单 1. 在 C89 中延迟 n 微秒</strong> </a><br>
            <table cellSpacing=0 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=code-outline>
                        <pre class=displaycode>    /* C89 */
                        {
                        struct timeval tv = { 0, n };
                        select(0, 0, 0, 0, &amp;tv);
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br><a name=IDAESUDD><strong>清单 2. 在 C99 中延迟 n 微秒</strong> </a><br>
            <table cellSpacing=0 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=code-outline>
                        <pre class=displaycode>    // C99
                        select(0, 0, 0, 0, &amp; (struct timeval) { .tv_usec = n });
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br>
            <p>复合文字的语法允许用大括号括起来的一系列值来初始化适当类型的自动对象。每一次运行到对象的声明时它会被初始化，所以使用可能会修改相应对象的函数（比如一些版本的 <code>select</code> ）时这样做是安全的。指定的初始化程序语法允许您通过名字初始化成员，不用理会它们在对象中出现的顺序。当对象庞大而复杂却只有很少成员需要初始化时，这特别有用。使用普通的聚合初始化程序时，缺少的值会被认为它们已经被初始化程序指定为 0 来处理。其他初始化规则稍有修改；例如，现在您可以在 <code>enum</code> 声明后跟一个逗号，以更轻松地编写代码生成器。 </p>
            <p>多年来，人们一直在争论 C 的类型系统的扩展，比如 <code>long long</code> 。C99 引入了几个新的整数类型。应用最广的是 <code>long long</code> 。标准方法引入的另一种类型是 <code>intmax_t</code> 。这两种类型在 gcc 中都可以用。不过，整数提升规则对于比 long 更大的类型并不总是正确。可能最好用显式的类型转换。 </p>
            <p>还有很多类型，允许对期望的性质进行更为详细的描述。例如，有的类型的名字是 <code>int_least8_t</code> ，它至少有 8 位，还有 <code>int32_t</code> ，它恰好是 32 位。标准保证至少可以访问 8 位、16 位、32 位和 64 位类型。没有保证会提供精确宽度类型。不要使用这种类型，除非您肯定是实在不能接受更大的类型。另一个可选的类型是新的 <code>intptr_t</code> 类型，它是一个足够大的可以容纳一个指针的整数。并不是所有的系统都提供这样一种类型（尽管当前所有的 Linux 和 BSD 实现都提供）。 </p>
            <p>C 预处理程序有很多新特性；它允许空参数，支持参数数量可变的宏，有一个用于宏生成程序的 <code>_Pragma</code> 操作符，还有一个 <code>__func__</code> 宏，它的内容始终是当前函数的名字。这些特性在当前版本的 gcc 中都已经有了。 </p>
            <p>C99 增加了 <code>inline</code> 关键字以支持函数内联。GNU C 也支持这一关键字，但是在语义上略有不同。如果您正在使用 gcc，而且如果您期望代码有与 C99 相同的行为，您应该记住在内联函数前使用 <code>static</code> 关键字。这在以后的修订中可能会解决，同时，您可以将 <code>inline</code> 作为一个编译技巧，但不要依赖于确切的语义。 </p>
            <p>C99 引入了一个限定词 <code>restrict</code> ，它可以向编译器给出关于指针的优化提示。因为编译器不需要对它做任何事，所以只是因为 gcc 接受了它才引入它。优化的程度不同。用它是安全的，但是还不要希望它可以带来多大的改变。根据相关的注解，新的类型别名 (type-aliasing) 规则已经在 gcc 中得到了完全的支持。这通常意味着您必须要更加留心类型的双义性，它几乎总会去调用不明确的行为，除非您用来访问错误类别数据的类型是 <code>unsigned char</code> 。 </p>
            <p>作为函数参数的数组声明符现在与指针声明符有了很大意义上的不同；您可以插入类型限定词。尤其有意思的是给数组声明增加 <code>了</code> <code>static</code> 类型修饰符，这是非常古怪的优化提示。看这个声明： <code>int foo(int a[static 10]); </code></p>
            <p>用一个没有指向至少 10 个 <code>int</code> 类型对象的指针去调用 <code>foo()</code> 是不明确的行为。这是一种优化技巧。您这样做是向编译器保证传递给那个函数的参数将至少是那么大；有一些机器可能会以此来拆解循环。老手应该会很清楚，它不是一个新的 C 标准，因为它没有给予 <code>static</code> 关键字全新的含义。 </p>
            <p>最后一个特性是灵活的数组成员。有一个常见的声明结构体的问题，可能会期望这个结构体由一个头以及接下来的一些数据类型构成。不幸的是，由于不能给结构体一个指向独立分配区域的指针，C89 没有提供好的解决方法。两个常见的解决方案包括，声明一个成员只占一字节存储空间，然后分配额外的超出数组边界的空间，或者声明一个成员要占用比您可能需要的更多的空间，等待分配，而且要小心只去使用可用的存储空间。这两种方案对一些编译器来说都会有问题，所以 C99 为此引入了一个新语法：</p>
            <a name=IDAALGCD><strong>清单 3. 具有灵活数组的结构体</strong> </a><br>
            <table cellSpacing=0 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td class=code-outline>
                        <pre class=displaycode>    struct header {
                        size_t len;
                        unsigned char data[];
                        };
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br>
            <p>这种结构体的有用之处在于，如果您分配 (sizeof(struct header) + 10) 字节的空间，您可以像处理一个 10 字节的数组一样来处理数据。这个新语法在 gcc 中也得到了支持。</p>
            <br>
            <table cellSpacing=0 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
                    </tr>
                </tbody>
            </table>
            <table class=no-print cellSpacing=0 cellPadding=0 align=right>
                <tbody>
                    <tr align=right>
                        <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
                        <table cellSpacing=0 cellPadding=0 border=0>
                            <tbody>
                                <tr>
                                    <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                                    <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-c99/index.html#main"><strong><u><font color=#0000ff>回页首</font></u></strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br><br>
            <p><a name=N1017C><span class=atitle>库特性</span></a></p>
            <p>这对编译器来说是很好的。那么标准库如何呢？基于现有的实践，尤其是来源于 BSD 和 Linux 社区的实践，C99 中增加了很多库特性。所以，这些特性中很多是在 Linux 和 BSD 标准库中已经可以找到的。这些特性中很多只是简单的工具函数；几乎所有特性原则上都可以在轻便的代码中完成，但有很多是特别难的。</p>
            <p>C99 中增加的最方便的特性在 <code>printf</code> 函数家族中。首先， <code>v*scanf</code> 函数成为了标准； <code>scanf</code> 家族的每一个成员都有一个相应的 <code>v*scanf</code> 函数，这些函数使用一个 <code>va_list</code> 参数而不是可变的参数列表。这些函数的角色与 <code>v*printf</code> 函数相同，允许用户自定义获取可变参数列表的函数，并最终调用 <code>printf</code> 或 <code>scanf</code> 家族中的函数来完成复杂的工作。 </p>
            <p>其次，引入了 4.4BSD 的 <code>snprintf</code> 函数家族。 <code>snprintf</code> 函数让您可以安全地输出到固定大小的缓冲区。当被告知输出不超过 <code>n</code> 个字节时， <code>snprintf</code> 保证会创建一个长度不超过 <code>n-1</code> 的字符串，字符串最后是一个空结束符。不过，如果 <code>n</code> 足够大，它的返回码是将会完成写入的字符数目。这样，您可以确切地得知您 <em>将 </em>需要多少缓冲区空间才可以完全格式化某些内容。这个函数随处可用，而且您应该始终使用它；很多安全漏洞都归咎于 <code>sprintf</code> 中的缓冲区溢出，而这个函数可以预防这个问题。 </p>
            <p>新标准中很多新的数学特性，包括复杂的数学特性和专用的函数，都是设计用来帮助优化特定浮点芯片的编译器，但不能保证所有场合都已实现。如果您需要这些函数，最好先去检查一下您的目标平台上有没有这些函数。浮点环境函数并不是总被支持，有一些平台不会支持 IEEE 运算。现在还不要依赖于这些新特性。</p>
            <p>C99 中对 <code>strftime()</code> 函数进行了扩展，以提供更多常用的格式化字符。这些新字符可能在最新的 Linux 和 BSD 系统上都已经可用；但是在较老的系统上，它们还没有广泛可用。在使用新格式之前先检查文档。 </p>
            <p>据说，大部分国际化代码还没有被可靠地实现。</p>
            <p>其他新的库特性通常还没有普遍可用；数学函数在超级计算机编译器中好像已经可用了，国际化函数在美国以外开发的编译器中可能可用。编译器厂商实现的是被要求实现的特性。</p>
            <br>
            <table cellSpacing=0 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
                    </tr>
                </tbody>
            </table>
            <table class=no-print cellSpacing=0 cellPadding=0 align=right>
                <tbody>
                    <tr align=right>
                        <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
                        <table cellSpacing=0 cellPadding=0 border=0>
                            <tbody>
                                <tr>
                                    <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                                    <td vAlign=top align=right><a class=fbox href="http://www.ibm.com/developerworks/cn/linux/l-c99/index.html#main"><strong><u><font color=#0000ff>回页首</font></u></strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br><br>
            <p><a name=IDA4NGCD><span class=atitle>展望</span></a></p>
            <p>一般来说，最好保守地采用新特性。不过，很多 C99 特性现在已经足够普及，因而新的开发项目可以适当地利用它们。gcc 编译器套件已被广为应用，大部分项目完全可以假定它为很多目标平台的一个可选项。如果您主要定位于 Linux 或 BSD 系统，或者两者兼有，您可以依赖于大量 C99 新特性，它们至少部分地得到了支持。这些特性是根据感觉上的需要和实际中的实现实践而采用的，您将会从中受益。</p>
            <p>当决定您期望依赖哪些特性时，不能只是看它是否在您正在使用的机器上可用，而要考虑目标系统（一个或多个）。您想让人们将 OS 升级到更新的发行版本吗？您的目标市场介意使用一个新的编译器吗？在您决定使用一个特性之前，先在可能的目标系统上测试一下这个特性。</p>
            <br><br>
            <p><a name=resources><span class=atitle>参考资料 </span></a></p>
            <ul>
                <li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/library/l-c99.html?S_TACT=105AGX52&amp;S_CMP=cn-a-l"><u><font color=#0000ff>英文原文</font></u></a>. <br><br>
                <li>在 GNU 项目主页上给出了 <a href="http://gcc.gnu.org/c99status.html"><u><font color=#0000ff>C99 语言工作</font></u></a>的当前状态。新版本的 gcc 可能会改变其中一部分。 <br><br><br><br>
                <li><a href="http://std.dkuug.dk/JTC1/SC22/WG14/"><u><font color=#0000ff>WG14</font></u></a> 是完成 C99 的 ISO 工作组。 <br><br><br><br>
                <li>Wiley 给出了 <a href="http://www.wiley.com/WileyCDA/WileyTitle/productCd-0470845732.html"><u><font color=#0000ff>ISO C 1999 的一个硬拷贝版本</font></u></a>，包括第一个技术勘误表 (Technical Corrigendum ，ISBN 0-470-84573-2)。 <br><br><br><br>
                <li><a href="http://std.dkuug.dk/JTC1/SC22/WG14/www/C99RationaleV5.10.pdf"><u><font color=#0000ff>Rationale for the C99 standard</font></u></a>可以以 PDF 格式在线阅读。 <br><br><br><br>
                <li>Kernighan 和 Ritchie 的 <em><a href="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=0131103628"><u><font color=#0000ff><em>The C Programming Language</em> </font></u></a>最早对 C 进行了描述。最初的 ANSI 标准基于这本书的第一版，作为回报，这本书的第二版描述了 ANSI 标准。从芬兰语到中文到犹太文再到盲语，各种版本都有。您可能还会喜欢 <a href="http://cm.bell-labs.com/cm/cs/who/dmr/chist.html"><u><font color=#0000ff>Dennis Ritchie's history of C</font></u></a>。 <br><br><br><br></em>
                <li><em><a href="http://www.ibm.com/software/awdtools/ccompilers/"><u><font color=#0000ff>IBM 的 C 和 C++ 产品家族</font></u></a>的很多成员都符合 C99 标准，包括 <a href="http://www.ibm.com/developerworks/eserver/articles/cheng_visualage.html?S_TACT=105AGX52&amp;S_CMP=cn-a-l"><u><font color=#0000ff>VisualAge C++ for AIX Version 6.0</font></u></a>，这是一个在 AIX 和 Linux 上可用的高级 C/C++ 编译器。在 2002 年的 <a href="http://www.ibm.com/common/ssi/rep_ca/1/897/ENUS202-161/ENUS202-161.PDF"><u><font color=#0000ff>这份声明</font></u></a>（PDF 格式）中可以找到更多细节。在 <a href="http://www.ibm.com/support/docview.wss?uid=swg27002099"><u><font color=#0000ff>C for AIX, Version 6.0, C/C++ Language Reference</font></u></a>（PDF 格式）中可以找到还要多的细节。 <br><br><br><br></em>
                <li><em>从&#8220; <a href="http://www.ibm.com/developerworks/cn/linux/i18n/unicode/linuni/index.html"><u><font color=#0000ff>Linux Unicode 编程</font></u></a>&#8221;（ <em>developerWorks</em>, 2001 年 8 月）中深入学习 Unicode 和国际化相关的标准。 <br><br><br><br></em>
                <li><em><a href="http://www.dinkumware.com/refxc.html"><u><font color=#0000ff>Dinkum C99 Library Reference Manual</font></u></a>是一个非常好的参考资料。 <br><br><br><br></em>
                <li><em><a href="http://www.kuro5hin.org/story/2001/2/23/194544/139"><u><font color=#0000ff>Are you Ready For C99?</font></u></a>( <em>Kuro5hin</em>, 2001) 一文极好地对 C99 进行了概述。 <br><br><br><br></em>
                <li><em>Comeau Computing 公布了 <a href="http://www.comeaucomputing.com/techtalk/c99/"><u><font color=#0000ff>Tech Talk About C99</font></u></a>，其中带有大量举例说明新特性（从 <code>_Bool</code> 到 <code>_func_</code> ，等等）的示例代码。 <br><br><br><br></em>
                <li><em>Thomas Wolf 有一个 <a href="http://home.tiscalinet.ch/t_wolf/tw/c/"><u><font color=#0000ff>关于 C 语言 ISO 标准的信息</font></u></a>的网页，其中有链接指向他所做的 <a href="http://home.tiscalinet.ch/t_wolf/tw/c/c9x_changes.html"><u><font color=#0000ff>C99 中大部分重要变化</font></u></a>的总结，还对新的函数和特性及其使用方法进行了编录。 <br><br><br><br></em>
                <li><em>David R. Tribble 的 <a href="http://david.tribble.com/text/cdiffs.htm"><u><font color=#0000ff>Incompatibilities Between ISO C and ISO C++</font></u></a>讨论了 C99 和 C++98 之间的那些抵触之处。 <br><br><br><br></em>
                <li><em>在 <a href="http://www.ibm.com/developerworks/cn/linux/index.html"><u><font color=#0000ff>developerWorks Linux 专区</font></u></a>可以找到更多为 Linux 开发人员准备的参考资料。 <br><br><br><br></em>
                <li><em>在 Developer Bookstore 的 <a href="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&amp;id=300&amp;parent=Linux"><u><font color=#0000ff>Linux 区</font></u></a>可以找到很多精选的 Linux 书籍。 <br><br><br></em></li>
            </ul>
            <em><br><br></em>
            <p><em><a name=author><span class=atitle>关于作者</span></a></em></p>
            <table cellSpacing=0 cellPadding=0 width="100%" border=0>
                <tbody>
                    <tr>
                        <td colSpan=3><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width="100%"></td>
                    </tr>
                    <tr vAlign=top align=left>
                        <td>
                        <p><img height=80 alt="" src="http://www.ibm.com/developerworks/i/p-seebach.jpg" width=64 align=left></p>
                        </td>
                        <td><img height=5 alt="" src="http://www.ibm.com/i/c.gif" width=4></td>
                        <td width="100%">
                        <p>Peter Seebach 自 1996 年后期以来一直是 ISO C 标准委员会的成员。他因 <code>strsep()</code> 没有加入到 C99 而耿耿于怀，为出气而到处使用它。可以通过 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#100;&#101;&#118;&#101;&#108;&#111;&#112;&#101;&#114;&#119;&#111;&#114;&#107;&#115;&#64;&#115;&#101;&#101;&#98;&#115;&#46;&#112;&#108;&#101;&#116;&#104;&#111;&#114;&#97;&#46;&#110;&#101;&#116;&#63;&#99;&#99;&#61;"><u><font color=#0000ff>developerworks@seebs.plethora.net</font></u></a> 与他联系。 </p>
                        </td>
                    </tr>
                </tbody>
            </table>
            <em><br></em></td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/tommyyan/aggbug/30394.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2007-08-20 00:38 <a href="http://www.cppblog.com/tommyyan/articles/30394.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字长与平台无关的整型数据类型</title><link>http://www.cppblog.com/tommyyan/articles/30358.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Sun, 19 Aug 2007 07:46:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/30358.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/30358.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/30358.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/30358.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/30358.html</trackback:ping><description><![CDATA[<p><br><!--start story AD-->C99标准定义一个叫着&lt;inttype.h&gt;的头文件，该头文件定义了一系列各种类别的整数类型typedef名字。尽管速多C++工具支持该头文件已经有一段时间了，但它尚未正式收录于C++标准，因此，在使用该头文件之前，你应该先阅读你的编译器文档，看看它是否支持该文件。<br><br>字长固定的整型类型<br>在这个头文件的类型定义中，有一套是字长固定的整型类型：</p>
<p>int8_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int16_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int32_t&nbsp;&nbsp;&nbsp;&nbsp; int64_t </p>
<p>与其对应的字长固定的无符号型整型类型有：</p>
<p>uint8_t&nbsp;&nbsp;&nbsp;&nbsp;uint16_t&nbsp;&nbsp;&nbsp;&nbsp;uint32_t&nbsp;&nbsp;&nbsp; uint64_t </p>
<p>它们的名字非常直观。例如，int8_t是长度固定为8比特的有符号整型类型，而uint8_t则是字长固定为8比特的无符号型整型类型。当你需要确保在不同的平台上，整型数据的字长固定不变，那么你就可以使用这些typedef名字。<br><br>字长最小的快速整型类型<br>该头文件还定义了另外一套typedef名字，即&#8220;最小指定长度的快速整型类型&#8221;。这套typedef名字中的每一种都表示一种整数类型，它满足在长度不小于某个指定长度的前提下，拥有最快的处理速度。这些整数类型的名字为int_fastn_t（有符号）或者uint_fastn_t（无符号），其中 &#8220;n&#8221;表示最小指定长度。例如，int_fast32_t指得是字长至少为32比特的快速有符号整型类型。最小字长快速整型类型有：</p>
<p>int_fast8_t&nbsp; int_fast16_t&nbsp;&nbsp;int_fast32_t&nbsp;&nbsp;int_fast64_t </p>
<p>对应的无符号整型类型有：</p>
<p>uint_fast8_t&nbsp; uint_fast16_t&nbsp;&nbsp;uint_fast32_t&nbsp;&nbsp;uint_fast64_t</p>
<p><br>什么情况下使用这些typedef名字？<br>假设你需要一个字长不少于16比特的循环计数器，那么你会希望该计数器的类型总是当前计算机CPU最佳操作的整型类型，而int_fast16_t可以保证任何平台上的编译器总是选择字长不少于16比特的最快整型类型。<br><br></p>
<p><font color=#0066ff>#include &lt;inttypes.h&gt;<br>for (int_fast16_t n=0; n&lt;30000; ++n)<br>{<br><font color=#006600>//.. do something</font><br>} </font></p>
<hr>
<p>本文作者Danny Kalev 是一个系统分析家、软件工程师，在C++和面向对象设计方面有着14年的专业经验。</p>
<p>===================================================================</p>
<p><strong>关于整型参数移植</strong> </p>
<p>假如你需要确定容量的整型，那么你应该使用stdint.h或者inttypes.h中定义的类型.</p>
<p>这些头文件中定义了以下的整数类型:<br>int8_t; <br>uint8_t; <br>int16_t; <br>uint16_t; <br>int32_t; <br>uint32_t; <br>int64_t; <br>uint64_t; </p>
<p>int_least8_t; <br>uint_least8_t; <br>int_least16_t; <br>uint_least16_t; <br>int_least32_t; <br>uint_least32_t; <br>int_least64_t; <br>uint_least64_t; </p>
<p>int_fast8_t; <br>uint_fast8_t; <br>int_fast16_t; <br>uint_fast16_t; <br>int_fast32_t; <br>uint_fast32_t; <br>int_fast64_t; <br>uint_fast64_t; </p>
<p>intptr_t; <br>uintptr_t; </p>
<p>intmax_t; <br>uintmax_t; </p>
<p><br>如果是boost库的用户则比较幸运,因为在boost库中,&lt;cstdint.hpp&gt;这个头文件封装了C99标准&lt;stdint.h&gt;中的整数类型.</p>
<p><br>参考文章： <br>1.http://blog.vckbase.com/bruceteen/archive/2004/07/27/638.html<br>2.Danny Kalev的文章: </p>
<br><br>
<p id=TBPingURL>Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=661859</p>
</td>
</tr>
<img src ="http://www.cppblog.com/tommyyan/aggbug/30358.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2007-08-19 15:46 <a href="http://www.cppblog.com/tommyyan/articles/30358.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用STL快速编写ini配置文件识别类 </title><link>http://www.cppblog.com/tommyyan/articles/8062.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Thu, 01 Jun 2006 17:07:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/8062.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/8062.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/8062.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/8062.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/8062.html</trackback:ping><description><![CDATA[
		<p> </p>
		<p>ini文件是技术人员经常用到的一种系统配置方法，如何读取和快速识别ini文件中的内容实现起来比较繁琐。STL强大的功能在于能快速的实现排序、查找、 识别等功能。本文通过STL中的map，string，vector,ifstream等，来快速实现ini文件的识别类class <span class="twikiNewLink" style="BACKGROUND: #ffffce"><font color="#0000ff">IniFile</font><a href="http://www.stlchina.org/twiki/bin/edit.pl/Main/IniFile?topicparent=Main.STLforIni"><sup><font color="#1e5bbd">?</font></sup></a></span>。IniFile可以实现常见查找功能，并提供完整的源码。 </p>
		<p>
		</p>
		<h3>
				<a name="1">
				</a>
				<a name="1_">
				</a>1 设计需求： </h3>
		<p>ini文件的格式一般如下: </p>
		<pre>[section1]
key1=value1
key2=value2
......

[section2]
key1=value1
key2=value2    #注释
......
实际的例子是：

#ini for path
[path]
dictfile = /home/tmp/dict.dat
inputfile= /home/tmp/input.txt
outputfile= /home/tmp/output.txt

#ini for exe
[exe]
user= winter       //user name
passwd= 1234567    #pass word
database= mydatabase
</pre>
		<p>其中有五种元素：section 名，Key名，value值，注释 #或者//开头,标志字符"[" "]" "="。查找项的对应关系为sectiong-key和value对应。需要得到是value。class <span class="twikiNewLink" style="BACKGROUND: #ffffce"><font color="#0000ff">IniFile</font><a href="http://www.stlchina.org/twiki/bin/edit.pl/Main/IniFile?topicparent=Main.STLforIni"><sup><font color="#1e5bbd">?</font></sup></a></span>要实现的是两个函数：读入ini文件，读取sect-key对应的value值。即实现下面的接口： </p>
		<pre>class IniFile{
public:
    IniFile();
    //打开ini文件
    bool open(const char* pinipath);
    //读取value值
    const char* read(const char* psect, const char*pkey);
  };
</pre>
		<p>
		</p>
		<h3>
				<a name="2">
				</a>
				<a name="2_">
				</a>2 设计实现： </h3>
		<p>用ifstream按行读入ini文件的内容 </p>
		<p>识别每一行的字符串，分析出sectiong，key，value，和注释。 </p>
		<p>用map<string, string,="" less&lt;string=""> &gt;来记录所有的sectiong－key和value。 
<p>重新定义class <span class="twikiNewLink" style="BACKGROUND: #ffffce"><font color="#0000ff">IniFile</font><a href="http://www.stlchina.org/twiki/bin/edit.pl/Main/IniFile?topicparent=Main.STLforIni"><sup><font color="#1e5bbd">?</font></sup></a></span></p><pre>typedef map&lt;string, string, less&lt;string&gt; &gt; strMap;
typedef strMap::iterator strMapIt;

const char*const MIDDLESTRING = "_____***_______";
class IniFile
{
public:
    IniFile( ){};
    ~IniFile( ){};
    bool open(const char* pinipath)
    {
        return do_open(pinipath);
    }
    string read(const char*psect, const char*pkey)
    {
        string mapkey = psect;
        mapkey += MIDDLESTRING;
        mapkey += pkey;
        strMapIt it = c_inimap.find(mapkey);
        if(it == c_inimap.end())
            return "";
        else
            return it-&gt;second;
    }
protected:
    bool do_open(const char* pinipath)
    {
        ifstream fin(pinipath);
        if(!fin.is_open())
            return false;
        vector&lt;string&gt; strvect;
        while(!fin.eof())
        {
            string inbuf;
            getline(fin, inbuf,'\n');
            strvect.push_back(inbuf);
        }
        if(strvect.empty())
            return false;
        for_each(strvect.begin(), strvect.end(), analyzeini(c_inimap));
        return !c_inimap.empty();
    }
    strMap c_inimap;
};
</pre><p>其中do_open是用来真正实现初始化ini内容的函数。先用ifstream fin打开一个文件，然后用is_open判断文件是否正常打开。顺序读取文件的时候用eof()判断是否到文件尾。getline是一个字符处理函数：直接从fin中读取一行。然后用while循环过滤一行末尾的空格等字符。最后保存到一个vector中，完成读入文本工作。其中比较值得关注的是以下为体，你知道为什么这么做么？ </p><p></p><ul><li>用ifstream和getline来读入而不是用fopen和fread。 
</li><li>用is_open判断是否打开，而不是直接读取。 
</li><li>用vector的push_pack而不是insert。 
</li><li>用empty判断是否为空，而不是用size()==0。 </li></ul><p>下一步用for_each函数来完成字符串的内容提取工作。声明一个结构，实现对操作符()的重载。代码如下： </p><pre>truct analyzeini{
    string strsect;
    strMap *pmap;
    analyzeini(strMap &amp; strmap):pmap(&amp;strmap){}
    void operator()( const string &amp; strini)
    {
        int first =strini.find('[');
        int last = strini.rfind(']');
        if( first != string::npos &amp;&amp; last != string::npos &amp;&amp; first != last+1)
        {
            strsect = strini.substr(first+1,last-first-1);
            return ;
        }
        if(strsect.empty())
            return ;
        if((first=strini.find('='))== string::npos)
            return ;
        string strtmp1= strini.substr(0,first);
        string strtmp2=strini.substr(first+1, string::npos);
        first= strtmp1.find_first_not_of(" \t");
        last = strtmp1.find_last_not_of(" \t");
        if(first == string::npos || last == string::npos)
            return ;
        string strkey = strtmp1.substr(first, last-first+1);
        first = strtmp2.find_first_not_of(" \t");
        if(((last = strtmp2.find("\t#", first )) != string::npos) ||
            ((last = strtmp2.find(" #", first )) != string::npos) ||
            ((last = strtmp2.find("\t//", first )) != string::npos)||
            ((last = strtmp2.find(" //", first )) != string::npos))
        {
            strtmp2 = strtmp2.substr(0, last-first);
        }
        last = strtmp2.find_last_not_of(" \t");
        if(first == string::npos || last == string::npos)
            return ;
        string value = strtmp2.substr(first, last-first+1);
        string mapkey = strsect + MIDDLESTRING;
        mapkey += strkey;
        (*pmap)[mapkey]=value;
        return ;
    }
};
</pre>这里大量使用了字符串的查找和字串功能。string的find_last_of系列和find系列，功能确实十分强大。所有在string中没有找到都会返回一个变量string::npos。 
<p>函数先找sectiong，然后分离key值和value值。符合要求的，把section和key值通过中间加上MIDDLESTRING组成一个新的string，插入map中。这里值得注意的是： </p><p>* for_each的使用，结构可以传递参数。 * string的查找函数及返回值 * string的链接和合并函数。 * map的下标操作符的使用。 </p><p></p><h3><a name="3"></a><a name="3_"></a>3 具体使用 </h3><p>把所有代码放在一个头文件中，以后别人使用的时候，只需要包含头文件就可以了，点击查看inifile.h文件。在使用的过程中，注意判断返回值。使用代码如下： </p><pre>#include &lt;iostream&gt;
#include "inifile.h"
using namespace std;
int main()
{
    IniFile ini;
    if(!ini.open("test.ini"))
       return -1;
    string strvalue = ini.read("sect1","key1");
    if(strvalue.empty())
        return -1;
    else
        cout&lt;&lt;"value="&lt;&lt;strvalue&lt;&lt;endl;
    return 0;
}  </pre></string,></p>
		<p>
		</p>
<img src ="http://www.cppblog.com/tommyyan/aggbug/8062.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2006-06-02 01:07 <a href="http://www.cppblog.com/tommyyan/articles/8062.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++的sstream标准库介绍</title><link>http://www.cppblog.com/tommyyan/articles/8023.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Thu, 01 Jun 2006 05:02:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/8023.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/8023.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/8023.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/8023.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/8023.html</trackback:ping><description><![CDATA[
		<p>接下来我们继续看一下<font color="#ff0000"><b>C++风格的串流控制</b></font>，C++引入了ostringstream、istringstream、stringstream这三个类，要使用他们创建对象就必须包含sstream.h头文件。 </p>
		<p>　　istringstream类用于执行C++风格的串流的输入操作。 <br />　　ostringstream类用于执行C风格的串流的输出操作。 <br />　　strstream类同时可以支持C风格的串流的输入输出操作。</p>
		<p>　　istringstream类是从istream（输入流类）和stringstreambase（c++字符串流基类）派生而来，ostringstream是从ostream（输出流类）和stringstreambase（c++字符串流基类）派生而来，stringstream则是从iostream(输入输出流类)和和stringstreambase（c++字符串流基类）派生而来。 </p>
		<p>　　他们的继承关系如下图所示:</p>
		<p align="center">
				<img alt="" src="http://www.pconline.com.cn/pcedu/empolder/gj/c/0504/pic/08cppios01.gif" border="0" />
		</p>
		<p>　　istringstream是由一个string对象构造而来，istringstream类从一个string对象读取字符。 <br />　　istringstream的构造函数原形如下： <br />　　istringstream::istringstream(string str); </p>
		<p class="code">
				<font color="#008000">//程序作者:管宁 </font>
				<br />
				<font color="green">//站点:www.cndev-lab.com </font>
				<br />
				<font color="green">//所有稿件均有版权,如要转载,请务必著名出处和作者 </font>
				<br />#include &lt;<font color="maroon">iostream</font>&gt;  <br />#include &lt;<font color="maroon">sstream</font>&gt;  <br /><font color="blue">using</font> <font color="blue">namespace</font> std;  <br /><font color="blue">int</font> <font color="blue">main</font>()   <br />{  <br />istringstream istr;  <br />istr.str("1 56.7",);  <br /><font color="green">//上述两个过程可以简单写成 istringstream istr("1 56.7"); </font><br /><font color="maroon">cout</font> &lt;&lt; istr.str()&lt;&lt;endl;  <br /><font color="blue">int</font> a;  <br /><font color="blue">float</font> b;  <br />istr&gt;&gt;a;  <br /><font color="maroon">cout</font>&lt;&lt;a&lt;&lt;endl;  <br />istr&gt;&gt;b;  <br /><font color="maroon">cout</font>&lt;&lt;b&lt;&lt;endl;  <br />system("pause");  <br />} </p>
		<p>　　上例中，构造字符串流的时候，空格会成为字符串参数的内部分界，例子中对a,b对象的输入"赋值"操作证明了这一点，字符串的空格成为了整型数据与浮点型数据的分解点，利用分界获取的方法我们事实上完成了字符串到整型对象与浮点型对象的拆分转换过程。 </p>
		<p>　　str()成员函数的使用可以让istringstream对象返回一个string字符串（例如本例中的输出操作(cout&lt;&lt;istr.str();）。</p>
		<p>　　ostringstream同样是由一个string对象构造而来，ostringstream类向一个string插入字符。 <br />　　ostringstream的构造函数原形如下： <br />　　ostringstream::ostringstream(string str); <br /><br />　　示例代码如下：</p>
		<p class="code">
				<font color="#008000">//程序作者:管宁 </font>
				<br />
				<font color="green">//站点:www.cndev-lab.com </font>
				<br />
				<font color="green">//所有稿件均有版权,如要转载,请务必著名出处和作者 </font>
				<br />#include &lt;<font color="maroon">iostream</font>&gt;  <br />#include &lt;<font color="maroon">sstream</font>&gt;  <br />#include &lt;<font color="maroon">string</font>&gt;  <br /><font color="blue">using</font> <font color="blue">namespace</font> std;  <br /><font color="blue">int</font> <font color="blue">main</font>()   <br />{  <br />ostringstream ostr;  <br /><font color="green">//ostr.str("abc");//如果构造的时候设置了字符串参数,那么增长操作的时候不会从结尾开始增加,而是修改原有数据,超出的部分增长 </font><br />ostr.put('d');  <br />ostr.put('e');  <br />ostr&lt;&lt;"fg";  <br />  <br /><font color="maroon">string</font> gstr <font color="red">=</font> ostr.str();  <br /><font color="maroon">cout</font>&lt;&lt;gstr;  <br />system("pause");  <br />}</p>
		<p>　　在上例代码中，我们通过put()或者左移操作符可以不断向ostr插入单个字符或者是字符串，通过str()函数返回增长过后的完整字符串数据，但值得注意的一点是，当构造的时候对象内已经存在字符串数据的时候，那么增长操作的时候不会从结尾开始增加,而是修改原有数据,超出的部分增长。</p>
		<p>　　对于stringstream了来说，不用我多说，大家也已经知道它是用于C++风格的字符串的输入输出的。 <br />　　stringstream的构造函数原形如下： <br /><br />　　stringstream::stringstream(string str);</p>
		<p>　　示例代码如下:</p>
		<p class="code">
				<font color="#008000">//程序作者:管宁 </font>
				<br />
				<font color="green">//站点:www.cndev-lab.com </font>
				<br />
				<font color="green">//所有稿件均有版权,如要转载,请务必著名出处和作者 </font>
				<br />#include &lt;<font color="maroon">iostream</font>&gt;  <br />#include &lt;<font color="maroon">sstream</font>&gt;  <br />#include &lt;<font color="maroon">string</font>&gt;  <br /><font color="blue">using</font> <font color="blue">namespace</font> std;  <br />  <br /><font color="blue">int</font> <font color="blue">main</font>()   <br />{  <br />    stringstream ostr("ccc");  <br />    ostr.put('d');  <br />    ostr.put('e');  <br />    ostr&lt;&lt;"fg";  <br />    <font color="maroon">string</font> gstr <font color="red">=</font> ostr.str();  <br />    <font color="maroon">cout</font>&lt;&lt;gstr&lt;&lt;endl;  <br />  <br />    <font color="blue">char</font> a;  <br />    ostr&gt;&gt;a;  <br />    <font color="maroon">cout</font>&lt;&lt;a  <br />      <br />    system("pause");  <br />}</p>
		<p>　　除此而外，stringstream类的对象我们还常用它进行string与各种内置类型数据之间的转换。 <br /><br />　　示例代码如下：</p>
		<p class="code">
				<font color="#008000">//程序作者:管宁 </font>
				<br />
				<font color="green">//站点:www.cndev-lab.com </font>
				<br />
				<font color="green">//所有稿件均有版权,如要转载,请务必著名出处和作者 </font>
				<br />#include &lt;<font color="maroon">iostream</font>&gt;  <br />#include &lt;<font color="maroon">sstream</font>&gt;  <br />#include &lt;<font color="maroon">string</font>&gt;  <br /><font color="blue">using</font> <font color="blue">namespace</font> std;  <br />  <br /><font color="blue">int</font> <font color="blue">main</font>()   <br />{  <br />    stringstream sstr;  <br /><font color="green">//--------int转string----------- </font><br />    <font color="blue">int</font> a=100;  <br />    <font color="maroon">string</font> str;  <br />    sstr&lt;&lt;a;  <br />    sstr&gt;&gt;str;  <br />    <font color="maroon">cout</font>&lt;&lt;str&lt;&lt;endl;  <br /><font color="green">//--------string转char[]-------- </font><br />    sstr.clear();<font color="green">//如果你想通过使用同一stringstream对象实现多种类型的转换，请注意在每一次转换之后都必须调用clear()成员函数。 </font><br />    <font color="maroon">string</font> name <font color="red">=</font> "colinguan";  <br />    <font color="blue">char</font> cname[200];  <br />    sstr&lt;&lt;name;  <br />    sstr&gt;&gt;cname;  <br />    <font color="maroon">cout</font>&lt;&lt;cname;  <br />    system("pause");  <br />}</p>
		<p>　　接下来我们来学习一下<b><font color="#ff0000">输入/输出的状态标志</font></b>的相关知识，C++中负责的输入/输出的系统包括了关于每一个输入/输出操作的结果的记录信息。这些当前的状态信息被包含在io_state类型的对象中。io_state是一个枚举类型（就像open_mode一样），以下便是它包含的值。 <br /><br />goodbit 无错误 <br /><br />Eofbit 已到达文件尾 <br /><br />failbit 非致命的输入/输出错误，可挽回 <br /><br />badbit　致命的输入/输出错误,无法挽回 <br /></p>
		<p>　　有两种方法可以获得输入/输出的状态信息。一种方法是通过调用rdstate()函数，它将返回当前状态的错误标记。例如，假如没有任何错误，则rdstate()会返回goodbit. <br /><br />　　下例示例，表示出了rdstate()的用法： </p>
		<p>
		</p>
		<p class="code">
				<font color="#008000">//程序作者:管宁   </font>
				<br />
				<font color="green">//站点:www.cndev-lab.com   </font>
				<br />
				<font color="green">//所有稿件均有版权,如要转载,请务必著名出处和作者   </font>
				<br />  <br />#include &lt;<font color="maroon">iostream</font>&gt;  <br /><font color="blue">using</font> <font color="blue">namespace</font> std;  <br />  <br /><font color="blue">int</font> <font color="blue">main</font>()   <br />{  <br />    <font color="blue">int</font> a;  <br />    <font color="maroon">cin</font>&gt;&gt;a;  <br />    <font color="maroon">cout</font>&lt;&lt;<font color="maroon">cin</font>.rdstate()&lt;&lt;endl;  <br />    <font color="blue">if</font>(<font color="maroon">cin</font>.rdstate() == <font color="maroon">ios</font>::goodbit)  <br />    {  <br />        <font color="maroon">cout</font>&lt;&lt;"输入数据的类型正确，无错误！"&lt;&lt;endl;  <br />    }  <br />    <font color="blue">if</font>(<font color="maroon">cin</font>.rdstate() == ios_base::failbit)  <br />    {  <br />        <font color="maroon">cout</font>&lt;&lt;"输入数据类型错误，非致命错误，可清除输入缓冲区挽回！"&lt;&lt;endl;  <br />    }  <br />    system("pause");  <br />}</p>
		<p>　　另一种方法则是使用下面任何一个函数来检测相应的输入/输出状态：</p>
		<p>bool bad(); <br /><br />bool eof(); <br /><br />bool fail(); <br /><br />bool good(); </p>
		<p>　　下例示例，表示出了上面各成员函数的用法：</p>
		<p class="code">
				<font color="#008000">//程序作者:管宁 </font>
				<br />
				<font color="green">//站点:www.cndev-lab.com </font>
				<br />
				<font color="green">//所有稿件均有版权,如要转载,请务必著名出处和作者 </font>
				<br />  <br />#include &lt;<font color="maroon">iostream</font>&gt;  <br /><font color="blue">using</font> <font color="blue">namespace</font> std;  <br />  <br /><font color="blue">int</font> <font color="blue">main</font>()   <br />{  <br />    <font color="blue">int</font> a;  <br />    <font color="maroon">cin</font>&gt;&gt;a;  <br />    <font color="maroon">cout</font>&lt;&lt;<font color="maroon">cin</font>.rdstate()&lt;&lt;endl;  <br />    <font color="blue">if</font>(<font color="maroon">cin</font>.good())  <br />    {  <br />        <font color="maroon">cout</font>&lt;&lt;"输入数据的类型正确，无错误！"&lt;&lt;endl;  <br />    }  <br />    <font color="blue">if</font>(<font color="maroon">cin</font>.fail())  <br />    {  <br />        <font color="maroon">cout</font>&lt;&lt;"输入数据类型错误，非致命错误，可清除输入缓冲区挽回！"&lt;&lt;endl;  <br />    }  <br />    system("pause");  <br />}</p>
		<p>　　如果错误发生，那么流状态既被标记为错误，你必须清除这些错误状态，以使你的程序能正确适当地继续运行。要清除错误状态，需使用clear()函数。此函数带一个参数，它是你将要设为当前状态的标志值。，只要将ios::goodbit作为实参。 <br /><br />　　示例代码如下：</p>
		<p class="code">
				<font color="#008000">//程序作者:管宁 </font>
				<br />
				<font color="green">//站点:www.cndev-lab.com </font>
				<br />
				<font color="green">//所有稿件均有版权,如要转载,请务必著名出处和作者 </font>
				<br />  <br />#include &lt;<font color="maroon">iostream</font>&gt;  <br /><font color="blue">using</font> <font color="blue">namespace</font> std;  <br />  <br /><font color="blue">int</font> <font color="blue">main</font>()   <br />{  <br />    <font color="blue">int</font> a;  <br />    <font color="maroon">cin</font>&gt;&gt;a;  <br />    <font color="maroon">cout</font>&lt;&lt;<font color="maroon">cin</font>.rdstate()&lt;&lt;endl;  <br />    <font color="maroon">cin</font>.clear(<font color="maroon">ios</font>::goodbit);  <br />    <font color="maroon">cout</font>&lt;&lt;<font color="maroon">cin</font>.rdstate()&lt;&lt;endl;  <br />    system("pause");  <br />}</p>
		<p>　　通常当我们发现输入有错又需要改正的时候，使用clear()更改标记为正确后，同时也需要使用get()成员函数清除输入缓冲区，以达到重复输入的目的。 <br /><br />　　示例代码如下：</p>
		<p class="code">
				<font color="#008000">//程序作者:管宁 </font>
				<br />
				<font color="green">//站点:www.cndev-lab.com </font>
				<br />
				<font color="green">//所有稿件均有版权,如要转载,请务必著名出处和作者 </font>
				<br />#include &lt;<font color="maroon">iostream</font>&gt;  <br /><font color="blue">using</font> <font color="blue">namespace</font> std;  <br />  <br /><font color="blue">int</font> <font color="blue">main</font>()   <br />{  <br />    <font color="blue">int</font> a;  <br />    <font color="blue">while</font>(1)  <br />    {  <br />        <font color="maroon">cin</font>&gt;&gt;a;  <br />        <font color="blue">if</font>(!<font color="maroon">cin</font>)<font color="green">//条件可改写为cin.fail() </font><br />        {  <br />            <font color="maroon">cout</font>&lt;&lt;"输入有错!请重新输入"&lt;&lt;endl;  <br />            <font color="maroon">cin</font>.clear();  <br />            <font color="maroon">cin</font>.get();  <br />        }  <br />        <font color="blue">else</font>  <br />        {  <br />            <font color="maroon">cout</font>&lt;&lt;a;  <br />            <font color="blue">break</font>;  <br />        }  <br />    }  <br />    system("pause");  <br />}</p>
		<p>　　最后再给出一个对文件流错误标记处理的例子，巩固学习，代码如下：</p>
		<p class="code">
				<font color="#008000">//程序作者:管宁 </font>
				<br />
				<font color="green">//站点:www.cndev-lab.com </font>
				<br />
				<font color="green">//所有稿件均有版权,如要转载,请务必著名出处和作者 </font>
				<br />#include &lt;<font color="maroon">iostream</font>&gt;  <br />#include &lt;<font color="maroon">fstream</font>&gt;  <br /><font color="blue">using</font> <font color="blue">namespace</font> std;  <br />  <br /><font color="blue">int</font> <font color="blue">main</font>()   <br />{  <br />    ifstream myfile("c:\\1.txt",ios_base::in,0);  <br />    <font color="blue">if</font>(myfile.fail())  <br />    {  <br />        <font color="maroon">cout</font>&lt;&lt;"文件读取失败或指定文件不存在!"&lt;&lt;endl;  <br />    }  <br />    <font color="blue">else</font>  <br />    {  <br />        <font color="blue">char</font> ch;  <br />        <font color="blue">while</font>(myfile.get(ch))  <br />        {  <br />            <font color="maroon">cout</font>&lt;&lt;ch;  <br />        }  <br />        <font color="blue">if</font>(myfile.eof())  <br />        {  <br />            <font color="maroon">cout</font>&lt;&lt;"文件内容已经全部读完"&lt;&lt;endl;  <br />        }  <br />        <font color="blue">while</font>(myfile.get(ch))  <br />        {  <br />            <font color="maroon">cout</font>&lt;&lt;ch;  <br />        }  <br />    }  <br />    system("pause");  <br />}</p>
<img src ="http://www.cppblog.com/tommyyan/aggbug/8023.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2006-06-01 13:02 <a href="http://www.cppblog.com/tommyyan/articles/8023.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>静态、共享和动态库的使用</title><link>http://www.cppblog.com/tommyyan/articles/7912.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Wed, 31 May 2006 00:42:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/7912.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/7912.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/7912.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/7912.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/7912.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="4" width="100%" border="0">
				<tbody>
						<tr>
								<td valign="top" colspan="2">
										<table cellspacing="1" cellpadding="4" width="100%" border="0">
												<tbody>
														<tr>
																<td class="content" valign="top&lt;DIV">
																		<p>　　C语言中有一些函数不需要进行编译，有一些函数也可以在多个文件中使用。一般来说，这些函数都会执行一些标准任务，如数据库输入/输出操作或屏幕控制等。可以事先对这些函数进行编译，然后将它们放置在一些特殊的目标代码文件中，这些目标代码文件就称为库。库文件中的函数可以通过连接程序与应用程序进行连接。这样就不必在每次开发程序时都对这些通用的函数进行编译了。 <br /><br />　　不同类型的应用程序将会使用不同的函数库。例如：libdbm库中组包含了对数据库文件进行访问的dbm函数，需要对数据库进行操作的程序就会与该库进行连接。数学应用程序将使用数学库libm，X-Windows应用程序将使用Xlib库，libX11。另外，所有的程序都将使用标准的C函数库。libc，该库中包含了诸好内存管理或输入输出操作的基本函数，这些库都存放在/usr/lib这些系统公用的目录中，系统中的任何用户都可以利用这些库。当然用户也可以建立自己专用的库函数，供自己或其它指定的人员使用。 <br /><br />　　库可以有三种使用的形式：静态、共享和动态。静态库的代码在编译时就已连接到开发人员开发的应用程序中，而共享库只是在程序开始运行时才载入，在编译时，只是简单地指定需要使用的库函数。动态库则是共享库的另一种变化形式。动态库也是在程序运行时载入，但与共享库不同的是，使用的库函数不是在程序运行开始，而是在程序中的语句需要使用该函数时才载入。动态库可以在程序运行期间释放动态库所占用的内存，腾出空间供其它程序使用。由于共享库和动态库并没有在程序中包括库函数的内容，只是包含了对库函数的引用，因此代码的规模比较小。 <br /><br />　　已经开发的大多数库都采取共享库的方式。ELF格式的可执行文件使得共享库能够比较容易地实现，当然使用旧的a.out模式也可以实现库的共享。Linux系统中目前可执行文件的标准格式为ELF格式。 <br /><br />　　GNU库的使用必须遵守Library GNU Public License(LGPL许可协议)。该协议与GNU许可协议略有不同，开发人员可以免费使用GNU库进行软件开发，但必须保证向用户提供所用的库函数的源代码。 <br /><br />　　系统中可用的库都存放在/usr/lib和/lib目录中。库文件名由前缀lib和库名以及后缀组成。根据库的类型不同，后缀名也不一样。共享库的后缀名由.so和版本号组成，静态库的后缀名为.a。采用旧的a.out格式的共享库的后缀名为.sa。 <br />　　libname.so.major.minor <br />　　libname.a <br /><br />　　这里的name可以是任何字符串，用来唯一标识某个库。该字符串可以是一个单字、几个字符、甚至一个字母。数学共享库的库名为libm.so.5，这里的标识字符为m，版本号为5。libm.a则是静态数学库。X-Windows库名为libX11.so.6，这里使用X11作为库的标识，版本号为6。 <br /><br />　　使用gcc编译器就可以将库与自己开发的程序连接起来，例如：libc.so.5中包含了标准的输入输出函数，当连接程序进行目标代码连接时会自动搜索该程序并将其连接到生成的可执行文件中。标准的输入输出库中包含了许多基本的输入输出函数，如printf函数等。也可以连接其它的一些系统函数库，如数学库等，但与libc.so.5不同，大部分其它的系统库需要在命令行中显式指定所用的库名。 <br /><br />　　在/usr/lib和/lib目录中可以找到绝大多数的共享库。连接时将首先搜索这两个目录。有一些库也可能存放在特定的目录中，在/etc/ld.conf配置文件中给出了这些目录的列表。连接程序也会对列出的这些目录进行搜索。在默认情况下，Linux将首先搜索指定库的共享版本，如果找不到，才会去搜索静态版本。在对共享库进行更新或安装新库后，必须运行ldconfig命令更新/etc/ld.conf文件中相应的项(如果使用RPM进行安装，一般会自动进行更新，不过也不能保证这一点)。 <br /><br />　　在gcc编译器中引用可搜索到的目录中的库文件时，需要使用-l选项和库名。在gcc命令行上输入-lm可以在程序中连接标准算术库，-l将首先使用libname.so进行搜索，这里是libm.so。下面的例子将使用算术库创建bookrecs程序，请注意这里的-lm选项。 <br />　　$ gcc main.c io.c -o bookrecs -lm <br /><br />　　系统中还有一些其它可用的库，常用的是libncurses.a库，包含了一些简单的鼠标移动例程。在命令行中使用-lncurses选项引用libncurses.so库。下面的例子同时调用了数学和光标库。 <br />　　$ gcc mian.c io.c -o bookrecs -lm -lncurses <br /><br />　　在引用其它目录中的库时，需要使用-ldir选项指定该目录。该选项指定了搜索库函数时其它路径。在下面的例子中，用户在连接时使用了mydir目录中的myio.so库文件。 <br />　　$ gcc main.c -o bookrecs -lmydir -lmyio <br /><br />　　.a的是为了支持较老的a.out格式的可执行文件的 <br />　　.so的是支持elf格式的可执行文件的库。 <br /><br />　　静态库是指编译连接时，把库文件的代码全部加入到可执行文件中，所以生成的文件较大， 但运行时，就不再需要库文件了。动态库正好相反，在编译连接时，没有把库文件的代码加入到可执行文件中，所以生成的文件较小，但运行时，仍需要加载库文件 <br /><br />　　.a是静态库文件，可以用ar 命令生成。 <br />　　.so是动态库文件，编译时加上指定的选项即可生成，具体选项看相应的系统文档了。。。。 <br /><br />　　IBM AIX下如下： <br />　　$(CC) $(SHOPT) $(SHLIBS) a.o b.o -o lib$@$(DBBUILDTAIL) <br /><br />　　假设你有test1.c test2.c test3.c , 编写成动态链接库 <br />　　1. 先编译成test1.o test2.o test3.o <br />　　2. gcc -shared -W1, -soname,libvTest.so.1 -o libvTest.so.1.0 *.o<br /></p>
																		<div>
																		</div>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/tommyyan/aggbug/7912.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2006-05-31 08:42 <a href="http://www.cppblog.com/tommyyan/articles/7912.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++中的虚函数(virtual function)</title><link>http://www.cppblog.com/tommyyan/articles/7909.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Tue, 30 May 2006 15:55:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/7909.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/7909.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/7909.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/7909.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/7909.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="95%" border="0">
				<tbody>
						<tr>
								<td align="middle" width="100%">
								</td>
						</tr>
						<tr>
								<td width="100%">1.简介 <br />    虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。假设我们有下面的类层次： 
<p>class A<br />{<br />public:<br />    virtual void foo() { cout &lt;&lt; "A::foo() is called" &lt;&lt; endl;}<br />};</p><p>class B: public A<br />{<br />public:<br />    virtual void foo() { cout &lt;&lt; "B::foo() is called" &lt;&lt; endl;}<br />};</p><p>那么，在使用的时候，我们可以：</p><p>A * a = new B();<br />a-&gt;foo();       // 在这里，a虽然是指向A的指针，但是被调用的函数(foo)却是B的!</p><p>    这个例子是虚函数的一个典型应用，通过这个例子，也许你就对虚函数有了一些概念。它虚就虚在所谓“推迟联编”或者“动态联编”上，一个类函数的调用并不是在编译时刻被确定的，而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数，所以被成为“虚”函数。</p><p>    虚函数只能借助于指针或者引用来达到多态的效果，如果是下面这样的代码，则虽然是虚函数，但它不是多态的：</p><p>class A<br />{<br />public:<br />    virtual void foo();<br />};</p><p>class B: public A<br />{<br />    virtual void foo();<br />};</p><p>void bar()<br />{<br />    A a;<br />    a.foo();   // A::foo()被调用<br />}</p><p>1.1 多态 <br />    在了解了虚函数的意思之后，再考虑什么是多态就很容易了。仍然针对上面的类层次，但是使用的方法变的复杂了一些：</p><p>void bar(A * a)<br />{<br />    a-&gt;foo();  // 被调用的是A::foo() 还是B::foo()？<br />}</p><p>因为foo()是个虚函数，所以在bar这个函数中，只根据这段代码，无从确定这里被调用的是A::foo()还是B::foo()，但是可以肯定的说：如果a指向的是A类的实例，则A::foo()被调用，如果a指向的是B类的实例，则B::foo()被调用。</p><p>这种同一代码可以产生不同效果的特点，被称为“多态”。</p><p>1.2 多态有什么用？ <br />    多态这么神奇，但是能用来做什么呢？这个命题我难以用一两句话概括，一般的C++教程（或者其它面向对象语言的教程）都用一个画图的例子来展示多态的用途，我就不再重复这个例子了，如果你不知道这个例子，随便找本书应该都有介绍。我试图从一个抽象的角度描述一下，回头再结合那个画图的例子，也许你就更容易理解。</p><p>    在面向对象的编程中，首先会针对数据进行抽象（确定基类）和继承（确定派生类），构成类层次。这个类层次的使用者在使用它们的时候，如果仍然在需要基类的时候写针对基类的代码，在需要派生类的时候写针对派生类的代码，就等于类层次完全暴露在使用者面前。如果这个类层次有任何的改变（增加了新类），都需要使用者“知道”（针对新类写代码）。这样就增加了类层次与其使用者之间的耦合，有人把这种情况列为程序中的“bad smell”之一。</p><p>    多态可以使程序员脱离这种窘境。再回头看看1.1中的例子，bar()作为A-B这个类层次的使用者，它并不知道这个类层次中有多少个类，每个类都叫什么，但是一样可以很好的工作，当有一个C类从A类派生出来后，bar()也不需要“知道”（修改）。这完全归功于多态--编译器针对虚函数产生了可以在运行时刻确定被调用函数的代码。</p><p>1.3 如何“动态联编” <br />    编译器是如何针对虚函数产生可以再运行时刻确定被调用函数的代码呢？也就是说，虚函数实际上是如何被编译器处理的呢？Lippman在深度探索C++对象模型[1]中的不同章节讲到了几种方式，这里把“标准的”方式简单介绍一下。</p><p>    我所说的“标准”方式，也就是所谓的“VTABLE”机制。编译器发现一个类中有被声明为virtual的函数，就会为其搞一个虚函数表，也就是VTABLE。VTABLE实际上是一个函数指针的数组，每个虚函数占用这个数组的一个slot。一个类只有一个VTABLE，不管它有多少个实例。派生类有自己的VTABLE，但是派生类的VTABLE与基类的VTABLE有相同的函数排列顺序，同名的虚函数被放在两个数组的相同位置上。在创建类实例的时候，编译器还会在每个实例的内存布局中增加一个vptr字段，该字段指向本类的VTABLE。通过这些手段，编译器在看到一个虚函数调用的时候，就会将这个调用改写，针对1.1中的例子：</p><p>void bar(A * a)<br />{<br />    a-&gt;foo();<br />}</p><p>会被改写为：</p><p>void bar(A * a)<br />{<br />    (a-&gt;vptr[1])();<br />}</p><p>    因为派生类和基类的foo()函数具有相同的VTABLE索引，而他们的vptr又指向不同的VTABLE，因此通过这样的方法可以在运行时刻决定调用哪个foo()函数。</p><p>    虽然实际情况远非这么简单，但是基本原理大致如此。</p><p>1.4 overload和override <br />    虚函数总是在派生类中被改写，这种改写被称为“override”。我经常混淆“overload”和“override”这两个单词。但是随着各类C++的书越来越多，后来的程序员也许不会再犯我犯过的错误了。但是我打算澄清一下：</p><p>override是指派生类重写基类的虚函数，就象我们前面B类中重写了A类中的foo()函数。重写的函数必须有一致的参数表和返回值（C++标准允许返回值不同的情况，这个我会在“语法”部分简单介绍，但是很少编译器支持这个feature）。这个单词好象一直没有什么合适的中文词汇来对应，有人译为“覆盖”，还贴切一些。 <br />overload约定成俗的被翻译为“重载”。是指编写一个与已有函数同名但是参数表不同的函数。例如一个函数即可以接受整型数作为参数，也可以接受浮点数作为参数。 <br />2. 虚函数的语法 <br />    虚函数的标志是“virtual”关键字。</p><p>2.1 使用virtual关键字 <br />    考虑下面的类层次：</p><p>class A<br />{<br />public:<br />    virtual void foo();<br />};</p><p>class B: public A<br />{<br />public:<br />    void foo();    // 没有virtual关键字!<br />};</p><p>class C: public B  // 从B继承，不是从A继承！<br />{<br />public:<br />    void foo();    // 也没有virtual关键字！<br />};</p><p>    这种情况下，B::foo()是虚函数，C::foo()也同样是虚函数。因此，可以说，基类声明的虚函数，在派生类中也是虚函数，即使不再使用virtual关键字。</p><p>2.2 纯虚函数 <br />    如下声明表示一个函数为纯虚函数：</p><p>class A<br />{<br />public:<br />    virtual void foo()=0;   // =0标志一个虚函数为纯虚函数<br />};</p><p>    一个函数声明为纯虚后，纯虚函数的意思是：我是一个抽象类！不要把我实例化！纯虚函数用来规范派生类的行为，实际上就是所谓的“接口”。它告诉使用者，我的派生类都会有这个函数。</p><p>2.3 虚析构函数 <br />    析构函数也可以是虚的，甚至是纯虚的。例如：</p><p>class A<br />{<br />public:<br />    virtual ~A()=0;   // 纯虚析构函数<br />};</p><p>    当一个类打算被用作其它类的基类时，它的析构函数必须是虚的。考虑下面的例子：</p><p>class A<br />{<br />public:<br />    A() { ptra_ = new char[10];}<br />    ~A() { delete[] ptra_;}        // 非虚析构函数<br />private:<br />    char * ptra_;<br />};</p><p>class B: public A<br />{<br />public:<br />    B() { ptrb_ = new char[20];}<br />    ~B() { delete[] ptrb_;}<br />private:<br />    char * ptrb_;<br />};</p><p>void foo()<br />{<br />    A * a = new B;<br />    delete a;<br />}</p><p>    在这个例子中，程序也许不会象你想象的那样运行，在执行delete a的时候，实际上只有A::~A()被调用了，而B类的析构函数并没有被调用！这是否有点儿可怕？</p><p>    如果将上面A::~A()改为virtual，就可以保证B::~B()也在delete a的时候被调用了。因此基类的析构函数都必须是virtual的。</p><p>    纯虚的析构函数并没有什么作用，是虚的就够了。通常只有在希望将一个类变成抽象类（不能实例化的类），而这个类又没有合适的函数可以被纯虚化的时候，可以使用纯虚的析构函数来达到目的。</p><p>2.4 虚构造函数？ <br />    构造函数不能是虚的。</p><p>3. 虚函数使用技巧 3.1 private的虚函数 <br />    考虑下面的例子：</p><p>class A<br />{<br />public:<br />    void foo() { bar();}<br />private:<br />    virtual void bar() { ...}<br />};</p><p>class B: public A<br />{<br />private:<br />    virtual void bar() { ...}<br />};</p><p>    在这个例子中，虽然bar()在A类中是private的，但是仍然可以出现在派生类中，并仍然可以与public或者protected的虚函数一样产生多态的效果。并不会因为它是private的，就发生A::foo()不能访问B::bar()的情况，也不会发生B::bar()对A::bar()的override不起作用的情况。</p><p>    这种写法的语意是：A告诉B，你最好override我的bar()函数，但是你不要管它如何使用，也不要自己调用这个函数。</p><p>3.2 构造函数和析构函数中的虚函数调用 <br />    一个类的虚函数在它自己的构造函数和析构函数中被调用的时候，它们就变成普通函数了，不“虚”了。也就是说不能在构造函数和析构函数中让自己“多态”。例如：</p><p>class A<br />{<br />public:<br />    A() { foo();}        // 在这里，无论如何都是A::foo()被调用！<br />    ~A() { foo();}       // 同上<br />    virtual void foo();<br />};</p><p>class B: public A<br />{<br />public:<br />    virtual void foo();<br />};</p><p>void bar()<br />{<br />    A * a = new B;<br />    delete a;<br />}</p><p>    如果你希望delete a的时候，会导致B::foo()被调用，那么你就错了。同样，在new B的时候，A的构造函数被调用，但是在A的构造函数中，被调用的是A::foo()而不是B::foo()。</p><p>3.3 多继承中的虚函数 3.4 什么时候使用虚函数 <br />    在你设计一个基类的时候，如果发现一个函数需要在派生类里有不同的表现，那么它就应该是虚的。从设计的角度讲，出现在基类中的虚函数是接口，出现在派生类中的虚函数是接口的具体实现。通过这样的方法，就可以将对象的行为抽象化。</p><p>    以设计模式[2]中Factory Method模式为例，Creator的factoryMethod()就是虚函数，派生类override这个函数后，产生不同的Product类，被产生的Product类被基类的AnOperation()函数使用。基类的AnOperation()函数针对Product类进行操作，当然Product类一定也有多态（虚函数）。</p><p>    另外一个例子就是集合操作，假设你有一个以A类为基类的类层次，又用了一个std::vector&lt;A *&gt;来保存这个类层次中不同类的实例指针，那么你一定希望在对这个集合中的类进行操作的时候，不要把每个指针再cast回到它原来的类型（派生类），而是希望对他们进行同样的操作。那么就应该将这个“一样的操作”声明为virtual。</p><p>    现实中，远不只我举的这两个例子，但是大的原则都是我前面说到的“如果发现一个函数需要在派生类里有不同的表现，那么它就应该是虚的”。这句话也可以反过来说：“如果你发现基类提供了虚函数，那么你最好override它”。</p></td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/tommyyan/aggbug/7909.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2006-05-30 23:55 <a href="http://www.cppblog.com/tommyyan/articles/7909.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[内存]内存池-hash map</title><link>http://www.cppblog.com/tommyyan/articles/6100.html</link><dc:creator>星仁</dc:creator><author>星仁</author><pubDate>Sun, 23 Apr 2006 07:29:00 GMT</pubDate><guid>http://www.cppblog.com/tommyyan/articles/6100.html</guid><wfw:comment>http://www.cppblog.com/tommyyan/comments/6100.html</wfw:comment><comments>http://www.cppblog.com/tommyyan/articles/6100.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tommyyan/comments/commentRss/6100.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tommyyan/services/trackbacks/6100.html</trackback:ping><description><![CDATA[
		<p>1、Features: </p>
		<p>a. Memory pool management with share memory <br />b. hash map by custom rules <br />c. support both fixed nodes and linked list nodes with variable length<br />d. custom node lookup </p>
		<p>coding...<br /></p>
<img src ="http://www.cppblog.com/tommyyan/aggbug/6100.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tommyyan/" target="_blank">星仁</a> 2006-04-23 15:29 <a href="http://www.cppblog.com/tommyyan/articles/6100.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>