单文件例子
先以单文件编译为例子 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...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。