# 编译
# 编译器
编译器:泛指用于编译源代码的工具。
常见的几种 C 语言编译器:
- gcc(GNU C Compiler)
- 最初只支持编译 C 语言的源代码,后来还支持 C++、Objective-C、Fortran、Java 等语言,并且被移植到多个平台。
- 是 GNU 编译器集合(GNU Compiler Collection)的一部分,后者是类 Unix 系统上的标准编译器。
- g++
- gcc 提供的另一种编译命令,本质上也是 gcc 编译器。
- 支持编译 C、C++ 语言,但默认将源代码当作 C++ 语言处理。
- MSVC(Microsoft Visual C++)
- 支持编译 C、C++ 语言。
- Clang
- 支持编译 C、C++、Objective-C 等语言。
- 基于 LLVM 编译器运行。
# gcc
# 编译流程
gcc 基本的编译流程如下:
预处理(Pre-Processing):主要是处理 C 语言源文件中的预处理命令,生成一个中间文件
*.i
。gcc -E test.c -o test.i # -o 选项表示 output
汇编(Assembling):由中间文件
*.i
生成汇编语言文件*.s
。gcc -S test.i -o test.s
编译(Compiling):由汇编语言文件
*.s
生成目标文件。gcc -c test.s -o test.o
- 目标文件又称为对象文件,它是二进制文件,与最终的可执行文件很像。
- 在 Linux、Windows 系统上,目标文件的扩展名分别为
.o
、.obj
。
链接(Linking):由目标文件生成最终的可执行文件。
gcc test.o -o 1
- Linux 系统的可执行文件没有扩展名,Windows 系统的可执行文件的扩展名为 .exe 。
上述四个步骤可以简化为一个步骤,称为 构建(Build):
gcc test.c -o test
-I /root/test/include # 添加头文件的检索目录
-L /root/test/lib # 添加库文件的检索目录
-l lib1 # 链接一个名为 lib1 的库文件
-static # 只使用静态链接库
- 使用了头文件或库文件时,gcc 会先检索-I、-L 指定的目录,再检索环境变量 PATH 路径。
# 编译报错
编译时出现的报错有两种:
- Error :致命错误,导致编译失败。
- Warning :警告,可以成功编译、链接,但运行时可能出错。
# 库文件
:包含了一些代码,可以被程序导入、调用。
- 命名格式为
lib + 库名 + 扩展名
。
# 用途
- 可以将程序的代码分离成多个模块,方便调整。
- 可以将一个程序的部分代码解耦出来,保存在库文件中,给另一个程序调用,提高代码的复用性。
- 可以将某种语言的程序保存在库文件中,给另一种语言的程序调用,提高代码的通用性。
# 分类
- 动态链接库:又称为共享库,在程序运行时才被动态导入。
- 这会减小程序文件的体积,但在运行时需要花时间加载。
- 在 Linux、Windows 系统上,静态链接库的扩展名分别为
.so
、.dll
。
- 静态链接库:在程序编译时的 "链接" 阶段被导入,保留在可执行文件中。
- 这会增加程序文件的体积,但运行时不必花时间加载。
- 在 Linux、Windows 系统上,静态链接库的扩展名分别为
.a
、.lib
。
# 示例
从目标文件生成动态链接库:
gcc -shared test.o... -o test.so # 可以调用多个目标文件,还可以从.c 文件生成 -fPIC # 使生成的代码全部采用相对地址,这样就可以被加载到内存的任意位置 -shared # 生成共享动态链接库
- gcc 在编译时会根据库名去寻找相应的库文件,且优先使用动态链接库,当动态链接库文件不存在时才寻找相同库名的静态链接库。
从目标文件生成静态链接库:
ar -rc lib1.a 1.o ... # 可以加上多个目标文件
# 构建
- 构建工具能调用编译器,自动化地编译大量源文件,并进行链接、打包等操作。
- 当项目中源文件较多、引用关系较复杂时,用编译器逐个编译比较麻烦,建议使用构建工具。
- 常见的构建工具:
- make :通常调用 gcc 作为编译器,根据 Makefile 文件,构建项目。
- cmake :可以根据 CMakeList.txt 文件自动生成 Makefile 文件。在 Windows 上则是生成 visual studio 的 project 文件。
- qmake :用于生成 Qt 项目的 Makefile 文件。
- 有的 IDE 本身提供了构建项目的功能,或者调用某种构建工具。
# make
:一个命令行工具,用于构建 C 语言项目。
- 命令:
make # 开始编译(默认使用当前目录下的 Makefile) clean # 删除所有被 make 创建的文件 install # 编译并安装到系统中 uninstall # 卸载
- make 在执行时,会读取 Makefile 文件中的指令,获取模块间的依赖关系,判断哪些文件过时了需要重新编译,然后生成中间代码或最终的可执行程序。
# 跨平台移植
- 将一个程序移植到不同平台上运行,主要有两种方式:
- 主机编译:如果要在某个平台上运行一个程序,则先将程序源代码拷贝到该平台,编译成可执行代码。
- 交叉编译:在一个平台上生成另一个平台上的可执行代码。常用于另一个平台缺少编译环境的情况,比如小型嵌入式系统。
- 在 Linux 上编译程序时:
- 如果程序没有调用 Linux 上特有的库文件、API ,则可以直接放到 Windows 上运行。
- 如果程序依赖 Linux ,则可以用 Visual Studio、MinGW、Cygwin 等工具编译成 Window 的可执行文件。
# 相关概念
- MinGW(Minimalist GNU For Windows)
- 将 GNU 编译器集合移植到了 Windows 上,让用户可以在 Windows 上使用 GNU 编译器(一般是在 DOS 窗口中使用),生成扩展名为 .exe 的可执行文件。
- 在编译时,会将源代码中调用的 Linux 库文件、API 转换成 Windows 库文件、API 。
- Msys
- :MinGW 的子项目,提供了一个在 Windows 上运行的简单 Linux 系统。
- Cygwin
- :一个可以在 Windows 上运行的简单 Linux 系统。
- Cygwin 将一些 Linux 特有的库文件、API 移植到了 Windows 上,模拟出 Linux 的运行环境。被 Cygwin 编译出的程序在运行时会调用这些库文件。
- Visual Studio
- :微软公司提供的一个重量级、功能丰富的 IDE ,支持 C、C++、C# 等语言的开发、调试、构建。
- 可用于在 Windows 上编译 C、C++ 代码,而且比 MinGW 的功能更强。
- 提供了 MSVC 编译器,以及 msbuild、cl、link、nmake 等命令行工具。
- Qt
- :一个跨平台的 C++ 开发框架。
- 提供了一些与平台解耦的 C++ 库文件,因此在该框架中开发的程序可以直接编译成 Linux、Windows、MacOS 等多种平台的可执行文件。
- 一些编程语言本身就支持跨平台。比如 Java 通过 JVM 的机制与底层平台解耦,只需编译一次,就可以拷贝到多个平台上直接运行。