当我们学习了C++的语法以及种种特性之后,可能要开始做一个项目。但是一个C++的项目究竟是怎么组成的呢?当我们用Visual Studio新建一个项目时,里面的cpp文件和项目究竟是怎样的关系?这篇文章就是为了解答这些问题。我们将解释项目、解决方案、库等各种术语的含义。

  首先要知道,C++从源文件到可执行文件的一个基本流程。C++的源文件,要通过预处理、编译、链接三个步骤,才能得到可执行的文件。这个过程中涉及到了预处理器、编译器、链接器。

  源文件通过预处理和编译得到的是目标文件(Object file)。一堆目标文件链接得到的是可执行文件(executable)。

1.源文件、头文件、翻译单元

  C++程序由源文件(source file)组成。常见的扩展名为.c、.cc、.cxx、.cpp。编译器负责翻译每个源文件至机器码,每个源文件被称作一个翻译单元(translation unit)。

  头文件(head file)通常用于多个翻译单元之间分享信息。头文件不会被编译。C++预处理器(preprocessor)预先把每个#include语句替换为相应的头文件内容,然后再由编译器处理。

2.程序库、可执行文件及动态链接库

  翻译单元编译后,得到的是目标文件(object file),储存机器码。这些机器码:

  • 可重定位的(relocatable):未决定代码的内存地址
  • 未链接的(unlinked):未解决的外部函数参考,以及翻译单元外定义的全局数据。

  程序库(library)只是为了方便,把大量目标文件集合成单个易用的文件。程序库只是一个简单的存档,像zip或tar一样,包多个目标文件。

  链接器把对象文件和程序库链接成可执行文件(executable)。链接器的工作包括:

  • 计算全部机器码的最终相对位置,即当程序执行时机器码在内存中的分布。
  • 确保正确地解析每个目标文件的所有外部函数参考和全局数据。

  动态链接库(dynamic linked library,DLL)是一种特殊的库。使用了DLL的可执行文件含有未完全链接(partially linked)的机器代码。这种可执行文件中,已解析大多函数及数据参考,但存于DLL的函数和数据参考则维持未连接的状态。当可执行文件运行时,操作系统会将相应DLL载入内存。

  DLL中的函数和数据可以供多个可执行文件使用。而在可执行文件中,也只要保存少量的DLL信息,不需要完整代码。

3.项目及解决方案

  项目(project)是源文件的集合。解决方案(solution)是项目的集合。

4.生成配置

  C/C++的预处理器、编译器、链接器都提供大量选项,用来控制代码生成的方式。这些选项通常由执行编译器的命令行设定。例如,使用微软编译器,生成一个翻译单元的典型命令行如下。   

C:> /c foo.cpp /Fo foo.obj /Wall /Od /Zi

  此命令行告诉编译器编译但不链接(/c)foo.cpp翻译单元,输出结果到foo.obj目标文件(/Fo foo.obj),打开所有警告(/Wall),关掉所有优化(/Od)并产生调试信息(/Zi)。

  现代的编译器提供大量选项,每次生成时都重新指定这些选项,既不现实又易犯错,因此生成配置(build configuration)应运而生。生成配置是解决方案内个别项目的预处理器、编译器、链接器的选项集合。程序员可以设置任意数量的生成配置,任意命名,并在每个生成配置中设定不同的选项。

  多数项目都有至少两个生成配置,通常名为“Debug”和“Release”。“Debug”做开发用途,“Release”做最终软件出版之用。“Debug”生成运行的较慢,但为程序员提供调试信息。   

参考文献:《游戏引擎架构》