打印
[牛人杂谈]

C语言预处理命令有哪些

[复制链接]
楼主: zhuomuniao110
手机看帖
扫描二维码
随时随地手机跟帖
21
598330983| | 2016-4-24 16:33 | 只看该作者 回帖奖励 |倒序浏览
好帖子,有意思,学的时候根本没有学到这些

使用特权

评论回复
22
orangebanana| | 2016-4-24 20:43 | 只看该作者
这些预处理命令如果处理好了会大大增强单片机的运行效率

使用特权

评论回复
23
643757107| | 2016-4-24 21:43 | 只看该作者
试图用宏开始或结束一段注释是不行的。

使用特权

评论回复
24
cnb12345| | 2016-4-25 08:39 | 只看该作者

使用特权

评论回复
25
cos12a| | 2016-4-26 12:36 | 只看该作者
本帖最后由 cos12a 于 2016-4-26 12:41 编辑

正好有一堆宏定义无法理解,还请赐教:如下:

#if defined(__GNUC__) || defined(__DOXYGEN__)
# include <avr/pgmspace.h>
# define PROGMEM_LOCATION(type, name, loc) \
                type name __attribute__((section (#loc)))
# define PROGMEM_DECLARE(type, name) const type name __attribute__((__progmem__))
# define PROGMEM_STRING(x) PSTR(x)
# define PROGMEM_STRING_T  PGM_P
# define PROGMEM_T const
# define PROGMEM_PTR_T const *
# define PROGMEM_BYTE_ARRAY_T uint8_t*
# define PROGMEM_WORD_ARRAY_T uint16_t*
# define PROGMEM_READ_BYTE(x) pgm_read_byte(x)
# define PROGMEM_READ_WORD(x) pgm_read_word(x)

#elif defined(__ICCAVR__)
# include <pgmspace.h>
# ifndef __HAS_ELPM__
#  define _MEMATTR_ASF  __flash
# else /* __HAS_ELPM__ */
#  define _MEMATTR_ASF  __hugeflash
# endif /* __HAS_ELPM__ */
# define PROGMEM_LOCATION(type, name, loc) const _MEMATTR_ASF type name @ loc
# define PROGMEM_DECLARE(type, name) _MEMATTR_ASF type name
# define PROGMEM_STRING(x) ((_MEMATTR_ASF const char *)(x))
# define PROGMEM_STRING_T  char const _MEMATTR_ASF *
# define PROGMEM_T const _MEMATTR_ASF
# define PROGMEM_PTR_T const _MEMATTR_ASF *
# define PROGMEM_BYTE_ARRAY_T uint8_t const _MEMATTR_ASF *
# define PROGMEM_WORD_ARRAY_T uint16_t const _MEMATTR_ASF *
# define PROGMEM_READ_BYTE(x) *(x)
# define PROGMEM_READ_WORD(x) *(x)
#endif直接就这样定认数据:
PROGMEM_DECLARE(char const, cdc_help[]) =
        "\r\n"
        "Welcome to the XMEGA-A3BU Xplained Demo CDC interface!\r\n"
        "\r\n"
        "Key bindings for LCD menu control:\r\n"
        "  Enter      : Enter\r\n"
        "  Backspace  : Back\r\n"
        "  Arrow Up   : Up\r\n"
        "  Arrow Down : Down\r\n";

这几句,真心不懂。
# define PROGMEM_LOCATION(type, name, loc) \
                type name __attribute__((section (#loc)))
# define PROGMEM_DECLARE(type, name) const type name __attribute__((__progmem__))


使用特权

评论回复
26
heisexingqisi| | 2016-4-28 21:37 | 只看该作者
只学过#define和#include
其他的只在头文件中看到过,不懂啊,多亏楼主的帖子,学习了。

使用特权

评论回复
27
zhuotuzi| | 2016-4-28 23:31 | 只看该作者
#define ERROR_POWEROFF -1
如果你在代码里不用ERROR_POWEROFF 这个宏而用-1,尤其在函数返回错误代码的时候(往往一个开发一个系统需要定义很多错误代码)。肯怕上帝都无法知道-1 表示的是什么意思吧。这个-1,我们一般称为“魔鬼数”,上帝遇到它也会发狂的。

使用特权

评论回复
28
zhuomuniao110|  楼主 | 2016-4-28 23:38 | 只看该作者
大家都常用的就是一个宏定义,预编译的其他很少用了,而头文件包含那个是必须的。

使用特权

评论回复
29
heisexingqisi| | 2016-4-29 16:59 | 只看该作者
#define SUM (x) (x)+(x)
这还是定义的宏函数SUM(x)吗?显然不是。编译器认为这是定义了一个宏:SUM,其代表的是(x) (x)+(x)。
原来是根据空格分界的。

使用特权

评论回复
30
gejigeji521| | 2016-4-29 21:46 | 只看该作者
预处理不就是include 和define么?
好多菜鸟都是这么认为的。

使用特权

评论回复
31
玛尼玛尼哄| | 2016-4-30 11:08 | 只看该作者
预编译又称为预处理,是做些代码文本的替换工作。是整个编译过程的最先做的工作。预编译又称为预处理,是做些代码文本的替换工作。

使用特权

评论回复
32
玛尼玛尼哄| | 2016-4-30 11:09 | 只看该作者
预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。常见的预编译指令有:
(1)#include 指令
该指令指示编译器将xxx.xxx文件的全部内容插入此处。若用<>括起文件则在系统的INCLUDE目录中寻找文件,若用" "括起文件则在当前目录中寻找文件。一般来说,该文件是后缀名为"h"或"cpp"的头文件。
注意:<>不会在当前目录下搜索头文件,如果我们不用<>而用""把头文件名扩起,其意义为在先在当前目录下搜索头文件,再在系统默认目录下搜索。
(2)#define指令
该指令有三种用法:
第一种是定义标识,标识有效范围为整个程序,形如#define XXX,常与#if配合使用;
第二种是定义常数,如#define max 100,则max代表100(这种情况下使用const定义常数更好,原因见注1);
第三种是定义"函数",如#define get_max(a, b) ((a)>(b)?(a):(b)) 则以后使用get_max(x,y)就可以得到x和y中较大的数(这种方法存在一些弊病,见注2)。
第四种是定义"宏函数",如#define GEN_FUN(type) type max_##type(type a,type b){return a>b?a:b;} ,使用时,用GEN_FUN(int),则此处预编译后就变成了 max_int(int a,int b){return a>b?a:b;},以后就可以使用max_int(x,y)就可以得到x和y中较大的数.比第三种,增加了类型的说明。
(3)#if、#else和#endif指令
这些指令一般这样配合使用:
#if defined(标识) //如果定义了标识
要执行的指令
#else
要执行的指令
#endif
在头文件中为了避免重复调用(比如说两个头文件互相包含对方),常采用这样的结构:
#if !(defined XXX) //XXX为一个在你的程序中唯一的标识符,
//每个头文件的标识符都不应相同。
//起标识符的常见方法是若头文件名为"abc.h"
//则标识为"abc_h"
#define XXX
真正的内容,如函数声明之类
#endif
注1:因为:const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误(边际效应)。
注2:例如get_max(a++, b)时,a++会被执行多少次取决于a和b的大小!所以建议还是用内联函数而不是这种方法提高速度。虽然有这样的弊病,但这种方法的确非常灵活,因为a和b可以是各种数据类型。
注3:可以查看百度百科的预处理命令,编排的比较简明。

使用特权

评论回复
33
玛尼玛尼哄| | 2016-4-30 11:27 | 只看该作者
#pragma C++
解析#pragma指令
在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
其格式一般为: #Pragma Para
其中Para 为参数,下面来看一些常用的参数。

(1)message 参数。 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗
口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:
#Pragma message(“消息文本”)
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_
X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了

使用特权

评论回复
34
玛尼玛尼哄| | 2016-4-30 11:28 | 只看该作者
(2)另一个使用得比较多的pragma参数是code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。
(3)#pragma once (比较常用)
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。
(4)#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。
有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。

使用特权

评论回复
35
玛尼玛尼哄| | 2016-4-30 11:31 | 只看该作者
(5)#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体
外观的定义。

(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等价于:
#pragma warning(disable:4507 34) // 不显示4507和34号警告信息
#pragma warning(once:4385) // 4385号警告信息仅报告一次
#pragma warning(error:164) // 把164号警告信息作为一个错误。
同时这个pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
这里n代表一个警告等级(1---4)。
#pragma warning( push )保存所有警告信息的现有的警告状态。
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告
等级设定为n。
#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的
一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。

使用特权

评论回复
36
玛尼玛尼哄| | 2016-4-30 11:33 | 只看该作者
(7)pragma comment(...)
该指令将一个注释记录放入一个对象文件或可执行文件中。
常用的lib关键字,可以帮我们连入一个库文件。

(8)#pragma pack()
我们知道在VC中,对于想结构体Struct这样的类型,VC采用8字节对齐的方式,如果我们不想使用8字节对齐(在网络变成中经常需要这样),我们可以在结构体前面加上
#pragma pack(1)
struct
{
......
}
#pragma pack( )

以下是另一个转载:

在vc6的时代头文件一般使用ifndef define endif
在vc7的时代头文件一般成了pragma once
不知道有没有人深究其中的意义
为什么有这样的代码,是为了头文件不被重复引用,那样编译器抱错的,这两种方法都是同样的目的,有没有区别呢?
还是举例来说明,可能有好几个库,每个库内部可能都有public.h这个文件,如果使用
ifndef public_h
define public_h
...
endif
那么当一个文件同时引用两个这样的库时,后一个库里的文件就不被编译了,而pragma once可以保证文件只被编译一次
看起来pragma once比ifndef define endif要好,那么ifndef define endif
的地方都pragma once好了。今天碰到了又一个例子,比如你有一个zlib.h在几个库都用到,而为了方便,把zlib每个目录下copy了一分,因为这个文件不会作修改,已经很完整了,这个时候如果使用pragma once,就会重复定义,看来ifndef define endif还是又派上用场的地方。
所以对于公有或者接口的文件,使用ifndef define endif,对于内部的文件使用pragma once.

#pragma once 与 #ifndef #define #endif 的区别

对于#pragma once,根据MSDN解说,能够防止一个文件被多次包含。与#ifndef #define #endif形式的文件保护相比,前者是平台相关的,可移植性比较差,但是它效率更高,因为它不需要去打开包含的文件,就可以判断这个文件有没有被包含。当然这个工作是系统帮我们完成的。
后者的优点在于它是语言相关的特性,所以可移植性好。但是在包含一个文件的时候,只有打开这个文件,根据文件的保护宏是否已经被定义来判断此文件是否已经被包含过。效率相对较低。当然在#i nclude的时候,程序员也可以自己判断所要包含的文件的保护宏是否已经被定义,来决定是否要包含这个文件。类似下面的代码:
#ifndef FILE_H_#i nclude "file.h"#endif这样作可以得到较高的效率,而且保证可移植性。但是文件之间的依赖性较高,如果一个文件的保护宏改变的话,所有使用如上形式包含这个文件的文件都要修改。有悖于模块化的思想。

使用特权

评论回复
37
玛尼玛尼哄| | 2016-4-30 11:35 | 只看该作者

#pragma data_seg用法总结
标签:杂谈   分类:编程

    Windows在一个Win32程序的地址空间周围筑了一道墙。通常,一个程序的地址空间中的数据是私有的,对别的程序而言是不可见的。但是执行STRPROG的多个执行实体表示了STRLIB在程序的所有执行实体之间共享数据是毫无问题的。当您在一个STRPROG窗口中增加或者删除一个字符串时,这种改变将立即反映在其它的窗口中。

在全部例程之间,STRLIB共享两个变量:一个字符数组和一个整数(记录已储存的有效字符串的个数)。STRLIB将这两个变量储存在共享的一个特殊内存区段中:

#pragma    data_seg ("shared")
int      iTotal = 0 ;

WCHAR    szStrings [MAX_STRINGS][MAX_LENGTH + 1] = { '\0' } ;
#pragma       data_seg ()      

第一个#pragma叙述建立数据段,这里命名为shared。您可以将这段命名为任何一个您喜欢的名字。在这里的#pragma叙述之后的所有初始化了的变量都放在shared数据段中。第二个#pragma叙述标示段的结束。对变量进行专门的初始化是很重要的,否则编译器将把它们放在普通的未初始化数据段中而不是放在shared中。

连结器必须知道有一个「shared」共享数据段。在「Project Settings」对话框选择「Link」页面卷标。选中「STRLIB」时在「Project Options」字段(在Release和Debug设定中均可),包含下面的连结叙述:

/SECTION:shared,RWS

字母RWS表示段具有读、写和共享属性。或者,您也可以直接用DLL原始码指定连结选项,就像我们在STRLIB.C那样:

#pragma comment(linker,"/SECTION:shared,RWS")
共享的内存段允许iTotal变量和szStrings字符串数组在STRLIB的所有例程之间共享。因为MAX_STRINGS等于256,而MAX_LENGTH等于63,所以,共享内存段的长度为32,772字节-iTotal变量需要4字节,256个指针中的每一个都需要128字节。

在Win16环境中,DLL的全局数据对每个载入它的进程来说都是相同的;而在Win32环境中,情况却发生了变化,DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。当进程在载入DLL时操作系统自动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个Dll的各进程之间共
享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。
#pragma data_seg预处理指令用于设置共享数据段。例如:
#pragma data_seg("SharedDataName")
HHOOK hHook=NULL;
#pragma data_seg()

在#pragma data_seg("SharedDataName")和#pragma data_seg()之间的所有变量将被访问该Dll的所有进程看到和共享。再加上一条指令

#pragma comment(linker,"/section:.SharedDataName,rws"),

那么这个数据节中的数据可以在所有DLL的实例之间共享。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。

   1.#pragma data_seg()一般用于DLL中。也就是说,在DLL中定义一个共享的,有名字的数据段。最关键的是:这个数据段中的全局变量可以被多个进程共享。否则多个进程之间无法共享DLL中的全局变量。

   2.共享数据必须初始化,否则微软编译器会把没有初始化的数据放到.BSS段中,从而导致多个进程之间的共享行为失败。

   3.你所谓的结果正确是一种错觉。如果你在一个DLL中这么写:

#pragma data_seg("MyData")

int g_Value; // Note that the global is not initialized.

#pragma data_seg()

DLL提供两个接口函数:

int GetValue()
{
     return g_Value;
}

void SetValue(int n)
{
     g_Value = n;
}

然后启动两个进程A和B,A和B都调用了这个DLL,假如A调用了SetValue(5); B接着调用int m = GetValue(); 那么m的值不一定是5,而是一个未定义的值。因为DLL中的全局数据对于每一个调用它的进程而言,是私有的,不能共享的。假如你对g_Value进行了初始化,那么g_Value就一定会被放进MyData段中。换句话说,如果A调用了SetValue(5); B接着调用int m = GetValue(); 那么m的值就一定是5!这就实现了跨进程之间的数据通信!


使用特权

评论回复
38
玛尼玛尼哄| | 2016-4-30 11:36 | 只看该作者
下面看一个实际应用,用共享数据来统计应用程序启动的次数,并作相应的处理。



在应用程序的入口处:
//控制应用程序只能启动一次
#pragma data_seg("flag_data")
   int count=0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:flag_data,RWS")

程序中:
   if(count>1)
     {
      MessageBox("已经启动了一个应用程序","Warning",MB_OK);
      return FLASE;
}
   count++;




Visual C++ 6.0编译指示收藏
新一篇: C++ 中的cast(显式类型转换) | 旧一篇: 看一个人是否快乐,不要看笑容
Document Source:

Pragma Directives, Preprocessor Reference, Visual C++ Programmer Guide.



每种C和C++的实现支持对其宿主机或操作系统唯一的功能。例如,一些程序需要精确控制超出数据所在的储存空间,或着控制特定函数接受参数的方式。#pragma指示使每个编译程序在保留C和C++语言的整体兼容性时提供不同机器和操作系统特定的功能。编译指示被定义为机器或操作系统特定的,并且通常每种编译程序是不同的。

语法:

#pragma token_string

“token_string”是一系列字符用来给出所需的特定编译程序指令和参数。数字符号“#”必须是包含编译指令的行中第一个非空白字符;而空白字符可以隔开数字符号“#”和关键字“pragma”。在#pragma后面,写任何翻译程序能够作为预处理符号分析的文本。#pragma的参数类似于宏扩展。

如果编译程序发现它不认得一个编译指示,它将给出一个警告,可是编译会继续下去。

为了提供新的预处理功能,或者为编译程序提供由实现定义的信息,编译指示可以用在一个条件语句内。C和C++编译程序可以识别下列编译程序指令。

alloc_text
comment
init_seg*
optimize

auto_inline
component
inline_depth
pack

bss_seg
data_seg
inline_recursion
pointers_to_members*

check_stack
function
intrinsic
setlocale

code_seg
hdrstop
message
vtordisp*

const_seg
include_alias
once
warning

使用特权

评论回复
39
玛尼玛尼哄| | 2016-4-30 11:47 | 只看该作者
22  setlocale
#pragma setlocale( "locale-string" )

定义用于翻译宽字符常数和字符串文字量时用的地区(国家和语言)。由于用于从多字节字符转换到宽字符的算法根据地区或者由于在运行可执行程序不同的地方进行编译而不同,这个编译指示提供一种在编译时指定目标地区的方式。这保证宽字符字符串将以正确的格式保存。默认的locale-string是“C”。“C”地区将字符串中的每个字符作为wchar_t(即unsigned int)映射其值。

23  vtordisp
C++特有

#pragma vtordisp({on | off} )

允许隐藏的附加vtordisp构造函数/析构函数替换成员。vtordisp编译指示仅能够用于具有虚拟基类的代码。如果派生类从一个虚拟基类重载了一个虚拟函数,并且如果派生类的构造函数或析构函数用指向虚拟基类的指针调用了这个函数,编译程序将根据虚拟基类在类中引入一个附加的隐藏“vtordisp”域。

vtodisp编译选项影响它后面的类布局。/vd0和/vd1选项为整个模块指定了相同的行为。指定off将禁止隐藏的vtordisp成员,指定on(默认)将在它们需要的时候允许vtordisp。仅在不可能出现类的构造函数和析构函数通过this指针调用其指向对象中的虚拟函数时才关闭vtordisp。

#pragma vtordisp( off )

class GetReal : virtual public { ... };

#pragma vtordisp( on )

24  warning
#pragma warning( warning-specifier : warning-number-list [,warning-specifier : warning-number-list...] )

#pragma warning( push[ , n ] )

#pragma warning( pop )

允许有选择地修改编译程序警告信息的行为。

warning-specifier能够是下列值之一:

warning-specifier
含义

once
只显示指定信息一次。

default
对指定信息应用默认的编译程序选项。

1,2,3,4
对指定信息引用给定的警告等级。

disable
不显示指定信息。

error
对指定信息作为错误显示。


warning-number_list能够包含任何警告编号。如下,在一个编译指示中可以指定多个选项:

#pragma warning( disable : 4507 34; once : 4385; error : 164 )

这等价于:

#pragma warning( disable : 4507 34 )  // Disable warning messages

                                            //  4507 and 34.

#pragma warning( once : 4385 )         // Issue warning 4385

                                            //  only once.

#pragma warning( error : 164 )         // Report warning 164

                                            //  as an error.

对于那些关于代码生成的,大于4699的警告标号,warning编译指示仅在函数定义外时有效。如果指定的警告编号大于4699并且用于函数内时被忽略。下面例子说明了用warning编译指示禁止、然后恢复有关代码生成警告信息的正确位置:

int a;

#pragma warning( disable : 4705 )

void func()

{

    a;

}

#pragma warning( default : 4705 )

warning编译指示也支持下面语法:

#pragma warning( push [ ,n ] )

#pragma warning( pop )

这里n表示警告等级(1到4)。

warning(push)编译指示保存所有警告的当前警告状态。warning(push,n)保存所有警告的当前状态并将全局警告等级设置为n。

warning(pop)弹出最后一次推入堆栈中的警告状态。任何在push和pop之间改变的警告状态将被取消。考虑下面的例子:

#pragma warning( push )

#pragma warning( disable : 4705 )

#pragma warning( disable : 4706 )

#pragma warning( disable : 4707 )

// Some code

#pragma warning( pop )

在这些代码的结束,pop恢复了所有警告的状态(包括4705,4706和4707)到代码开始时候的样子。

当你编写头文件时,你能用push和pop来保证任何用户修改的警告状态不会影响正常编译你的头文件。在头文件开始的地方使用push,在结束地方使用pop。例如,假定你有一个不能顺利在4级警告下编译的头文件,下面的代码改变警告等级到3,然后在头文件的结束时恢复到原来的警告等级。

#pragma warning( push, 3 )

// Declarations/ definitions

#pragma warning( pop )

使用特权

评论回复
40
玛尼玛尼哄| | 2016-4-30 11:49 | 只看该作者
三 编译选项的控制:

编译选项对于一些工程非常有效,可以控制多语言版本,多种编译版本,多种编译方式等。

选择菜单 Build->Configurations,增加一个工程配置,在Configuration中输入 Debug English 在 Copy Setting from 中选择 Debug 就可以(见下图),使用相同的方法,再增加一个 Debug Chinese 配置,并把原来的 Debug 删除。

选择菜单 Project->Settings,在左边的 Setting For 中选择 Debug Chinese 在 Generatl 属性页的 Intermediate files 中输入 Debug Chinese,在 Output files 中输入 Chinese。在 Resource 属性页的 Resource file name 中输入 Debug Chinese/Example_Ch.res
四 注意一些问题

#pragma once 与 #ifndef 解析  为了避免同一个文件被include多次(即防止编译多次),C/C++中有两种方式,一种是#ifndef方式,一种是#pragma once方式。在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别。
    方式一:

    #ifndef __SOMEFILE_H__
    #define __SOMEFILE_H__
    ... ... // 声明、定义语句
    #endif


    方式二:


    #pragma once
    ... ... // 声明、定义语句

    #ifndef的方式受C/C++语言标准支持。它不光可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件(或者代码片段)不会被不小心同时包含。
    当然,缺点就是如果不同头文件中的宏名不小心“撞车”,可能就会导致你看到头文件明明存在,编译器却硬说找不到声明的状况——这种情况有时非常让人抓狂。
    由于编译器每次都需要打开头文件才能判定是否有重复定义,因此在编译大型项目时,ifndef会使得编译时间相对较长,因此一些编译器逐渐开始支持#pragma once的方式。

    #pragma once一般由编译器提供保证:同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。你无法对一个头文件中的一段代码作pragma once声明,而只能针对文件。
    其好处是,你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。大型项目的编译速度也因此提高了一些。
    对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。当然,相比宏名碰撞引发的“找不到声明”的问题,这种重复包含很容易被发现并修正。

    #pragma once方式产生于#ifndef之后,因此很多人可能甚至没有听说过。目前看来#ifndef更受到推崇。因为#ifndef受C/C++语言标准的支持,不受编译器的任何限制;而#pragma once方式却不受一些较老版本的编译器支持,一些支持了的编译器又打算去掉它,所以它的兼容性可能不够好。一般而言,当程序员听到这样的话,都会选择#ifndef方式,为了努力使得自己的代码“存活”时间更久,通常宁愿降低一些编译性能,这是程序员的个性,当然这是题外话啦。

    还看到一种用法是把两者放在一起的:

    #pragma once
    #ifndef __SOMEFILE_H__
    #define __SOMEFILE_H__
    ... ... // 声明、定义语句
    #endif

    看起来似乎是想兼有两者的优点。不过只要使用了#ifndef就会有宏名冲突的危险,也无法避免不支持#pragma once的编译器报错,所以混用两种方法似乎不能带来更多的好处,倒是会让一些不熟悉的人感到困惑。

    选择哪种方式,应该在了解两种方式的情况下,视具体情况而定。只要有一个合理的约定来避开缺点,我认为哪种方式都是可以接受的。而这个已经不是标准或者编译器的责任了,应当由程序员自己或者小范围内的开发规范来搞定。

    btw:我看到GNU的一些讨论似乎是打算在GCC 3.4(及其以后?)的版本取消对#pragma once的支持。不过事实上,我手上的GCC 3.4.2和GCC 4.1.1仍然支持#pragma once,甚至没有deprecation warning,倒是GCC2.95会对#pragma once提出warning。
    VC6及其以后版本亦提供对#pragma once方式的支持,这一特性应该基本稳定下来了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则