#pragma once与#ifndef #define #endif的区别 (比较全)

来源:互联网 发布:pageoffice java 教程 编辑:程序博客网 时间:2024/05/16 15:38

摘要: 一、相同点        两者的共同点都是为了避免同一个文件被 include 多次,但是 #ifndef #define #endif 不只有这个作用。 在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别。

一、相同点 
       两者的共同点都是为了避免同一个文件被 include 多次,但是 #ifndef #define #endif 不只有这个作用。 
在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别。 
  
二、收集理解 
1.#pragma once 
       这个是编译器相关,就是说在这个编译系统上能用,在其他编译系统不一定行,即移植性差。不过现在基本上已经是每个编译器都有这个定义了。 
       此方式由编译器保证同一个文件不会被包含多次。注意:这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。于是不必再费劲想个宏名了,当然也就可以避免宏的名字冲突问题了。 

缺点:如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。 
举例:在一般的 MFC 程序中可以看到 
?
1
2
3
4
5
6
7
8
9
10
11
#if !defined(AFX_STDAFX_H__32722022_E372_4A5C_8EC5_BBB243CEDE1D__INCLUDED_)
#define AFX_STDAFX_H__32722022_E372_4A5C_8EC5_BBB243CEDE1D__INCLUDED_
 
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
 
// TODO: reference additional headers your program requires here
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_STDAFX_H__32722022_E372_4A5C_8EC5_BBB243CEDE1D__INCLUDED_)

其中 _MSC_VER 分解如下:  
MS:Microsoft(微软)的简写  
C:MSC 就是 Microsoft 出的 C 编译器  
VER:Version(版本)的简写 

#if _MSC_VER > 1000 的意思就是如果编译器版本高于 1000(VC++5.0) 
可以看到:在 _MSC_VER 小于 1000 时,它对 #pragma once 是不支持的。 

2.#ifndef #define #endif 
       该方法与 C++ 语言相关,是 C++ 语言中的宏定义,通过宏定义避免文件多次编译。所以在所有支持 C++ 语言的编译器上都是有效的。如果写的程序要跨平台,最好使用这种方式。该方式由于是 C++ 语言本身支持,所以移植性好。它依赖于宏名字不能冲突,这不光可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件不会被不小心同时包含。另外,为了保证不同头文件中的宏名不冲突,故采取类似于_ABC_H_的取名方式。其中,abc.h为当前头文件名。 
  
举例:常常在一些头中可以看到 
?
1
2
3
4
#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__ 
// 一些声明语句
#endif

缺点:如果不同头文件的宏名不小心“撞车”,可能就会导致头文件明明存在,编译器却硬说找不到声明的状况。但这个缺点恰恰是我们可以利用的优点,#ifndef 方式可以通过前面介绍的特殊的宏的取名方式来避免名称冲突问题,于是其缺点也就不复存在了,进而 #ifndef 方式就更常用了。 

三、相比之下 
1.性能上的区别 
       使用 #ifndef 的话,编译器每次看到 #include 这个文件都需要读入文件,解析代码; 而使用 #pragma once 编译器根本不会重复打开文件, 大大提高了效率。 

2.编码风格上的区别 
       使用 #pragma once 的代码简洁,显然比 #ifndef 要简短许多,重要的是它避免了头文件标号(如 __myheader_h__ )的重定义或者 #endif 包含范围错误的情况。 

3.语意上的区别 
#pragma once 是针对文件的,它告诉编译器,本文件只编译一次。 
#ifndef #define #endif 只是针对文件中的某一个标号而言的,它能用于防止三个指令间所包含内容的重复性处理。就这一点而言,后者更灵活。 

4.可移植性方面 
#pragma once 是微软的开发工具中所使用的,如 .net,vc6 等工具可以完好的支持; 
#ifndef #define #endif 是标准里面的一部分,所以对于任何完好支持 C/C++ 的编译器都能使用。显而易见,后者的可移植性更高。  

四、引用通告 

在总结的过程中,看了一些网页,也引用到其中的一些内容,现给出链接,这里仅供本人学习,谢谢引用到的作者。 

四、引用通告 

在总结的过程中,看了一些网页,也引用到其中的一些内容,现给出链接,这里仅供本人学习,谢谢引用到的作者。 


原文地址:http://pppboy.blog.163.com/blog/static/3020379620106130528324/

文章二:

来自:http://blog.csdn.net/hkx1n/article/details/4313357

为了避免同一个文件被include多次,C/C++中有两种方式,一种是#ifndef方式,一种是#pragma once方式。在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别。
    方式一:
    #ifndef __SOMEFILE_H__
    #define __SOMEFILE_H__
    ... ... // 声明、定义语句
    #endif


    方式二:

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

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

    如 A/C.h  B/C.h  B/D.cpp

[plain] view plain copy
 print?
  1. A/C.h  class C{}  
  2. B/C.h  class C{}  
  3. D.cpp    
  4.    #include "A/C.h"  
  5.    如果D.cpp还存在其他头文件依赖B/C.h  
  6.    那么就会出现class C redefination  


     这时候 #pragma once 不能保证内容相同的头文件被包含;但是  #ifndef可以;

     当然,缺点就是如果不同头文件中的宏名不小心“撞车”,可能就会导致你看到头文件明明存在,编译器却硬说找不到声明的状况——这种情况有时非常让人抓狂。
    由于编译器每次都需要打开头文件才能判定是否有重复定义,因此在编译大型项目时,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方式的支持,这一特性应该基本稳定下来了。



0 0
原创粉丝点击