摘自:http://www.cnblogs.com/hanford/p/6028047.html
这篇文章太好了!
目录
第1章编译步骤 1
第2章编译源文件 2
2.1 编译器 2
2.2 包含头文件 3
2.3 重复包含 6
2.4 预编译头文件 7
2.4.1 创建 7
2.4.2 使用 8
2.4.3 说明 9
第3章编译资源 10
3.1 编译 10
3.1.1 输出文件 10
3.1.2 语言 11
3.2 include语句 12
3.3 引用文件 13
第4章连接 14
第5章其它 17
5.1 编译目录 17
5.2 编译事件 18
5.3 自定义编译 19
5.4 预定义宏 22
5.4.1 ANSI C 预定义宏 22
5.4.2 微软C++预定义宏 23
5.4.3 应用实例 28
5.5 预处理器操作符 28
5.5.1 # 28
5.5.2 #@ 28
5.5.3 ## 28
5.5.4 defined 29
第1章编译步骤
使用Visual C++ 6.0新建一个MFC程序,然后编译。IDE(集成开发环境)下,可以看到编译信息:
图1.1 VC++6.0编译信息
可见其编译的基本步骤为:
1、删除项目的临时文件和输出文件;
2、编译资源,即编译rc文件,生成res文件;
3、预编译头文件,生成pch文件;
4、编译各个源文件(*.c、*.cpp、*.cxx),生成obj文件;
5、链接。具体的就是将obj、res文件合成为一个exe文件,中间可能还需要一些Lib文件。
注意:EVC3.0和EVC4.0的编译步骤与VC++6.0完全一致。VC++6.0之后的版本,其编译步骤发生了变化。第2步的编译资源被移至第4步之后。
从上面的编译步骤可以看到,编译时并没有编译头文件(*.h)。这样是否就意味着不需要头文件了?答案当然是否定的。事实上在编译源文件时,预编译器首先处理#include指令,它会将头文件包含至源文件后再进行编译。编译器是以这种方式使用头文件的,认识到这一点对于理解C/C++编译至关重要。
第2章编译源文件
VC++6.0里编译就是调用C:\Program Files\Microsoft Visual Studio\VC98\Bin\cl.exe将源文件(*.c、*.cpp、*.cxx)编译为obj文件。
2.1 编译器
假定1.cpp里有如下代码:
int a = 0; ++a; int b = a + 1; |
显然编译它是没问题的,但是把1.cpp改名为1.c,再次编译就会出问题。为什么?原因很简单:源文件的扩展名为c时,编译器按C语言的语法进行编译,生成C代码;源文件的扩展名为cpp或cxx时,编译器按C++语言的语法进行编译,生成C++代码。C和C++语法还是有一定差别的。
大家都知道C++支持函数重载,如下面两个函数在C++里是允许的:
int fun(); int fun(int a); |
但在C语言里是不允许的。为什么呢?因为C语言编译fun时不会将其更名,一个obj文件里是不能有两个同名函数的。C++语言就不同了,会将这两个函数自动更名。VC++6.0里将fun()更名为?fun@@YAHXZ,将fun(int a)更名为?fun@@YAHH@Z,程序员眼里的一个函数fun在编译器眼里仍是两个。
这样就引来一个问题:如果一个函数funA在.c文件里,而程序在.cpp里调用了funA函数会出现什么情况?答案是无法调用:因为funA在obj里的名字就是funA,但cpp要连接的funA已经被更名了。为了在cpp里调用c函数,函数声明时需要使用extern "C"语句。如下面代码。
extern "C" int fun(); extern "C" { int funcA(); int funcB(); } |
在C和C++混合编程的时候一定要注意这个问题。
2.2 包含头文件
编译源文件时,若遇到 #include "A.h" 语句,则编译器对头文件的搜索顺序如下:
1、#include语句所在文件的目录
如:C:\VC\1.c 里有 #include "A.h",则编译器会在1.c所在目录查找A.h,即在C:\VC下查找A.h;
如:C:\VC\1.c 里有 #include "../B.h",则编译器会在1.c所在目录查找../B.h,就是查找C:\VC\..\B.h,即C:\B.h。这里的..表示相对路径,即上一级目录。#include "../B.h"里的斜杠/可以换成反斜杠\,不过因为在双引号里\表示转义字符,所以需要两个反斜杠,即:#include "..\\B.h"。以一个斜杠代替两个反斜杠完全是为了简化;
说明:相对路径可通过vcHelper程序获取,这样较为方便。
2、上一级包含文件所在目录
如:C:\VC\1.c 里有 #include "A.h"。编译器在D:\Test目录下找到了A.h。这个A.h里又有#include "B.h",显然编译器会首先在A.h所在目录D:\Test下查找B.h,如果找不到则会在C:\VC\1.c所在目录继续查找。
3、Additional include directories 所列目录下依次进行查找
Additional include directories的设置仅对本项目有效,具体请参考图2.1或图2.2。VC++6.0的多个路径之间请以逗号分隔。路径可以使用绝对路径也可以使用相对路径。如:C:/VC,../Share,./Inc表示了三个目录:
C:\VC 绝对路径
..\Share 相对于项目文件(dsp、vcp、vcproj)的相对路径
.\Inc 相对于项目文件(dsp、vcp、vcproj)的相对路径
搜索头文件时,先搜索C:\VC目录,再搜索..\Share目录,最后搜索.\Inc目录。
图2.1 VC++6.0 的Additional include directories
图2.2 VC++7.0 的Additional include directories
4、在Standard Include Paths里依次进行查找
Standard Include Paths的设置对所有VC++项目都有效。
VC++6.0下,单击【Tools】菜单下的【Option】菜单项,然后进入Directories页面,如下图所示。
图2.3 VC++6.0 的Standard Include Paths
VC++7.0下,单击【Tools】菜单下的【Option】菜单项。显示界面如下图所示。
图2.4 VC++7.0 的Standard Include Paths
以上讨论的是#include ""语句,如果是 #include <>,则对头文件的查找顺序将是上面的第 3、4步。
建议:
1、如果是自己写的头文件,请使用#include ""语句,并使用相对路径;
2、如果是系统的头文件,如:stdio.h,请使用#include <>语句。
2.3 重复包含
假如1.cpp里包含了头文件A.h和B.h,这两个头文件又都包含了C.h。显然编译1.cpp时,C.h将被包含两次。C.h里的结构、类定义也会出现两次,编译器会认为这些结构、类被重复定义了,从而导致编译失败。
解决办法有两个
1、增加#ifndef ... #define ... #endif
如:在C.h里增加如下代码:
//C.h #ifndef __C_H__ #define __C_H__ //以下是C.h的文件内容 ... ... ... //最后别忘了#endif #endif //__C_H__ |
编译器编译1.cpp,第二次包含C.h的时候,因为__C_H__已经被定义了,因此会舍弃第二次包含的内容。
该方法要保证每个头文件里的__C_H__都不相同,否则会导致某两个头文件不能同时被包含。
2、增加#pragma once语句
这个方法比较简单,直接在C.h里增加#pragma once即可。美中不足的是:并不是所有的编译器都支持这条指令。所以,采用其它C/C++编译器的时候要注意这一点。
2.4 预编译头文件
假如有多个源文件都包含了windows.h文件。这个头文件非常大,每次编译都比较耗时。有几个源文件包含了它,它就要被编译几次。显然这样的编译效率是相当低的。能不能只编译一次这样的头文件?答案是肯定,那就是使用预编译头文件。即在编译源文件之前,先把windows.h编译出来生成pch文件。在编译源文件的时候,编译器遇到windows.h时将不再编译,而是直接使用pch文件。这个pch就是预编译头文件。
2.4.1 创建
正如前面所说的,编译器是不会直接编译头文件的。因此要预编译头文件,需要指定一个源文件来完成编译并生成pch文件。这里假定要预编译windows.h。请创建一个源文件,如:pch.cpp。其内容很简单,就是#include
图2.5 VC++6.0创建预编译头文件设置
在VC++7.0的Solution Explorer里,右键单击pch.cpp,然后单击【Properties】菜单项,设置界面如下图所示
图2.6 VC++7.0创建预编译头文件设置
2.4.2 使用
创建PCH文件后,需要设置其它的源文件使用它。
VC++6.0的设置界面如下。
图2.7 VC++6.0使用预编译头文件设置
在VC++7.0的Solution Explorer里,选择要使用预编译头文件的源文件,然后右键单击某一选中文件,或直接右键单击项目然后单击【Properties】菜单项,设置界面如下图所示。注意下图里的my.pch要和图2.6的保持一致,即:创建什么pch文件就使用什么pch文件。
图2.8 VC++7.0使用预编译头文件设置
设置多个源文件的属性与设置项目属性的区别:项目就好像国家,源文件就好像各个省。国家出台了政策后,各个省要遵循;但是各个省可以根据自己的情况对政策进行修改。一个源文件的某个属性没有进行设置,则它使用项目的该属性。项目的属性改变后,这个源文件的属性也随之改变。但是如果单独对这个源文件的属性进行了设置,则项目属性的改变对它没有影响。
2.4.3 说明
需要说明以下几点:
1、VC++7.0项目里可以使用多个预编译头文件;VC++6.0项目使用多个预编译头文件好像比较困难;
2、创建MFC程序后,预编译头文件设置已经完成,一般无需再设置;
3、一个源文件一旦使用了预编译头文件A.h,则该源文件的第一行一定要包含该头文件。换句话说#include "A.h"之前的代码将被忽略;
4、编译预编译头文件出错后,很难调试。因为不知道编译到哪一行出错了?解决方法就是使用#error或#pragma message跟踪编译进度,精确定位编译出错的位置。
第3章编译资源
Windows程序需要资源,如:图标、光标、对话框……VC++6.0里编译资源就是调用C:\Program Files\Microsoft Visual Studio\COMMON\MSDev98\Bin\rc.exe将资源文件(*.rc)编译为res文件。连接程序负责将res文件嵌入exe文件。
3.1 编译
使用记事本创建资源文件1.rc,其内容如下所示。只有一个 ID 为 100 的字符串。注意:1.rc的编码必须为ANSI。
STRINGTABLE DISCARDABLE { 100 "This is a string" } |
在DOS命令窗口,执行如下命令
"C:\Program Files\Microsoft Visual Studio\COMMON\MSDev98\Bin\rc.exe" C:\1.rc |
rc.exe将编译1.rc,并生成1.res。可以使用VC++6.0打开1.res,查看资源内容。
3.1.1 输出文件
默认情况下,1.res与1.rc在同一目录。可以通过/fo命令开关更改输出文件,如下命令将更改输出文件为D:\t.res。
"C:\Program Files\Microsoft Visual Studio\COMMON\MSDev98\Bin\rc.exe"/fo"D:\t.res" C:\1.rc |
在集成开发环境下,Resource file name 对应的就是/fo命令开关。集成开发环境编译时,会将dsp、vcproj文件所在目录设置为当前目录,所以如果Resource file name不是绝对路径,那么它就是相对dsp文件目录的相对路径。
图3.1 VC++6.0资源设置
图3.2 VC++7.0资源设置
3.1.2 语言
将1.rc文件修改成如下内容:
STRINGTABLE DISCARDABLE { 100 "这是一个字符串" } |
使用VC++6.0打开编译生成的res文件,会发现字符串是乱码。因为rc.exe默认使用的语言是美国英语,语言ID为0x409。需要将语言ID更改为简体中文的0x804。具体命令如下:
"C:\Program Files\Microsoft Visual Studio\COMMON\MSDev98\Bin\rc.exe" /fo"D:\t.res" /l804 C:\1.rc |
命令开关/l804相当于在1.rc的第一行插入如下语句:
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
即明确指定语言为简体中文。
如果1.rc的第一行为 LANGUAGE 9,1。即已经指定语言为美国英语,则命令开关/l804是不起任何作用的。
在集成开发环境下,可以通过修改"Language"或"Culture"修改/l命令开关。
3.2 include语句
假定资源文件1.rc里有语句#include "1.rc2",而1.rc2里又有语句#include "2.rc2"。按如下命令进行编译:
图3.3 编译资源
则查找2.rc2的顺序如下:
1、包含它的文件(1.rc2)所在目录;
2、1.rc2被1.rc包含,所以如果上一步未找到,继续在1.rc所在目录查找;
3、在当前目录查找。上图中,当前目录就是C:\User\yhf;
4、由命令开关/i指定的目录。上图中,有两项/i。会依次在D:\inc1、D:\inc2中查找。集成开发环境中,可以设定这些目录。
5、如果在集成环境编译,还会在设置的Include目录中查找。至于它的实现,笔者猜测:集成开发环境调用rc.exe时,将Include目录添加至/i命令开关。
3.3 引用文件
有些资源,如图标,其实就是引用的图标文件,如下面的语句:
IDR_MAINFRAME ICON DISCARDABLE "res\\t6.ico" |
假如上面一行语句位于2.rc2,那么查找res\t6.ico的顺序如下:
1、被编译资源文件所在目录。注意:rc.exe不会查找2.rc2和1.rc2所在目录,它会认为这条语句位于被编译的资源文件(1.rc)内。
2、当前目录;
3、由命令开关/i指定的目录。上图中,有两项/i。会依次在D:\inc1、D:\inc2中查找。集成开发环境中,可以设定这些目录。
4、如果在集成环境编译,还会在设置的Include目录中查找。
第4章连接
VC++6.0里连接就是调用C:\Program Files\Microsoft Visual Studio\VC98\Bin\link.exe将编译生成的obj、res合成为一个exe文件或dll文件。
连接的时候可能还需要其他程序员生成的obj文件和lib文件,使用这些文件的方法有三种。
1、增加该文件至VC项目
图4.1 增加文件至项目
该方法的优点就是简单,缺点是不灵活。如这个方法很难处理这个要求:对于Debug版本需要连接TestD.lib,对于Release版需要连接Test.lib。
2、修改项目设置
首先增加文件,如图4.2和图4.3所示。
如果需要,还可以指定 Additional library path。如图4.2和图4.4所示。VC++6.0连接程序时,首先在项目目录(即dsp、vcp、vcproj文件所在目录)下查找 Func.obj 和 Test.lib,其次在Additional library path下查找,最后在标准目录下查找。标准库文件目录的设置请参考图2.3和图2.4。
图4.2 VC++6.0增加库文件及设置库文件目录
图4.3 VC++7.0增加库文件
图4.4 VC++7.0设置库文件目录
3、在头文件或源文件中,插入如下代码:
#pragma comment(lib, "Test.lib") //连接时使用 Test.lib。
接着指定 Additional library path,请参考方法2里的描述。连接器查找 Test.lib 时,其查找顺序与第 2 种方法相同。
这种方法非常灵活,请见下面的代码。其含义为:Debug版本下连接TestD.lib,Release版本下连接Test.lib。
#ifdef _DEBUG #pragma comment(lib, "TestD.lib") #else #pragma comment(lib, "Test.lib") #endif |
说明:
1、这种方法不能指定obj文件;
2、#pragma comment还可指定绝对路径和相对路径,但是建议不要这样做。
第5章其它
5.1 编译目录
编译、连接的时候会产生大量的文件,可以指定这些文件的存放路径。
临时目录用来存放编译过程中产生的文件,如:pch、obj、res文件。输出目录用来存放最终生成的exe或dll文件。这两个目录的设置如下图所示。注意它们可以是绝对路径,也可以是相对于dsp、vcp、vcproj文件的相对路径。
图5.1 VC++6.0临时、输出目录设置
图5.2 VC++7.0临时、输出目录设置
建议将临时、输出目录设置为Temp,便于查找、删除这些文件。
还可以指定目标文件,请参考下图。
图5.3 VC++6.0设置目标文件
图5.4 VC++7.0设置目标文件
5.2 编译事件
编译的时候有三个事件:
1、Pre-Build
开始编译、连接的时候发生该事件。VC++7.0以上的版本才支持此事件。
2、Pre-Link
编译完毕,准备连接的时候发生该事件。
3、Post-Build
编译、连接都结束的时候发生该事件。
假如需要在程序编译完成的时候将exe文件移动到c:\,可在Post-Build里增加DOS命令:move $(TargetPath) c:\。具体的设置请参考下图。
图5.5 VC++6.0增加编译后处理命令
图5.6 VC++7.0增加编译后处理命令
$(TargetPath)是编译时产生的环境变量,其它的环境变量及含义见下表
环境变量 |
说明 |
$(MSDEVDIR) |
Microsoft Developer目录,如:C:\Program Files\Microsoft Visual Studio\COMMON\MSDev98 |
$(IntDir) |
临时目录 |
$(OutDir) |
输出目录 |
$(TargetPath) |
目标文件,包括路径 |
$(TargetDir) |
目标文件所在目录 |
$(TargetName) |
目标文件名,不包括路径和扩展名 |
5.3 自定义编译
有没有想过在VC++6.0里编译VC#代码?有了Custom Build一切皆有可能。假定有如下VC#代码,存在于文件1.cs。
using System; using System.Windows.Forms; static class Program { static void Main() { MessageBox.Show("Hello VC#"); } } |
请将1.cs添加至VC++6.0工程,并设置其Custom Build属性:
Commands有两条命令,第一行为:
C:\WINDOWS\Microsoft.NET\Framework\v3.5\Csc.exe /out:$(InputDir)\$(InputName).exe $(InputPath)
其实就是调用csc程序,编译1.cs文件,生成1.exe文件。环境变量$(InputPath)就是1.cs相对于dsp文件的路径(包括文件名)。环境变量$(InputDir)是$(InputPath)的目录部分。环境变量$(InputName)是1.cs的文件名,不包括扩展名,其实就是1了。$(InputDir)\$(InputName).exe其实就是与1.cs在同一目录下的文件1.exe。
第二行命令为$(InputDir)\$(InputName).exe,其实就是编译结束后运行这个程序。
Outputs里为输出文件,这里就是$(InputDir)\$(InputName).exe了。
编译后,可以看到运行结果。
图5.7 编译VC#
可以猜测到Custom Build的编译步骤:
1、删除输出文件,这里就是1.exe;
2、设置当前目录为dsp所在目录;
3、设置环境变量;
4、调用Commands里面的命令。命令的输出全部被重定向至VC++6.0的Output窗口,这样就可以在IDE里查看编译结果。
5.4 预定义宏
对于VC++而言,预定义宏分为两大类:ANSI C 预定义宏、微软C++预定义宏。前者被绝大多数C++编译器支持,后者只被微软的VC++支持。
5.4.1 ANSI C 预定义宏
宏名 |
说明 |
示例 |
__DATE__ |
文件的修改日期(本地时间)。asctime的返回值 |
"Oct 31 2009" |
__FILE__ |
文件名,ANSI编码。编译时/FC控制是否含目录。 下面的__WFILE__将是Unicode编码的文件名 #define WIDEN2(x) L ## x #define WIDEN(x) WIDEN2(x) #define __WFILE__ WIDEN(__FILE__) |
"c:\\vcproj\\a.c" |
__LINE__ |
所在的行号,是10进制的整数。可以被#line预编译指令修改 |
5 |
__STDC__ |
表示编写的C代码必须符合ANSI C标准。不能编译C++代码。 |
|
__TIME__ |
文件的修改时刻(本地时间) |
"12:48:06" |
__TIMESTAMP__ |
文件的修改日期时刻(本地时间) |
"Sat Oct 31 12:48:06 2009" |
5.4.2 微软C++预定义宏
宏名 |
说明 |
_ATL_VER |
ATL版本 |
_CHAR_UNSIGNED |
char为无符号。指定/J后,该宏被定义 |
__CLR_VER |
CLR(common language runtime)版本,其格式为Mmmbbbbb M 是主版本号 mm 是次版本号 bbbbb 是编译版本号 |
__cplusplus_cli |
Defined when compiling with /clr, /clr:pure, or /clr:safe. Value of __cplusplus_cli is 200406. __cplusplus_cli is in effect throughout the translation unit. |
__COUNTER__ |
从0开始,出现一次增加1。可以用来产生读一无二的变量名: // pre_mac_counter.cpp #include #define FUNC2(x,y) x##y #define FUNC1(x,y) FUNC2(x,y) #define FUNC(x) FUNC1(x,__COUNTER__)
int FUNC(my_unique_prefix); int FUNC(my_unique_prefix); int main() { my_unique_prefix0 = 0; printf_s("\n%d",my_unique_prefix0); my_unique_prefix0++; printf_s("\n%d",my_unique_prefix0); } |
__cplusplus |
如果使用C++编译器,则该宏被定义 |
_CPPLIB_VER |
Defined if you include any of the C++ Standard Library headers; reports which version of the Dinkumware header files are present. |
_CPPRTTI |
编译开关/GR被打开时,该宏被定义,表示支持运行时类型信息Run-Time Type Information |
_CPPUNWIND |
Defined for code compiled with /GX (Enable Exception Handling). |
_DEBUG |
Defined when compiling with /LDd, /MDd, and /MTd. |
_DLL |
Defined when /MD or /MDd (Multithread DLL) is specified. |
__FUNCDNAME__ |
Valid only within a function and returns the decorated name of the enclosing function (as a string). __FUNCDNAME__ is not expanded if you use the /EP or /P compiler option. |
__FUNCSIG__ |
Valid only within a function and returns the signature of the enclosing function (as a string). __FUNCSIG__ is not expanded if you use the /EP or /P compiler option. On a 64-bit operating system, the calling convention is __cdecl by default. |
__FUNCTION__ |
Valid only within a function and returns the undecorated name of the enclosing function (as a string). __FUNCTION__ is not expanded if you use the /EP or /P compiler option. |
_INTEGRAL_MAX_BITS |
Reports the maximum size (in bits) for an integral type. |
_M_ALPHA |
Defined for DEC ALPHA platforms (no longer supported). |
_M_CEE |
Defined for a compilation that uses any form of /clr (/clr:oldSyntax, /clr:safe, for example). |
_M_CEE_PURE |
Defined for a compilation that uses /clr:pure. |
_M_CEE_SAFE |
Defined for a compilation that uses /clr:safe. |
_M_IX86 |
Defined for x86 processors. See Values for _M_IX86 for more details. _M_IX86 = 300 /G3 80386 _M_IX86 = 400 /G4 80486 _M_IX86 = 500 /G5 Pentium _M_IX86 = 600 /G6 Pentium Pro, Pentium II, and Pentium III _M_IX86 = 600 /GB Blend(Default. Future compilers will emit a different value to reflect the dominant processor.) |
_M_IA64 |
Defined for Itanium Processor Family 64-bit processors. |
_M_IX86_FP |
Expands to a value indicating which /arch compiler option was used: 0 if /arch was not used. 1 if /arch:SSE was used. 2 if /arch:SSE2 was used. See /arch (Minimum CPU Architecture) for more information. |
_M_MPPC |
Defined for Power Macintosh platforms (no longer supported). |
_M_MRX000 |
Defined for MIPS platforms (no longer supported). |
_M_PPC |
Defined for PowerPC platforms (no longer supported). |
_M_X64 |
Defined for x64 processors. |
_MANAGED |
Defined to be 1 when /clr is specified. |
_MFC_VER |
MFC的版本 0x0600 VC++6.0/EVC3.0/4.0 0x0700 VC++7.0(VC2002) 0x0710 VC++7.1(VC2003) 0x0800 VC++8.0(VC2005) 0x0900 VC++9.0(VC2008) |
_MSC_EXTENSIONS |
This macro is defined when compiling with the /Ze compiler option (the default). Its value, when defined, is 1. |
_MSC_VER |
VC++的版本 1100 VC++5.0 1200 VC++6.0/EVC3.0 1201 EVC4.0 1300 VC++7.0(VC2002) 1310 VC++7.1(VC2003) 1400 VC++8.0(VC2005) 1500 VC++9.0(VC2008) |
__MSVC_RUNTIME_CHECKS |
Defined when one of the /RTC compiler options is specified. |
_MT |
Defined when /MD or /MDd (Multithreaded DLL) or /MT or /MTd (Multithreaded) is specified. |
_NATIVE_WCHAR_T_DEFINED |
Defined when /Zc:wchar_t is used. |
_OPENMP |
Defined when compiling with /openmp, returns an integer representing the date of the OpenMP specification implemented by Visual C++. |
_VC_NODEFAULTLIB |
Defined when /Zl is used; see /Zl (Omit Default Library Name) for more information. |
_WCHAR_T_DEFINED |
Defined when /Zc:wchar_t is used or if wchar_t is defined in a system header file included in your project. |
_WIN32 |
Defined for applications for Win32 and Win64. Always defined. |
_WIN64 |
Defined for applications for Win64. |
_Wp64 |
Defined when specifying /Wp64. |
UNDER_CE _WIN32_WCE |
WinCE的版本 |
5.4.3 应用实例
下面的预处理指令将区分EVC++3.0、EVC++4.0、VC++6.0、VC++9.0
#ifdef _WIN32_WCE //智能设备
#if _MSC_VER==1200 //EVC3.0
#elif _MSC_VER==1201 //EVC4.0
#elif _MSC_VER==1500 //VC++9.0,即VC2008
#endif
#else
#if _MSC_VER==1200 //VC++6.0
#elif _MSC_VER==1500 //VC++9.0,即VC2008
#endif
#endif
5.5 预处理器操作符
5.5.1 #
增加双引号,如下面的预编译指令
#define STR(x) #x
编译时,STR(123)将被预处理器替换为 "123"
5.5.2 #@
增加单引号,如下面的预编译指令
#define CHR(x) #@x
编译时,CHR(A)将被预处理器替换为 'A'
5.5.3 ##
连接字符串,如下面的预编译指令
#define CAT(x,y) x##y
编译时,CAT(ABC,123) 将被预处理器替换为 ABC123
5.5.4 defined
必须与条件编译语句#if……#elif……#endif一起使用。
如:#ifdef _WIN32_WCE 与 #if defined(_WIN32_WCE) 等价。
如:#if defined(_WIN32_WCE) && _MSC_VER==1200 表示 EVC3.0 编译器。