为了避免同一个文件被include多次

摘自http://www.cppblog.com/szhoftuncun/archive/2007/10/28/35356.html

 

为了避免同一个文件被include多次

1   #ifndef方式
2   #pragma once方式

在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别。
    方式一:

    #ifndef __SOMEFILE_H__
    #define __SOMEFILE_H__
    ... ... // 一些声明语句
    #endif

    方式二:

    #pragma once
    ... ... // 一些声明语句


    #ifndef的方式依赖于宏名字不能冲突,这不光可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件不会被不小心同时包含。当然,缺点就是如果不同头文件的宏名不小心“撞车”,可能就会导致头文件明明存在,编译器却硬说找不到声明的状况

    #pragma once则由编译器提供保证:同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。带来的好处是,你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。当然,相比宏名碰撞引发的“找不到声明”的问题,重复包含更容易被发现并修正。

   方式一由语言支持所以移植性好,方式二 可以避免名字冲突

 

 

 

 

摘自http://blog.csdn.net/ghost5216/article/details/7083947

 

#pragma once的使用 摘自vivi'smile的博客

 

 

 

 

pragma指令简介

在编写程序的时候,我们经常要用到#pragma指令来设定编译器的状态或者是指示编译器完成一些特定的动作。

一.message参数

message它能够在编译消息输出窗口中输出相应的消息,这对于源代码信息的控制非常重要的,使用方法为:

#pragma message(“消息文本”)

当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的候进行检查,假设我们希望判断自己有没有源代码的什么地方定义了_X86这个宏可以用下面的方法:

#ifdef _x86

#pragma message("_x86 macro activated!")

#endif

当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示"_x86 macro activated!"。

二. 另一个使用得比较多的#pragma参数是code_seg。格式如:

#pragma code_seg([[{push|pop},][identifier,]]["segment-name"][,"segment-class"])

