#ifndef#define#endif防止头文件重复包含, 你不是真的懂

注:以下所用环境皆为VS2005, 由于本人编程能力及表达能力有限, 大家有看不懂的地方可以多看几遍,有错误地方请一定指出  

 

这里首先说明下几点基础知识, 相信大部分人对于以下几点大部分都已经知道了, 你也可以直接跳到最后部分看#ifndef#define#endif的真正作用

  1.预编译阶段把所有#include ”***.h“ (“”与<>的区别这里就不说了)用***.h的内容来替换了, 所以之后就没有.h了所有.h的内容都已经包含进了需要它们的.cpp中(注:该步个人认为是发生在预编译阶段) 

  2.生成最后的exe文件是由编译、链接两步完成的, 编译是源代码生成obj二进制目标文件的过程, 注意一个源代码文件(指.cpp, 而非.h, .h已经被包含进.cpp中了)生成一个obj文件, 在VS2005中单独编译一个obj的方法是选中该.cpp文件ctrl+f7, 文章中以下所说的编译皆按该方法执行而非F7, 由于编译是独立的, 所以在两个独立的编译单元里是可以有重名的函数的, 例如a.cpp中可以有一个void fun(); b.cpp中可同时有一个void fun(); 这点十分重要, 大家可以试一下并且理解清楚

  3.编译期间, 我们只要声明了的东西就能使用, 而无需它的定义, 声明可以重复, extern在编译时是告诉该编译单元该变量的定义在别的编译单元里, 相当于声明, 链接时, 定义在整个程序中有且仅有一份,  例如如下代码, 编译可通过, 但链接时失败

[cpp]  view plain  copy
  1. extern int a;  
  2. //extern double a;//错误, a只能有一个类型  
  3. extern int a;  
  4. extern void fun1(int a, int b);  
  5. extern void fun1(int a, int b);  
  6. extern void fun1(int a, int b, int c);//函数重载  
  7. void fun2();  
  8. void fun2(int a);//函数重载  
  9. void fun2()  
  10. {  
  11. }  
  12. int main()  
  13. {  
  14.     a = 100;  
  15.     fun1(200, 300);  
  16.     fun2();  
  17.     fun2(100);  
  18.     return 0;  
  19. }  
  20. void fun2();  
 

 

 

  4.我们从语法上来分析下#ifndef#define#endif(这点相信地球人都知道了)

[cpp]  view plain  copy
  1. //-----a.h-----  
  2. #ifndef A_H_  
  3. #define A_H_  
  4. void fun();  
  5. #endif  
 

预编译阶段, 当第一次执行该段代码(即#include "a.h",参见第一条)时, 由于我们并没有宏定义A_H_, 所以会执行#define A_H_以及void fun()两条语句, 第二次执行该段代码时因为#ifndef A_H_为假就直接走到#endif后面也就等于该次#include "a.h"什么也没做了

 

总结:

好了, 下面就我们以上所学的知识来总结一下, 来纠正大家一直以来对#ifndef#define#endif的误解

当我们一个简单的project中有三个文件main.cpp, a.cpp, a.h,而 main.cpp 和a.cpp分别包含了a.h, 在编译阶段, 两个编译单元是都会分别包含a.h的, 即使他们使用了#ifndef#define#endif, 这也是为什么当a.h被多个文件包含时我们不允许在a.h中定义变量及函数的原因, 因为在链接阶段会出现重定义。 但是在a.h中定义一个static变量却是允许的, 因为static变量是模块性作用域, 就这个例子来说, 若我们在a.h中写static int sss = 0;那么main.cpp与a.cpp使用的sss将为2个独立的sss.

那么是否#ifndef#define#endif就没用了呢, 大家可以想想, 当我们a.cpp中写了多个#include "a.h"时, 如果我们使用了#ifndef#define#endif那么预编译阶段就只会包含一个a.h中的内容到a.cpp中, 你也许会说, 有谁会傻到在a.cpp中写多个#include "a.h"呢, 那么请考虑稍微复杂点的情况, 当我们main.cpp中包含了a.h和b.h, 而a.h中我们又包含了b.h, 那么如果我们使用了#ifndef#define#endif则main.obj只会包含一份b.h


优秀回复:

按我的理解总结一下:
1. c中所有的宏都是编译之前的预编译阶段处理完成的,#ifndef #define #endif当然也一样。
2. 编译器的作用范围是“单个文件”(包括.h文件,因为其最终会展开至.c文件内),又由于宏是在编译之前处理的,即宏的功能仅仅只能体现在编译阶段,因此#ifndef #define #endif只能防止编译阶段的“重复包含”错误。
3. 链接器的作用是将编译器生成的.obj文件链接起来并生成最终可执行文件,因此不妨认为链接器的作用范围是“文件之间”。而链接器工作的时候,实际上是对变量、函数等地址的处理,这时候已经没有“宏”这一概念了,因此#ifndef #define #endif根本无法在链接阶段起作用,也就无法防止链接阶段的“重复包含”错误了。
4. 实际上,“重复包含”并不是错误,只要.h文件中只写声明而不写定义,那么重复包含多少次都是没关系的,这时候甚至不需要写#ifndef #define #endif。如果真的要在.h文件中写定义,那么加上#ifndef #define #endif则可以防止编译阶段的“重复定义”错误;在变量前加上文件作用域关键字static则可以防止链接阶段的“重复定义”错误,当然,这时候的变量已经不是真正的全局变量了。


总结:是用防止编译阶段的重复包含错误,进一步预防编译阶段的重复定义错误。


原文链接:http://blog.csdn.net/q191201771/article/details/6399820

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值