C语言头文件重复包含的2种解决方法(附带实例)
本节我们讨论一个 C语言问题,文件 main.c 包含了头文件 person.h。但是,如果我们不小心写了两个相同的 #include 指令,例如:
这个问题很好解决,细心一点,不要写重复的 #include 指令就行。但是,还有更加隐蔽的重复包含的问题,例如:
为了实现这个目标,我们可以使用条件编译。例如,在 person.h 文件中,我们可以添加以下代码:
让我们看看当文件 main.c 包含了两次 person.h 文件时,条件编译将如何工作:
在第一次包含头文件时,预处理指令 #ifndef 检查到宏 PERSON_H 未定义。因此,将保留从 #ifndef PERSON_H 开始,直到 #endif 结束的代码。在这段代码内,宏 PERSON_H 被定义。
在第二次包含头文件时,预处理指令 #ifndef 检查到宏 PERSON_H 已定义。因此,从 #ifndef PERSON_H 开始直到 #endif 结束的代码将被删除。
通过使用条件编译,我们可以有效地避免头文件被重复包含的问题。
#pragma once 指令可以在头文件开头使用,告诉编译器这个头文件只需要被包含一次。如果已经包含过,就不再重复包含。使用该指令的格式如下:
使用 #pragma once 指令的优点是简单易用,无须定义任何宏,这可以大大减少头文件中的代码量,从而提高编译速度。然而,该指令属于编译器扩展,并非标准 C/C++ 语言的一部分,因此部分编译器可能不支持。另外,一些编程规范推荐使用条件编译,因为它具有更广泛的兼容性和可移植性。
#include "person.h" // 对person.h文件包含一次 #include "person.h" // 对person.h文件包含两次 int main() { Person p; p = newPerson(); printPerson(&p); return 0; }显然,经过预处理后,文件 main.c 中出现了标识符的多次声明或定义。例如,第一次定义 Person 类型后,它的作用域从声明开始到文件结束。紧接着,文件内出现了另一个 Person 的定义,两个同名标识符的作用域重叠。这种情况会导致文件 main.c 因为标识符重定义而编译失败。
这个问题很好解决,细心一点,不要写重复的 #include 指令就行。但是,还有更加隐蔽的重复包含的问题,例如:
#include "person.h" #include "students.h" int main() { Student s; s = newStudent(); printStudent(&s); return 0; }文件 main.c 包含了头文件 person.h 和 students.h。假设,头文件 students.h 也包含了 person.h 头文件。这样,依然会导致头文件 person.h 被重复包含的问题。此外,若嵌套层次更加复杂,问题会比较难排查。
条件编译解决头文件重复包含
为了解决头文件被重复包含的问题,我们的目标是确保同一个头文件仅被包含一次,而不论其相互包含的顺序。为了实现这个目标,我们可以使用条件编译。例如,在 person.h 文件中,我们可以添加以下代码:
#ifndef PERSON_H #define PERSON_H // 头文件代码 #endif在上面的代码中,我们使用条件编译来保护头文件中的代码。
让我们看看当文件 main.c 包含了两次 person.h 文件时,条件编译将如何工作:
// -----------第一次包含----------- #ifndef PERSON_H #define PERSON_H // person.h头文件代码 #endif // -----------第二次包含----------- #ifndef PERSON_H #define PERSON_H // person.h头文件代码 #endif int main() { //... }预处理指令 #ifndef 用于检查其后跟随的宏是否未定义。如果未定义宏,则保留从 #ifndef 开始,直到 #endif 结束的代码。如果已定义宏,则删除从 #ifndef 开始,直到 #endif 结束的代码。
在第一次包含头文件时,预处理指令 #ifndef 检查到宏 PERSON_H 未定义。因此,将保留从 #ifndef PERSON_H 开始,直到 #endif 结束的代码。在这段代码内,宏 PERSON_H 被定义。
在第二次包含头文件时,预处理指令 #ifndef 检查到宏 PERSON_H 已定义。因此,从 #ifndef PERSON_H 开始直到 #endif 结束的代码将被删除。
通过使用条件编译,我们可以有效地避免头文件被重复包含的问题。
#pragma once指令解决头文件重复包含
#pragma once 指令也是用于防止头文件被重复包含的一种方法。它与条件编译有相同的作用,但使用起来更简单。#pragma once 指令可以在头文件开头使用,告诉编译器这个头文件只需要被包含一次。如果已经包含过,就不再重复包含。使用该指令的格式如下:
#pragma once // 头文件内容与条件编译不同,#pragma once 指令不需要在头文件结尾处添加任何条件编译指令。当编译器遇到 #pragma once 指令时,它会检查指令所在的头文件是否已经被包含过。如果已经包含,就跳过该头文件的编译,否则继续编译该头文件。
使用 #pragma once 指令的优点是简单易用,无须定义任何宏,这可以大大减少头文件中的代码量,从而提高编译速度。然而,该指令属于编译器扩展,并非标准 C/C++ 语言的一部分,因此部分编译器可能不支持。另外,一些编程规范推荐使用条件编译,因为它具有更广泛的兼容性和可移植性。