避免头文件重复引入和重复定义


版权声明未经作者允许严禁用于商业出版否则追究法律责任。转载请注明出处!!!


1 防止头文件重复引入

使用预编译指令,常用的预编译指令如下:

//条件编译
     #define        //  宏定义 
     #undef         //  取消宏 
     #include       //  文本包含 
     #ifdef         //  如果宏被定义就进行编译 
     #if defined    //  与ifdefine的区别在于可以可以组成复杂的判别条件
     #ifndef        //  如果宏未被定义就进行编译 
     #if !defined   //  与if !define的区别在于可以可以组成复杂的判别条件
     #endif         //  结束编译块的控制 
     #if   defined        //  表达式非零就对代码进行编译 
     #else          //  作为其他预处理的剩余选项进行编译 
     #elif          //  这是一种#else和#if的组合选项 
     #elif defined    //  与ifdefine的区别在于可以可以组成复杂的判别条件
	 #elif !defined    //  与ifdefine的区别在于可以可以组成复杂的判别条件
//编译指令
     #line          //  改变当前的行数和文件名称 
     #error         //  输出一个错误信息 
     #pragma        //  为编译程序提供非常规的控制流信息,可跟once,message等许多参数。

注:这里针对的都是在一个.c文件中避免头文件重复引入。如果一个工程有多个".c"文件,由于编译器对每个.c文件是分开处理的,只在最后进行链接。在这种情况下,如果有多个.c文件都直接或间接引入了某个头文件,这时无法避开的。

1.1 使用条件编译指令

头文件的里内容都包含在条件指令中:

#ifndef  XXXX_H
#define  XXXX_H

......
......

#endif

优点:

  • 不光可以保证同一个文件不被包含多次,也能保证内容完全相同的两个文件(或者代码片段)不被同时引入。
  • 受C/C++语言标准的支持,不受编译器的任何限制

缺点:

  • 如果不同头文件中的宏名不小心“撞车”,可能就会导致你看到头文件明明存在,编译器却硬说找不到声明的状况——这种情况有时非常让人抓狂。
  • 由于编译器每次都需要打开头文件才能判定是否有重复定义,因此在编译大型项目时,条件编译指令会使得编译时间相对较长,因此一些编译器逐渐开始支持#pragma once的方式。

1.2 使用pragma once指令

pragma once编译指令使得同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。你无法对一个头文件中的一段代码作pragma once声明,而只能针对文件。

优点:

  • 你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。

  • 遇到已经引入过的头文件时会直接跳过,而不需要打开。因此大型项目的编译速度也因此提高了一些。

缺点:

  • #pragma once方式却不受一些较老版本的编译器支持,一些支持了的编译器又打算去掉它,所以它的兼容性可能不够好。

  • 如果某个头文件有多份拷贝或者被多个.c.h文件引入,pragma once不能保证他们不被重复引入。当然,相比宏名碰撞引发的“找不到声明”的问题,这种重复包含很容易被发现并修正。

1.3 两者混用

两者混用可以节约一定的间,比如多次引入同一“物理意义上的”头文件,就可以不用展重复引入开头文件。

#pragma once

#ifndef  XXXX_H
#define  XXXX_H

……
……

#endif

2 防止重复定义

最根本的方式是将声明和定义分开。

.c文件中定义,在*.h 中声明。声明包括函数声明,自定义数据类型声明,外部变量声明等。如:

//test.c
#include"add.h"   	//头文件应全部在对应的*.h中引入,`*.c`文件只需引入对应的`*.h`文件即可

int c[10]={0};
int Add(int a, int b)
{
	return a+b;
}


//test.h
#pragma once

#ifndef TEST_H
#define TEST_H

extern int c[];
int Add(int a, int b);

#endif


//main.c
#include"test.h"
int main()
{
	return add(c[0],1);
}

如果一定要把定义放在头文件中,则该工程中只能有一个.c文件中可以先定义对应宏,再直接或间接引入该头文件。格式如下:

//一个工程有多个.c文件

//comm.h,定义放在头文件中
#pragma once
#ifndef COMM_H
#define COMM_H

#ifdef EXTERN_C
	int c=10;
#else 
	extern int c;
#endif

#endif


//addc.h,
#pragma once

#ifndef ADDC_H
#define ADDC_H
	#include"comm.h"
	int addc(int ,int);
#endif

//addc.c
#define  EXTERN_C   //只能有一个.c文件中可以先定义对应宏,再直接或间接引入该头文件
#include"addc.h"
int addc(int a, int b)
{
	return a+b+c;
}


//subc.h
#pragma once

#ifndef SUBC_H
#define SUBC_H
	#include"comm.h"
	int subc(int ,int);
#endif

//subc.c
#include"sub.h"
int subc(int a, int b)
{
	return a-b-c;
}


//main.c
#include"add.h"
#include"sub.h"
int main()
{
	addc(1,2);
	subc(1,2);
}

......



版权声明未经作者允许严禁用于商业出版否则追究法律责任。转载请注明出处!!!


  • 12
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝月心语

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值