C - Include Header File Once

How to make sure header file just included once

include指令的本质就是在预处理阶段找到被包含的文件将其内容插入到include指令的位置,我们之所以要保证头文件只被包含一次的主要原因是为了防止循环引用,试想一下a.h中含有内容#include "b.h",b.h中含有#include "a.h",当你在程序中无论使用a.h还是b.h给你带来的都是噩梦,我们通过某些措施可以检测到这种循环引用从而中断他们

代码重构

此解决方案有特定的使用情形,如果你的代码是意大利面条式的相互依赖结构的话,就符合此情形,重新规划函数所在文件,避免头文件之间出现相互依赖的情形,有相互依赖情形的可以通过拆解头文件来解除相互依赖,具体的做法:将相互依赖到的代码独立出来创建一个头文件,之前存在相互依赖关系的头文件都包含新创建的头文件

**注:**遵循一个原则,使用include的目的符合该指令的本意,include的本意是将函数提供者的源码同函数使用者的源码隔离开,这样只要函数接口不变,提供者源码变化并不会导致使用者重编译

include guards

此方案是最为通用,也是标准库头文件避免多次被包含使用的方法,具体的做法是:

    #ifndef MY_HEADER_FILE_H_

    #define MY_HEADER_FILE_H_

    /* your header file content */

    #endif

通过条件编译和宏定义来保证头文件只被包含一次(通过宏定义的唯一性来保证),从中也可以看出头文件包含,宏扩展以及条件编译是同时进行(从前往后遇到哪个指令执行哪个)没有先后顺序的。这里其实还涉及到一个软件设计上的责任归属问题,此方案将确保头文件仅被包含一次的责任交给了头文件的开发者

pragma once

现代多数编译器都实现了对指令#pragma once "file.h"的支持,通过该指令将头文件被包含一次的责任交给了编译器,当然该指令并非C标准,这是因为对该指令的实现是不可靠且复杂的,假设编译器是通过文件的绝对路径来对已经包含的文件进行记录以确保仅被包含一次,但该方案没办法识别不同位置的符号连接文件(对符号连接文件的读取将会取回被连接文件内容),如果编译器通过比对被包含文件的内容来保证头文件仅被包含一次呢?这也许没问题了,但却带来了性能问题,所以建议使用include guards

Xiao Wenbin
Xiao Wenbin
Natural Language Processing Engineer

My research interests include machine learning, information retrieval and natural language processing.

Related