`

#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, 别相信我, 去验证一下.

相关推荐

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

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

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

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

    #pragma预处理指令详解

    这一点与#ifndef预处理指令有着本质的区别。#ifndef依赖于预处理宏的定义,而#pragma once是基于磁盘文件的,因此更为高效。使用#pragma once可以避免多重包含头文件可能导致的重定义错误。 ### #pragma warning #...

    #pragma_命令集合

    这里`#pragma code_seg(push, r1, ".my_data2")`创建了一个新的记录`r1`,并将其与`.my_data2`关联起来;而`#pragma code_seg(pop)`则会从内部编译器的堆栈中移除最后一个`push`操作创建的记录。 #### 四、#pragma ...

    pragma once的用法用的地方以及技巧什么的

    ### 关于 `#pragma once` 的使用方法与技巧 #### 一、`#pragma once` 概述 `#pragma once` 是一个预处理器指令,主要用于C++编程中,尤其是在包含头文件时防止重复包含同一份头文件。尽管C++标准并没有明确规范 `#...

    #pragma_命令集合.pdf

    下面,我们将深入探讨几个常见的 `#pragma` 指令,并详细解释它们的功能与用途。 #### 1. #pragma message 指令 `#pragma message` 指令可以在编译过程中向编译器输出一条信息,这对于调试和源代码管理特别有用。...

    #pragma 预处理指令详解

    `#pragma once`是一个非常实用的指令,它可以确保包含该指令的头文件在整个编译单元中仅被包含一次。这相当于使用传统的`#ifndef`、`#define`和`#endif`组合来避免重复包含问题,但在现代IDE中更受欢迎且易于阅读。 ...

    #pragma预处理命令整理

    与传统的`#ifndef`/`#define`/`#endif`相比,`#pragma once`更简洁且易于维护。 ##### 3. `#pragma warning` **用途**:控制编译器的警告信息,可以用来禁用某些警告或调整警告等级。 - **示例**: ```cpp #...

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

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

    Duanxx的C++学习:预处理指令 #pragma

    由于`basic.h`还包含了`#pragma once`指令,这确保了即使`basic.h`被多个源文件包含,该消息也只会被输出一次。 #### 2\. `#pragma once` `#pragma once` 是一种广泛使用的预处理指令,它的主要功能是确保一个...

    Pragma用法.doc

    这与`#ifndef`、`#define`、`#endif`的include guard机制有相同的效果,但`#pragma once`更简洁,且通常被认为效率更高。例如: ```cpp #if _MSC_VER &gt; 1000 #pragma once #endif ``` 上述代码仅在Microsoft...

    externC_ifndef_define_endif_pragma onceDemo.zip

    `#pragma once` 是一种非标准的编译器特性,其效果与 `#ifndef`、`#define`、`#endif` 相似,但更简洁。它指示编译器如果已经处理过当前的头文件,就不再继续处理。这个指令对大多数现代编译器都有效,但不是C++...

    c++预编译命令 (2).pdf

    虽然大多数现代编译器支持#include卫士(如`#ifndef`、`#define`和`#endif`),但`#pragma once`提供了一种更简洁的方式,尤其是在Visual C++中。 4. **#pragma hdrstop** 当使用预编译头文件时,`#pragma hdrstop...

    c++预处理指令

    1. #pragma once:保证所在文件只会被包含一次,它是基于磁盘文件的,而#ifndef则是基于宏的。 2. #pragma warning:允许有选择性的修改编译器的警告消息的行为。例如,#pragma warning(disable:4507 34)可以屏蔽...

    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...

Global site tag (gtag.js) - Google Analytics