MST

星途 面试题库

面试题:C++ 中头文件和实现文件编译过程的基础理解

请简要描述 C++ 头文件和实现文件在编译过程中的主要步骤以及它们之间的关系。同时说明头文件中常出现的 `#ifndef`、`#define` 和 `#endif` 的作用是什么。
27.7万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

1. C++ 头文件和实现文件在编译过程中的主要步骤

  • 头文件
    • 预处理:头文件在预处理阶段被处理。预处理器会将头文件的内容包含到包含它的源文件中(通过 #include 指令)。例如,#include "header.h" 会将 header.h 的内容插入到该行所在位置。它还会处理头文件中的预处理指令,如 #define#ifdef 等。
    • 不单独编译:头文件本身并不直接参与编译,而是被包含进源文件后,随着源文件一起进入后续编译步骤。
  • 实现文件
    • 预处理:与头文件类似,实现文件(如 .cpp 文件)首先经过预处理阶段。预处理器处理 #include 指令,将相关头文件内容包含进来,同时处理其他预处理指令。
    • 编译:编译器将预处理后的代码翻译成目标机器的汇编代码。它会检查语法错误,进行类型检查等。例如,对于函数调用,编译器会检查函数声明是否存在,参数类型是否匹配等。
    • 汇编:汇编器将汇编代码转换为目标机器的机器码(目标文件,通常为 .obj.o 文件)。
    • 链接:链接器将多个目标文件(包括实现文件生成的目标文件以及可能用到的库文件)链接在一起,解析外部符号引用,生成可执行文件或库文件。例如,如果一个函数在一个源文件中声明,在另一个源文件中定义,链接器会将它们正确关联起来。

2. 头文件和实现文件之间的关系

  • 依赖关系:实现文件通常依赖头文件来获取函数声明、类型定义等信息。例如,一个 cpp 文件中调用某个函数,该函数的声明一般在头文件中,通过 #include 头文件,cpp 文件才能知道如何正确调用这个函数。
  • 信息共享:头文件为多个实现文件提供共享的声明和定义信息,保证了不同实现文件对同一类型、函数等的认知一致。例如,多个 cpp 文件可能都需要使用某个自定义结构体,这个结构体定义在头文件中,各 cpp 文件通过包含该头文件来使用它。

3. #ifndef#define#endif 的作用

#ifndef#define#endif 一起构成了头文件保护符(也叫头文件卫士)。

  • 防止重复包含:假设一个项目中有多个源文件都包含同一个头文件,如果没有头文件保护符,在预处理时,该头文件的内容会被多次插入到不同源文件中,导致重复定义错误。例如,头文件中定义了一个全局变量或一个结构体,如果多次包含,编译器会认为有多个相同定义,从而报错。
  • #ifndef:意思是 “如果没有定义”,它检查后面跟着的宏是否已经被定义。例如 #ifndef HEADER_FILE_NAME_H,它检查 HEADER_FILE_NAME_H 这个宏是否已经被定义。
  • #define:如果 #ifndef 检查到宏未定义,就会执行 #define 指令,定义这个宏。如 #define HEADER_FILE_NAME_H,这样就定义了 HEADER_FILE_NAME_H 宏。
  • #endif:表示头文件保护符的结束。在 #ifndef#endif 之间的内容,在宏未定义时会被处理,一旦宏被定义,再次遇到这个头文件包含时,#ifndef 检查为假,#ifndef#endif 之间的内容就不会被再次处理,从而避免了重复包含带来的问题。