该指令用来指定函数在.obj文件中存放的节,观察OBJ文件可以使用VC自带的dumpbin命令行程序,函数在.obj文件中默认的存放节 
为.text节 
如果code_seg没有带参数的话,则函数存放在.text节中 
push (可选参数) 将一个记录放到内部编译器的堆栈中,可选参数可以为一个标识符或者节名 
pop(可选参数) 将一个记录从堆栈顶端弹出,该记录可以为一个标识符或者节名 
identifier (可选参数) 当使用push指令时,为压入堆栈的记录指派的一个标识符,当该标识符被删除的时候和其相关的堆栈中的记录将被弹出堆栈 
"segment-name" (可选参数) 表示函数存放的节名 
例如: 
//默认情况下,函数被存放在.text节中 
void func1() { // stored in .text 


//将函数存放在.my_data1节中 
#pragma code_seg(".my_data1") 
void func2() { // stored in my_data1 


//r1为标识符,将函数放入.my_data2节中 
#pragma code_seg(push, r1, ".my_data2") 
void func3() { // stored in my_data2 


int main() { 

三. #pragma once(比较常用)

这是一个比较常用的指令,只要在头文件的最开始加入这条指令就能够保证头文件被编译一次

#pragma once用来防止某个头文件被多次include,#ifndef,#define,#endif用来防止某个宏被多次定义。

#pragma once是编译相关,就是说这个编译系统上能用,但在其他编译系统不一定可以,也就是说移植性差,不过现在基本上已经是每个编译器都有这个定义了。

#ifndef,#define,#endif这个是C++语言相关,这是C++语言中的宏定义,通过宏定义避免文件多次编译。所以在所有支持C++语言的编译器上都是有效的,如果写的程序要跨平台,最好使用这种方式

四. #pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。 

BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。 
有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级, 
如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。 



五. #pragma warning指令 

该指令允许有选择性的修改编译器的警告消息的行为 


指令格式如下: 
#pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list...] 
#pragma warning( push[ ,n ] ) 
#pragma warning( pop ) 

主要用到的警告表示有如下几个: 

once:只显示一次(警告/错误等)消息 
default:重置编译器的警告行为到默认状态 
1,2,3,4:四个警告级别 
disable:禁止指定的警告信息 
error:将指定的警告信息作为错误报告 

如果大家对上面的解释不是很理解,可以参考一下下面的例子及说明 

#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) 

在使用标准C++进行编程的时候经常会得到很多的警告信息,而这些警告信息都是不必要的提示, 
所以我们可以使用#pragma warning(disable:4786)来禁止该类型的警告 

在vc中使用ADO的时候也会得到不必要的警告信息,这个时候我们可以通过 
#pragma warning(disable:4146)来消除该类型的警告信息 




六. pragma comment(...) 
该指令的格式为 
#pragma comment( "comment-type" [, commentstring] ) 


该指令将一个注释记录放入一个对象文件或可执行文件中, 
comment-type(注释类型):可以指定为五种预定义的标识符的其中一种 
五种预定义的标识符为: 

compiler:将编译器的版本号和名称放入目标文件中,本条注释记录将被编译器忽略 
如果你为该记录类型提供了commentstring参数,编译器将会产生一个警告 
例如:#pragma comment( compiler ) 

exestr:将commentstring参数放入目标文件中,在链接的时候这个字符串将被放入到可执行文件中, 
当操作系统加载可执行文件的时候,该参数字符串不会被加载到内存中.但是,该字符串可以被 
dumpbin之类的程序查找出并打印出来,你可以用这个标识符将版本号码之类的信息嵌入到可 
执行文件中! 

lib:这是一个非常常用的关键字,用来将一个库文件链接到目标文件中 


常用的lib关键字,可以帮我们连入一个库文件。 
例如: 
#pragma comment(lib, "user32.lib") 
该指令用来将user32.lib库文件加入到本工程中 


linker:将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或者在开发环境中 
设置的链接选项,你可以指定/include选项来强制包含某个对象,例如: 
#pragma comment(linker, "/include:__mySymbol") 

你可以在程序中设置下列链接选项 

/DEFAULTLIB 
/EXPORT 
/INCLUDE 
/MERGE 
/SECTION 
这些选项在这里就不一一说明了,详细信息请看msdn! 

user:将一般的注释信息放入目标文件中commentstring参数包含注释的文本信息,这个注释记录将被链接器忽略 
例如: 
#pragma comment( user, "Compiled on " __DATE__ " at " __TIME__ ) 


补充一个 



#pragma pack(n) 

控制对齐 如 


#pragma pack(push) 
#pragma pack(1) 
struct s_1{ 
char szname[1]; 
int a; 
}; 
#pragma pack(pop) 
struct s_2{ 
char szname[1]; 
int a; 
}; 
则 


printf("s_1 size : %d\n", sizeof(struct s_1)); 
printf("s_2 size : %d\n", sizeof(struct s_2)); 
得到5,8。

 

 

 

摘自http://www.cnblogs.com/Braveliu/archive/2012/12/29/2838726.html

【1】#pragma once这个宏有什么作用?

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

【2】两者的使用方式有何区别?

示例代码如下:

复制代码

1 //方式一:
2 #ifndef   __SOMEFILE_H__
3 #define   __SOMEFILE_H__
4 ... ... // 声明、定义语句
5 #endif
6 
7 //方式二:
8 #pragma once
9 ... ... // 声明、定义语句

复制代码

【3】两者各有何特点?

(1)#ifndef

  #ifndef的方式受C/C++语言标准支持。它不仅可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件(或者代码片段)不会被不小心同时包含。

  当然,缺点就是如果不同头文件中的宏名不小心“撞车”,可能就会导致你看到头文件明明存在,但编译器却硬说找不到声明的状况——这种情况有时非常让人郁闷。

  由于编译器每次都需要打开头文件才能判定是否有重复定义,因此在编译大型项目时,ifndef会使得编译时间相对较

  长,因此一些编译器逐渐开始支持#pragma once的方式。

(2)#pragma once

  #pragma once 一般由编译器提供保证:同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。

  你无法对一个头文件中的一段代码作pragma once声明,而只能针对文件。

  其好处是,你不必再担心宏名冲突了,当然也就不会出现宏名冲突引发的奇怪问题。大型项目的编译速度也因此提高了一些。

  对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。当然,相比宏名冲突引发的“找不

  到声明”的问题,这种重复包含很容易被发现并修正。

【4】两者之间有什么联系?

 #pragma once 方式产生于#ifndef之后,因此很多人可能甚至没有听说过。目前看来#ifndef更受到推崇。因为#ifndef受C/C++语言标准的支持,不受编译器的任何限制;

 而#pragma once方式却不受一些较老版本的编译器支持,一些支持了的编译器又打算去掉它,所以它的兼容性可能不够好。

 一般而言,当程序员听到这样的话,都会选择#ifndef方式,为了努力使得自己的代码“存活”时间更久,通常宁愿降低一些编译性能,这是程序员的个性,当然这是题外话啦。

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

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

  看起来似乎是想兼有两者的优点。不过只要使用了#ifndef就会有宏名冲突的危险,也无法避免不支持#pragma once的编译器报错,所以混用两种方法似乎不能带来

  更多的好处,倒是会让一些不熟悉的人感到困惑。

  选择哪种方式,应该在了解两种方式的情况下,视具体情况而定。只要有一个合理的约定来避开缺点,我认为哪种方

  式都是可以接受的。而这个已经不是标准或者编译器的责任了,应当由程序员自己或者小范围内的开发规范来搞定。

 

 

 

 

====2019-03-27====================================================================================== 

解析#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!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了 

。 

(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就会根据优先级的大小先后编译。 

(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)。 

(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

这样作可以得到较高的效率,而且保证可移植性。但是文件之间的依赖性较高,如果一个文件的保护宏改变的话,所有使用如上形式包含这个文件的文件都要修改。有悖于模块化的思想。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/roger_it/archive/2007/02/09/1506249.aspx
--------------------- 
作者:hkx1n 
来源:CSDN 
原文:https://blog.csdn.net/hkx1n/article/details/4313357 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值