编译器如何从头文件中知道源文件存在于某处?

Ite*_*tor 3 c c++ include preprocessor-directive

我刚开始学习C/C++,但我有点困惑.我经常看到#include预处理器指令,带有头文件参数:

#include <stdio.h>
#include <iostream.h> 
#include "windows.h"
#include <math.h>
Run Code Online (Sandbox Code Playgroud)

但有时.h缺少:

#include <iostream>
Run Code Online (Sandbox Code Playgroud)

我已经知道,头文件只包含函数的签名和一些预处理器命令.但是编译器何时以及如何包含数学函数,如果我包括math.h

如果我创建一个库,那么放置.c/.cpp/.h文件的位置,以及如何包含它们?

Joh*_*ode 6

安装编译器时,标准头文件和库文件也会安装到众所周知的位置。例如,在 Linux 或 Unix 系统上,标准头文件通常安装在/usr/include或下/usr/local/include,标准库通常安装在/usr/lib或下/usr/local/lib

当你找到#include头文件时,编译器会根据你使用尖括号 ( #include <stdio.h>) 还是引号 ( #include "myfile.h")在不同的地方寻找它。例如,如果您在尖括号中包含一个头文件,gcc将在以下目录中搜索该头文件:

/usr/local/include
libdir/gcc/target/version/include
/usr/target/include
/usr/include
Run Code Online (Sandbox Code Playgroud)

如果包含带引号的头文件,gcc将首先搜索当前工作目录,然后是命令行选项指定的其他目录,最后是上面的标准包含路径。

大多数编译器允许您指定额外的包含路径作为编译命令的参数,例如

gcc -o executable-name -I /additional/include/path source-files
Run Code Online (Sandbox Code Playgroud)

但有时 .h 丢失:

#include <iostream>

这是 C++ 约定1;C++ 标准头文件没有.h扩展名,我想是为了让它看起来比实际更面向对象(也就是说,你可以假装你是直接导入类,而不是包含描述类的文件的文本)。

我已经知道,头文件只包含函数的签名和一些预处理器命令。但是,如果我包含 math.h,编译器何时以及如何包含数学函数?

数学函数的实现通常保存在一个单独的库中(已经编译并存档到单个二进制文件中的代码);根据编译器的不同,数学库可能会也可能不会自动链接到可执行文件中。例如,gcc即使包含math.h头文件,也不会自动链接任何数学库函数。您必须明确包含数学库作为构建命令的一部分:

gcc -o executable-name command-line-options source-files -lm
Run Code Online (Sandbox Code Playgroud)

使用gcc,约定是链接名为2的库中的链接。标准数学库文件名为,因此告诉链接包含在. -lnamelibname.alibm.a-lmgcclibm.a

与标准头文件一样,编译器将在众所周知的位置搜索标准库。您可以为不属于标准安装的库指定其他搜索路径,如下所示:

gcc -o executable-name command-line-options source-files -L /additional/library/path -ladditional-library
Run Code Online (Sandbox Code Playgroud)

如果我创建一个库,那么将 .c/.cpp/.h 文件放在哪里,以及如何包含它们?

你几乎可以把文件放在任何你想要的地方;您将在构建命令中指定它们的位置。因此,假设您正在创建一个名为mylib. 您在主目录下创建一个新目录:

mkdir ~/mylib
cd ~/mylib
Run Code Online (Sandbox Code Playgroud)

然后创建和编辑源文件和标题:

cd ~/mylib
vi mylib.h
vi mylib.c
Run Code Online (Sandbox Code Playgroud)

要构建mylib为静态库,请执行以下操作:

cd ~/mylib
gcc -c mylib.c
ar libmylib.a mylib.o
Run Code Online (Sandbox Code Playgroud)

因此,当您完成后,该目录~/mylib包含以下内容:

libmylib.a
mylib.c 
mylib.h
mylib.o
Run Code Online (Sandbox Code Playgroud)

要使用mylib程序中收集的函数,您需要指定头文件和库包含路径作为编译命令的一部分:

gcc -o executable-name command-line-options source-files -I ~/mylib -L ~/mylib -lmylib
Run Code Online (Sandbox Code Playgroud)

所以,如果你include "mylib.h"在另一个程序,这将告诉编译器来搜索该标题~/mylib,它会告诉链接,该libmylib.a库将下找到~/mylib

在实际情况下,您将使用 makefile 处理所有构建,而不是手动构建所有内容。您还可能将其设置为作为构建过程的一部分,其他任何人需要使用您的库的所有头文件都将被复制到一个单独的include子目录中,并且该库将被写入一个单独的lib子目录,因此您可以在不暴露源代码的情况下分发您的库。在这种情况下,上面的构建命令看起来更像是:

gcc -o executable-name command-line-options source-files -I ~/mylib/include -L ~/mylib/lib -lmylib
Run Code Online (Sandbox Code Playgroud)


1. 情况并非总是如此;最早的 C++ 实现使用iostream.hvector.h等。我不确定该约定何时更改,但已经有一段时间了。

2. 其实,我已经不知道这是gcc约定还是*nix约定了;这两个人已经在一起很长时间了,有时很难记住。


Som*_*ude 5

实际上,编译器对其他源文件一无所知,只知道当前的翻译单元.链接器的工作是获取所有转换单元并将它们与库链接在一起以创建可执行程序,并且链接器将解析所有定义并注意缺少的函数定义.

传统上,使用C或C++是一个四步过程:

  1. 编辑文件
  2. 将源文件编译为目标文件
  3. 链接对象文件和库以生成可执行程序
  4. 运行程序.

预处理器通常内置在编译器中,因此预处理和编译只需一步.


至于"缺失" .h,没有一个C++ - 只有标准库头文件具有该扩展名.