面试题答案
一键面试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
之间的内容就不会被再次处理,从而避免了重复包含带来的问题。