防止头文件二次编译

防止头文件二次编译

   假设一个叫head.h的头文件,被你工程中两个源文件同时包含如:A.cpp和B.cpp,编译时会将head.h编译两次,导致编译效率低下。为了避免这种情况,我们需要采用条件编译的方式,防止一个源文件两次包含同一个头文件,而不是防止两个源文件包含同一个头文件。
   假如你有一个C源文件,它包含了多个头文件,比如头文件A和头文件B,而头文件B又包含了头文件A,则最终的效果是,该源文件包含了两次头文件A。如果你在头文件A里定义了结构体或者类类型(这是最常见的情况),那么问题来了,编译时会报大量的重复定义错误

条件编译

C语言提供的编译预处理的功能有一下三种:宏定义 文件包含 条件编译。

一般情况下,源程序中所有的行都参加编译。但有时希望对其中一部分内容只在满足一定条件下才进行编译,即对一部分内容指定编译条件,这就是“条件编译”(conditional compile)。

条件编译语句排版时,需考虑以下三种位置:

  1. 条件编译语句块与函数定义体之间不存在相互嵌套(主要在(.h)文件中);
  2. 条件编译语句块嵌套在函数体之外(主要在(.c)文件中);
  3. 条件编译语句嵌套在函数体内 (主要在(.c)文件中)。条件编译指令将决定哪些代码被编译,而哪些是不被编译的。可根据表达式的值或某个特定宏是否被定义来确定编译条件。

#if、#else、#elif和#endif指令

一般形式有如下几种
(1)
#if 表达式
//语句段1
#else
//语句段2
#endif
如果表达式为真,就编译语句段1,否则编译语句段2[1]
(2)
#if表达式1
//语句段1
#elif表达式2
//语句段2
#else
//语句段3
#endif

如果表达式1真,则编译语句段1,否则判断表达式2;如果表达式2为真,则编译语句段2,否则编译语句段3

#ifdef和#ifndef

(1)#ifdef的一般形式:

#ifdef宏名
//语句段
#endif

作用:如果在此之前已定义了这样的宏名,则编译语句段。
(2)#ifndef的一般形式:

#ifndef宏名
//语句段
#endif

作用:如果在此之前没有定义这样的宏名,则编译语句段。
#else可以用于#ifdef和#ifndef中,但#elif不可以。

防止头文件二次编译

一、下划线“__”属于编程风格的内容,对程序没有影响。不用下划线也可以,用几个下划线也由个人习惯。

二、其实质是一个宏名。由此我们可以防止发生重复定义或声明。

假设你的头文件名为head.h,根据习惯,我们声明一个宏HEAD_H,对应这个头文件,在头文件中开始的地方和结尾的地方加上 对HEAD_H的声明和判断,头文件Head.h如下:

ifndef HEAD_H
define HEAD_H

……(头文件内容)

#endif

这样,头文件可以避免被多次包含。头文件中定义的变量不存在重复声明或定义。

一、关键字:#ifndef,#define,#endif可以防止头文件被重复引用

结构是这样的:
如a.h头文件

 #ifndef XX
 #define XX
// 函数声明
 #endif

第一次include这个头文件的时候由于XX还没有被define,所以满足#ifndef XX这个判断,那么执行它下面的内容 #define XX和他下面的函数声明;
如果由于编码者的不小心或者嵌套包含造成了这个头文件被多次引用;那么当编译到第二次#inlude的地方的时候由于不满足 #ifndef XX这个判断条件所以不执行后面的内容(#define XX和函数声明都不走)直接跳到#endif因此可以很好的防止头文件被重复引用。

二、现在有一个大型的程序分为很多模块每个模块中又有很多的函数这样你肯定不可能知道他的全部函数中的代码
现在假如说
main.c文件中有这们两行代码:
#include

#program once 和 #ifndef的区别

#ifndef这种是最早期使用的方法,是基于语言的宏定义名字不能冲突的前提下的。这种方法不仅能保证同一个头文件不会被包含两次,也可以保证内容完全相同的两个文件也只能被包含一次。但是他优缺点,就是你的#ifndef 后面跟的宏名字和你程序中的其他宏名字发生了”撞车“,那么会出现以下两种后果
第一种:file1头文件中有一个宏
//file1.h
#define BOOK_H //宏名
现在又有一个文件 book.h 里面使用了宏定义方式防止头文件二次编译
#ifndef BOOK_H
#define BOOK_H
// program codes
#endif
下面是你的主函数所在文件内容
#include”fil1.h”
#include”book.h” //这两个都是你自己的头文件
#include<……..>
………………
预编译阶段把file1文件展开,就得到了宏 BOOK_H,在处理book.h文件时就发现BOOK_H这个宏已经存在了,就不会包含book.h头文件了,这就是弊端所在了。
第二种就是:
两者顺序反过来了,弊端类似。

#pragma once这种方式,是微软编译器独有的,也是后来才有的,所以知道的人并不是很多,用的人也不是很多,因为他不支持跨平台。如果你想写跨平台的代码,最好使用上一种。这是一种由编译器提供支持的方式,防止同一文件的二次编译,这里的同一文件指的是物理文件。
他也是有弊端的:
假如你的某一个头文件有多份拷贝,那么这些文件虽然在逻辑上都是一样的,但是在物理上他们却是不同的,所以当你把这些文件包含的时候,就会发现真的都包含进来了,然后就是编译错误了。还有,当物理上的同一文件被嵌套包含的时候,使用第一种方法预处理会每一次打开该文件做判断的,但是第二种方法则不会,所以在此#pragma once 会更快些。下面举例说明
// Test1.h
#ifndefine TEST1_H
#defineTEST1_H

#endif

// Test2.h
#pragma once        
...

// Test.cpp
#include "Test1.h"      // line 1
#include "Test1.h"      // line 2
#include "Test2.h"      // line 3
#include "Test2.h"      // line 4 这里的Test2.h是同一物理文件

预处理器在执行这四句的时候,先打开Test1.h然后发现里面的宏TEST1_H没有被定义,所以会包含这个文件,第二句的时候,同样还是会打开Test2.h的发现宏已定义,就不包含该文件按了。第三句时,发现之前没有包含Test2,h则会把该文件包含进来,执行第四句的时候,发现该文件已经被包含了,所以不用打开就直接跳过了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值