关于头文件定义中#ifdef,#define,#endif和#pragma once的区别

用VS的编译器,发现自动生成的代码头文件中经常会加上一个#pragma once的宏,查阅msdn后发现解释为:Specifies that the file will be included (opened) only once by the compiler when compiling a source code file. 大概的含义即使防止在头文件中发生重定义的情况。尤其是在面向对象的头文件中,在头文件中声明类,加入这种编译标记,可以避免一些不必要的错误。

 

但是很奇怪微软提供的这个宏和C语言本身的#ifdef,#define,#endif有什么区别呢?难道仅仅是省去了两行代码么?网上有一些关于此问题分析的帖子,但看后总感觉似是而非,于是自己亲手实验一下。

 

具体文件定义如下:

 

//tst.cpp

 

#include "stdafx.h"
#include
#include "b.h"
#include "c.h"

int _tmain(int argc, _TCHAR* argv[]){

 print_c();
 print_b();

 return 0;
}

 

//c.h:

 

//#ifdef _C_H_
//#define _C_H_
//#pragma once
void print_c();

struct a{
 int s;
};

class b{
 int s;
};

//int b;

//#endif

 

//c.cpp:

 

#include "stdafx.h"

void print_c(){
 printf("i'm in c/n");
}

 

//b.h:

 

#include "c.h"
void print_b();

 

//b.cpp:

 

#include "stdafx.h"
#include "c.h"


void print_b(){
 print_c();
}

 

程序故意在tst.cpp中多include进一次c.h,这样c.h中的类和结构体的类型声明将会重复,运行后报error C2011: 'a' : 'struct' type redefinition 错误,这是在意料之中的。(在此,可以发现函数声明的重复定义编译器并不会报错!这之后也会再分析。)。

将#ifdef...三个的注释去掉,再次运行,得到:b.cpp error C3861: 'print_c': identifier not found,之前重复定义的错误没有了。在编译b.cpp得到b.obj的过程中为什么会报print_c没有声明的错误呢?我想原因在于编译别的.cpp文件时,第一次 include c.h后,别的.cpp的文件再编译的时候,再次判断 #ifdef发现成立,于是就不再include c.h了,于是会告知print_c没有被声明。 所以,#ifdef 这几个宏的特点在于:只能include一次是对于多个.cpp文件而言的,一个include了,别的就不能include了。

 

注释掉#ifdef的宏,取消#pragma once的注释,编译、连接均没有报错,可以正常运行。于是,可以肯定的是这两个宏之间还是有一定得区别的,于是再次看msdn上的定义:Specifies that the file will be included (opened) only once by the compiler when compiling a source code file 才恍然大悟,他说的是:when compiling a source code file,也就是这个仅仅include一次是针对编译一个.cpp文件而言的,而不是对于要编译的所有.cpp而言的。我想这也是它与#ifdef最大的区别。所以相比之下#pragma once这个宏更加方便。

 

除此之外,在做这个实验的时候,在c.h中放了一个int b; 当在#pragma once下运行时,发现编译是可以通过的,但是在连接的时候却报了错:b.obj : error LNK2005: "int b" (?b@@3HA) already defined in tst.obj, 在linking时,由于要将之前几个.obj连接起来,由于在tst.obj和b.obj中均有int b的定义,于是在解析符号b时发现重定义。 所以在头文件中,最好不应该有变量的定义(可以有类型的定义,比如struct,class类型定义,但不应该定义它们的对象或变量),放的主要应该是类型的定义和函数的声明。

你可能感兴趣的:(关于头文件定义中#ifdef,#define,#endif和#pragma once的区别)