C++ 编译过程
一般而言,对于 C++ 程序编译有以下4个阶段:
- 预处理(preprocessing)
对源程序中的伪指令(以#开头的指令)和特殊符号进行处理。伪指令包括宏定义、条件编译指令、头文件包含指令等。 - 编译(compilation)
将预处理后的文件编译生成后缀为 .s 的汇编语言文件,。编译程序所要做的工作是通过记法分析和语法分析,在确认所有指令都符合语法规则后,将其翻译成等价的中间代码或汇编代码。 - 汇编(assembly)
将汇编文件汇编生成后缀为 .o 的目标文件(二进制)。汇编过程实际上是指把汇编语言代码翻译成目标机器指令的过程。 - 链接(linking)
将多个目标文件和库连接生成后缀为 .out 或 .exe 的可执行文件。链接程序的主要工作就是将有关的目标文件彼此相连接,即将在一个文件中引用的符号现该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。
链接处理分为两种:
- 静态链接(static linking)
简单说,指链接器在链接时将库(静态库)的内容拷贝到可执行程序中。 - 动态链接(dynamic linking)
简单说,指程序运行时才将库(动态链接库)连接到程序中。
- 静态链接(static linking)
Linux系统
参考《An Introduction to GCC》
使用gcc(GNU编译器套件)
gcc 最开始的时候是 GNU C Compiler,就是一个 c 编译器。但是后来因为这个项目里边集成了更多其他不同语言的编译器, GCC 就代表 the GNU Compiler Collection,所以表示一堆编译器的合集。g++ 则是GCC的 c++ 编译器。
现在调用 gcc 的时候,已经不是当初那个 c 语言编译器了,更确切地说它是一个驱动程序,根据代码的后缀名来判断调用 c 编译器还是 c++ 编译器(g++)。即代码后缀是 .c,则调用 c 编译器和 linker 去链接 c 的 library,代码后缀是 .cpp,则调用 g++ 编译器,但是这里 gcc 不会自动和 c++ 库链接。
基本参数
shell 窗口中执行 gcc –help 列出基本参数:$ gcc --help # 省略一部分输出 -E Preprocess only; do not compile, assemble or link -S Compile only; do not assemble or link -c Compile and assemble, but do not link -o <file> Place the output into <file> # 省略一部分输出
例
$ cd ~/design-pattern/factory/ $ ls Factory.cpp Factory.h main.cpp Product.cpp Product.h
编译
如上所述,gcc 不会自动和 c++ 库链接,所以这里只编译,否则会有一堆链接错误$ gcc *.cpp -c $ ls # 可以看到编译生成 *.o 对象文件(二进制) Factory.cpp Factory.o main.o Product.h Factory.h main.cpp Product.cpp Product.o
链接
- 上面强调 gcc 不会自动链接 c++ 库,这里我们可以手动链接 c++ 库
$ gcc *.o -lstdc++ $ ls a.out Factory.h main.cpp Product.cpp Product.o Factory.cpp Factory.o main.o Product.h $ ./a.out Concrete Facotry Concrete Product...
- 因 gcc 不会自动链接 c++ 库,所以可直接用 g++ 链接 .o 文件生成 .out 可执行文件
$ g++ *.o $ ls a.out Factory.h main.cpp Product.cpp Product.o Factory.cpp Factory.o main.o Product.h $ ./a.out Concrete Facotry Concrete Product...
综上,可直接使用 g++ 编译链接 c++ 程序生成可执行文件
$ g++ *.cpp $ ls a.out Factory.cpp Factory.h main.cpp Product.cpp Product.h $ ./a.out Concrete Facotry Concrete Product...
include 包含文件搜索路径
include 包含文件在编译的时候使用,GCC 搜索头文件路径的顺序:
- 当前目录(#include “” 方式会搜索当前目录,#include <> 方式不会搜索当前目录)
- -I 选项指定的目录
- gcc 环境变量 CPLUS_INCLUDE_PATH 指示的目录(c 程序使用的是 C_INCLUDE_PATH)
gcc 的默认目录
/usr/local/include /usr/include /usr/lib/x86_64-linux-gnu/5.4.0/include
gcc 的默认目录,不是由 $PATH 环境变量指定的,而是由 g++ 的配置 prefix 指定。
lib 库文件搜索路径
链接的时候使用,搜索顺序:
静态库文件
- -L 选项指定的路径
- gcc 的环境变量 LIBRARY_PATH
- gcc 的默认目录
/lib /usr/local/lib /usr/lib
- 动态库文件
- 编译代码时指定的路径(gcc 的参数 -Wl, -rpath 指定)
- 环境变量 LD_LIBRARY_PATH 指定的路径(多个路径用冒号 : 分隔)
- 配置文件 /etc/ld.so.conf 指定的路径
- 默认的动态库路径 /lib,/usr/lib
-I(大写的i)、 -include、 -L、 -l(小写的L) 参数说明
-I 扩展 gcc 在编译时对包含文件的搜索路径,即不使用 -I 参数时,只会在上述默认路径下搜索。
$ cd ~/design-pattern/factory/ $ ls Factory.cpp Factory.h main.cpp Product.cpp Product.h $ mv *.h ../ $ gcc -c -I ../ *.cpp $ ls Factory.cpp Factory.o main.cpp main.o Product.cpp Product.o $ gcc
- -include 指定包含头文件,很少用,因为一般头文件都在源代码中用 #include xxx实现了。
-L 扩展 gcc 在链接时对库文件的搜索路径,即不使用 -L 参数时,只会在上述默认路径下搜索。
$ gcc *.cpp -I ../ -L /usr/lib/gcc/x86_64-linux-gnu/5.4.0 -lstdc++ # or $ g++ *.cpp -I ../ -L /usr/lib/gcc/x86_64-linux-gnu/5.4.0 $ ./a.out Concrete Facotry Concrete Product...
-l 紧接库名,指定程序要链接的库。
这里注意库名与库文件名的区别,以文件 ./usr/lib/gcc/x86_64-linux-gnu/5/libstdc++.so 为例,libstdc++.so 为库文件名,而库名则是 stdc++。如$ gcc *.cpp -I ../ -lstdc++ $ ls a.out Factory.cpp Factory.o main.cpp main.o Product.cpp Product.o $ ./a.out Concrete Facotry Concrete Product...
-shared 参数说明
用于编译生成动态库。
Windows系统
cl.exe 和 link.exe 分别是 visual studio IDE 中的编译器和链接器。
使用cl.exe编译器
用法
usage: cl [ option... ] filename... [ /link linkoption... ]
基本参数
- -I: 指定第一个寻找头文件的目录(如果指定多个目录,则使用多个-I)
- -c: 只编译不链接
- -EHsc: 指示编译器启用 C++ 异常处理
例:
Windows下命令中使用路径时,注意特殊字符要使用转义字符”\”,如下路径中包含\,空格和括号,前面均加转义符”\”,或者将路径用引号括起来。
gitbash窗口:
$ cl *.cpp -c -EHsc -I C:\\Program\ Files\ \(x86\)\\Microsoft\ Visual\ Studio\ 14.0\\VC\\include -I C:\\Program\ Files\ \(x86\)\\Windows\ Kits\\10\\Include\\10.0.10240.0\\ucrt
# or
$ cl *.cpp -c -EHsc -I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt" -I "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"
使用link.exe链接器
- 用法
usage: LINK [options] [files] [@commandfile]
- 基本参数(以下参数在windows cmd窗口中执行有效,在bash中不过)
- /LIBPATH: 指定要在环境库路径之前搜索的路径
- /OUT: 指定输出文件名
- 例:
windows cmd 窗口(bash下测试不过):
$ link *.obj /LIBPATH:"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib" /LIBPATH:"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib" /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x86" /OUT:fac.exe
编译器控制的LINK选项
参考MSDN https://msdn.microsoft.com/zh-cn/library/92b5ab4h.aspx https://msdn.microsoft.com/zh-cn/library/btw8x58e.aspx
除非指定 -c 选项,否则 cl.exe 会自动调用 link.exe 。以下为 cl 中影响链接的常用功能(注意当参数有指定输出文件时,要带冒号:):
- /Fo: filename: 指定生成的 .obj 文件名
- /Fe: filename: 传递 /OUT:filename 给 link.exe, 指定生成的 .exe 文件名
环境变量
参考MSDN https://msdn.microsoft.com/zh-cn/library/kezkeayy.aspx https://msdn.microsoft.com/zh-cn/library/6y6t9esh.aspx
CL 和 LINK 用到的环境变量有:
- cl.exe
- INCLUDE: 指示 C/C++ 源文件中使用 #include 包含的文件,如头文件等。标准 C/C++ 开发,会使用 Visual C++ 的安装路径下 vc\include 子目录中的包含文件, Windows API 开发,会使用Platform SDK 的安装路径下 vc\include 子目录中的包含文件。
- LIBPATH: 指定用于搜索使用 #using 引用的元数据文件的目录。
- link.exe
- LIB: 指定搜索对象、库或其他文件的路径。标准 C/C++ 开发,会使用 Visual C++ 的安装路径下 vc\lib 子目录中的包含文件, Windows API 开发,会使用Platform SDK 的安装路径下 vc\lib 子目录中的包含文件link.exe会从如下路径搜索对象文件和库文件:
- 当前路径
- 命令行上由 /BASE 指定的路径
- 使用 LIB环境变量中的路径。
- TMP: 链接时寻找 OMF 或 .res 文件时,会从这个路径中寻找。
- LIB: 指定搜索对象、库或其他文件的路径。标准 C/C++ 开发,会使用 Visual C++ 的安装路径下 vc\lib 子目录中的包含文件, Windows API 开发,会使用Platform SDK 的安装路径下 vc\lib 子目录中的包含文件link.exe会从如下路径搜索对象文件和库文件:
我本机的系统环境变量设置脚本
echo off
setx -m WIN_KIT "C:\Program Files (x86)\Windows Kits"
setx -m WIN_SDK "C:\Program Files (x86)\Microsoft SDKs"
setx -m VS_INSTALL_DIR "C:\Program Files (x86)\Microsoft Visual Studio 14.0"
setx -m Microsoft_SDK "C:\Program Files (x86)\Microsoft SDKs"
setx -m PATH "%VS_INSTALL_DIR%\VC\bin;%PATH%"
rem INCLUDE
rem -------
setx -m INCLUDE "%VS_INSTALL_DIR%\VC\INCLUDE;%VS_INSTALL_DIR%\VC\ATLMFC\INCLUDE;%WIN_KIT%\10\Include\10.0.10240.0\ucrt"
rem LIB
rem ---
setx -m LIB "%VS_INSTALL_DIR%\VC\lib;%VS_INSTALL_DIR%\VC\ATLMFC\LIB;%Microsoft_SDK%\Windows\v7.1A\Lib;%WIN_KIT%\10\Lib\10.0.10240.0\ucrt\x86;%WIN_KIT%\10\Lib\10.0.10240.0\ucrt\x64"
rem LIBPATH
rem -------
setx -m LIBPATH "%VS_INSTALL_DIR%\VC\lib;%VS_INSTALL_DIR%\VC\ATLMFC\LIB;%WIN_KIT%\10\Lib\10.0.10240.0\ucrt\x86;%Microsoft_SDK%\10\Lib\10.0.10240.0\ucrt\x86"
pause
使用 CL 生成可执行文件
环境变量设置好后,即可通过 cl 在命令行一步生成 C++ 的可执行文件,例
$ cd /e/program/designPattern/Factory/Factory
$ ls *.cpp
Factory.cpp main.cpp Product.cpp
$ cl *.cpp -EHcs -Fe: fac
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
Factory.cpp
main.cpp
Product.cpp
Generating Code...
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:fac.exe
Factory.obj
main.obj
Product.obj
$ ls *.cpp *.obj *.exe
fac.exe* Factory.cpp Factory.obj main.cpp main.obj Product.cpp Product.obj
$ ./fac.exe
Concrete Facotry
Concrete Product...