共计 2367 个字符,预计需要花费 6 分钟才能阅读完成。
单文件例子
先以单文件编译为例子 hello.cc:
#include<iostream>
using namespace std;
int main() {
cout << "Hello World" << endl;
return 0;
}
编译加运行的命令
g++ 'hello.cc' -o 'hello' -Wall -g -O2 -std=c++17 && hello
其含义就是将 hello.cc 源文件编译输出为 hello 这个可执行文件并运行, 得到一个 ”Hello World” 的输出, 那么这个简单的 g++ 命令背后发生了什么呢?
编译过程
上述 gcc 命令其实依次执行了四步操作:
1. 预处理(Preprocessing)
2. 编译(Compilation)
3. 汇编(Assemble)
4. 链接(Linking)
常见后缀含义
.c 为后缀的文件:c 语言源代码文件
.a 为后缀的文件: 是由目标文件构成的库文件
.cpp 为后缀的文件: 是 c ++ 源代码文件
.h 为后缀的文件: 头文件
.o 为后缀的文件: 是编译后的目标文件
.s 为后缀的文件: 是汇编语言源代码文件
.m 为后缀的文件:Objective- C 原始程序
.so 为后缀的文件: 编译后的动态库文件
1. 预处理(Preprocessing)
预处理阶段做的事情: 宏的替换,还有注释的消除,还有找到相关的库文件,将 #include 文件的全部内容插入。若用 <> 括起文件则在系统的 INCLUDE 目录中寻找文件,若用 ”” 括起文件则在当前目录中寻找文件。预处理之后得到的仍然是文本文件,但文件体积会大很多, 生成的文件是 -i 文件, 对 hello.cc 文件进行预处理的命令
g++ -E hello.cc -o hello.i
或者直接调用 cpp 命令
cpp hello.cc -o hello.i
-E
是指让编译器在预处理之后就退出,不进行后续编译过程, -o
是指指定输出文件名, 在处理成.i 文件以后, 文件的体积会大的多, 变成了 4w 多行的文件
2. 编译(Compilation)
这里的编译不是指程序从源文件到二进制程序的全部过程,而是指 将经过预处理之后的程序转换成特定汇编代码 (assembly code) 的过程。将.i 预处理文件编译为汇编代码的命令是 cc1, 使用 - S 可以直接从.cc 文件到汇编代码
g++ -S hello.cc -o hello.s
hello.s 文件的部分内容
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 10, 14 sdk_version 10, 14
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
3. 汇编(Assemble)
汇编过程将上一步的汇编代码转换成机器码 (machine code),这一步产生的文件叫做 目标文件 ,是二进制格式。gcc
汇编过程通过 as
命令完成:
as hello.s -o hello.o
等价于
g++ -c hello.s -o hello.o
输出的 hello.o 为二进制文件
这一步会 为每一个源文件产生一个目标文件。
4. 链接(Linking)
链接过程将多个目标文件以及所需的库文件 (.so 等) 链接成最终的可执行文件 (executable file)。
命令大致如下
ld -o test.out test.o inc/mymath.o ...libraries...
所以其实简单的一行命令
g++ hello.cc
其中其实包含了好几层流程
多文件例子
添加两个文件 math.h
`int add(int a, int b);`
和math.cc
, 定义一下这个 add 方法
int add(int a, int b) {return a + b;};
然后在 hello.cc
中调用一下这个 add 方法
#include<iostream>
#include "math.h"
using namespace std;
int main() {
cout << "Hello World" << endl;
cout << add(1, 2) << endl;
return 0;
}
再直接运行
g++ hello.cc
试试, 发现收到如下报错
Undefined symbols for architecture x86_64:
"add(int, int)", referenced from:
_main in hello-9842e2.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
看到说 add(int, int)这个方法是未定义符号, 可是明明将 math.h 引入进来了啊, 其实是因为没有链接到 math.cc 生成的.o 文件, 链接 math.o 再生成可执行文件试试
g++ -c hello.cc math.cc
这样生成了 hello.o math.o 两个二进制文件, 然后链接 math.o 和 hello.o 生成可执行文件
g++ hello.o math.o -o hello
再运行 hello 试试, 发现得到了想要的输出。或者还有一个更简单的办法
g++ hello.cc math.cc -o hello
经过我测试, 可以直接得到上面两个命令合并的效果。
上面的命令, 在中大型项目中, 一般使用 makefile
文件进行整合, makefile
文件的作用和写法单独写一个文章。
参考链接
https://www.cnblogs.com/ericl…
https://blog.csdn.net/csdn912…