`

#pragma once与 #ifndef的区别

阅读更多

今年招的本科生都在实习, 最近他们在准备毕业论文, 论文用C++, 于是其中有位牛人(确实很牛, 今天上午他刚查完日语能力考的成绩, 1级过了, 恭喜一下~)问了我这个问题:#pragma once与 #ifndef的区别

 

以前还真没注意pragma,他是编译指示, 从pragmatic来的?

 

他们都是用来防止同一个文件被include了多次,  调查了一下整理如下:

 

#ifndef方式:

 

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

 


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


#pragma方式

 

 

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


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

 

 

总结:

1. #ifndef 由语言支持所以移植性好,#pragma 可以避免名字冲突

2. 调查一下<stdlib.h>和<iostream>等标准库, 用得都是#ifndef, 我个人推荐这种方式.

 

分享到:
评论
2 楼 wjason 2010-01-05  
TO: slimzhao

你的回复果然有深度,让我受益不少,thx 

关于两个下滑线的问题。
我查了一下cpp2003,里面是这么写道的(注:起初我以为标准会在,第二章中关于Identifiers和Keywords的地方,讨论这一问题,结果不是)。

引用

17.4.3.1.2 Global names [lib.global.names]

1 Certain sets of names and function signatures are always reserved to the implementation:
— Each name that contains a double underscore (_ _) or begins with an underscore followed by an uppercase letter (2.11) is reserved to the implementation for any use.

— Each name that begins with an underscore is reserved to the implementation for use as a name in the
global namespace.165)



c99中,也有关于这一个问题的讨论
引用

7.1.3 Reserved identifiers
1 Each header declares or defines all identifiers listed in its associated subclause, and optionally declares or defines identifiers listed in its associated future library directions subclause and identifiers which are always reserved either for any use or for use as file scope identifiers.

— All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
— All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.
— Each macro name in any of the following subclauses (including the future library directions) is reserved for use as specified if any of its associated headers is included; unless explicitly stated otherwise (see 7.1.4).
— All identifiers with external linkage in any of the following subclauses (including the future library directions) are always reserved for use as identifiers with external linkage.154)
— Each identifier with file scope listed in any of the following subclauses (including the future library directions) is reserved for use as a macro name and as an identifier with file scope in the same name space if any of its associated headers is included.

2 No other identifiers are reserved. If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined.

3 If the program removes (with #undef) any macro definition of an identifier in the first group listed above, the behavior is undefined.



另:
http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier
1 楼 slimzhao 2010-01-01  
同意第一条总结:
说的更直白点: pragma once 是VC编译器的做法, 虽然#pragma 本身是编译器之间的事实标准.

关于第二条, 我个人也更喜欢 #ifndef, 但是对于VC程序, 使用pragma有另一个好处:
它可以真正避免compiler 读取一个头文件, #ifndef或许不能, 一个隐含的问题是: 预编译器还是要把头文件从头读到尾. 目的仅仅是为了发现结束的 #endif

这在<<C++ common knowledge>>中有提到. 所以有建议使用外部 #ifndef方法, 也就是在.cpp文件中使用
#ifndef __SOMEFILE_H__
#include "somefile.h"
#endif

但这个方法有个严重的不便, 你必需保证__SOMEFILE_H__这个符号的同步, 否则会出严重问题.

所以更新的C++规范, 如Hurb Sutter的书中, 又再次建议不要使用这种方法. 回归到头文件开始放一个 #ifndef, 结尾放一个 #endif, 说现在的编译器已经聪明到可以真正避免去读取该文件了.

验证这一点的方法, 对于VC, 可以打开/showincludes, 对于gcc, 我猜想也有一个选项, 但不知道是什么, 如果是在windows上, 还可以用filemon这样的工具监控是否真有文件操作.

结论还是一样, 我也是推荐#ifndef, 至于宏名字冲突, 我在现实中基本没碰到过, 如果真的担心, 可以用stdafx.h中的一个技巧, 用uuidgen生成一个UUID, 就保险了.

另外, __SOMEFILE_H__是不妥的
* 开头为_的标识符是编译器保留的
* 即使不在开头出现, 连续的两个_也是编译器保留的, 这一点比第一点更少有人会注意, 我记得你的blog中提到你有C++ 标准 PDF, 别相信我, 去验证一下.

相关推荐

    全面了解#pragma once与 #ifndef的区别

    2 #pragma once方式 在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别。 方式一: #ifndef __SOMEFILE_H__ #define __SOMEFILE_H__ … … // 一些声明语句 #endif 方式二...

    C语言头文件避免重复包含、#pragma once和#ifndef区别

    一般情况下,我们都是把函数声明、类定义、模板定义等写到一个头文件里,需要时将相应的头文件用#include包含到源文件(*.cpp文件)里来。但头文件中又允许包含其它的头文件,这样就难免发生某个头文件被重复地包含...

    C++ 中pragma once 与 #ifndef _XXX_H_ #define _XXX_H_的区别

    C++ 中pragma once 与 #ifndef _XXX_H_ #define _XXX_H_的区别 pragma once 之前一直用的很好,今天和同事的配合中发现自己没有真正理解pragma once。 原因:同事喜欢把公共的头文件通过生成后事件复制到一个公共的...

    C++EGE: Ballon Ball 动画

    #pragma once #endif #ifndef __cplusplus #error You must use C++ compiler, or you need filename with '.cpp' suffix #endif #if defined(_INC_CONIO) || defined(_CONIO_H_) #error can not include "conio.h...

    C语言/C++常见笔试面试题难疑点汇总

    #pragma once。保证所在文件只会被包含一次,它是基于磁盘文件的,而#ifndef则是基于宏的。 当类不包含任何成员的时候,大小本该是0,但是为了便于区分,大小是1 .c是标准C程序文件名的后缀;.cpp则是C++程序文件名...

    运动会分数统计 C++

    #pragma once #endif #ifndef _INC_EH #define _INC_EH #if !defined(_WIN32) && !defined(_MAC) #error ERROR: Only Mac or Win32 targets supported! #endif #ifdef _MSC_VER // Currently, all MS C ...

    扫雷的源代码(C++语言版)

    #pragma once #include &lt;afxwin.h&gt; //mine struct struct Mine { UINT i;//纵坐标 Y UINT j;//横坐标 X UINT ID;//格子iD 0~9 iD 10 标示地雷 区别于其他ID UINT flag;//格子标志 问号 旗子 BOOL bMine;//...

    语音信号四特征四状态端点检测系统源代码1

    // stdafx.h : 标准系统包含文件的包含文件,// 或是经常使用但不常更改的// 特定于项目的包含文件#pragma once#ifndef _SEC

    PIC 芯片驱动 8563时钟的程序

    //#pragma once #ifndef _clock_8563_pic24FJ #define _clock_8563_pic24FJ #define SDA_OUT LATDbits.LATD9 #define SDA_IN PORTDbits.RD9 #define SDA_DDR TRISDbits.TRISD9 #define SCK ...

    HK StreamMediaPlayer DEMO

    浙江海康威视硬盘录像机播放器DEMO / stdafx.h : 标准系统包含文件的包含文件, // 或是经常使用但不常...#pragma once #ifndef VC_EXTRALEAN #define VC_EXTRALEAN // 从 Windows 标头中排除不常使用的资料 #endif

    C++选择文件夹代码的封装

    代码如下:#pragma once  #ifndef __DIRDIALOG_H_HH  #define __DIRDIALOG_H_HH  #include   class CDirDialog  {  protected:   BROWSEINFO m_bi;   char m_szDisplayName[MAX_PATH]; //显示名称   

    C++实现循环队列

    #pragma once #pragma once #ifndef CIRCULARQUEUE_H #define CIRCULARQUEUE_H #include #include using std::cout; using std::cin; using std::endl; using std::ostream; template&lt;class&gt; class cirQueue; ...

    C语言 时钟

    #pragma once #endif // _MSC_VER &gt; 1000 #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers #include &lt;afxwin.h&gt; // MFC core and standard components #include &lt;afxext.h&gt; // MFC ...

    listview_5.zip

    #pragma once #endif // _MSC_VER &gt;= 1000 #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers #include &lt;afxwin.h&gt; // MFC core and standard components #include &lt;afxext.h&gt; // MFC ...

    XPButton控件

    #pragma once #endif // _MSC_VER &gt;= 1000 // Return values #ifndef BTNST_OK #define BTNST_OK 0 #endif #ifndef BTNST_INVALIDRESOURCE #define BTNST_INVALIDRESOURCE 1 #endif #ifndef BTNST_FAILEDMASK #...

    C++通过类实现控制台贪吃蛇

    #pragma once #ifndef SNACK_H #define SNACK_H #define Frame_X 5//边框起点横坐标 #define Frame_Y 5//边框起点纵坐标 #define Frame_width 50//边框长度 #define Frame_height 25//边框高度 struct point//蛇个点...

    VS2010+Opencv+MFC读取图像和视频显示在Picture控件

    VS2010+Opencv+MFC读取图像和视频显示在Picture控件,供大家参考,具体内容如下 1.新建MFC对话框应用程序。 其余选项默认,单击完成,创建出...#pragma once #ifndef CVVIMAGE_CLASS_DEF #define CVVIMAGE_CLASS_DEF #i

    vc StdAfx头文件

    #pragma once #endif // _MSC_VER &gt; 1000 #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers #include &lt;afxwin.h&gt; // MFC core and standard components #include &lt;afxext.h&gt; // MFC ...

Global site tag (gtag.js) - Google Analytics