- C或者C++程序从源代码生成可执行程序的过程,需要经历4个过程分别是:预处理,编译,汇编,链接。
- 但考虑实际使用过程中,用户可能并不关心程序的执行结果,只是想快速得到最终的可执行程序,因此gcc和gcc++都对此做了支持。
#include<iostream>
int main() {
std::cout << "hello world" << std::endl;
return 0;
}
1: gcc -o 选项
gcc -o
选项用来指定输出文件,如果不使用 -o
选项,那么将采用默认的输出文件。例如默认情况下,生成的可执行文件的名字默认为 a.out
。
2:gcc 常用的编译选项
2.1 预处理 gcc -E
- 无论是 C 还是 C++ 程序,其从源代码转变为可执行代码的过程,具体可分为 4 个过程,分别为预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。
- 默认情况下,gcc 指令会一气呵成,直接将源代码历经这 4 个过程转变为可执行代码,且不会保留各个阶段产生的中间文件。
- 而如果想查看这 4 个阶段各自产生的中间文件,最简单直接的方式就是对源代码进行“分步编译”,即控制 GCC 编译器逐步对源代码进行预处理、编译、汇编以及链接操作。其中,通过为 gcc 指令添加 -E 选项,即可控制 GCC 编译器仅对源代码做预处理操作。
g++
指令再添加一个-C
选项,阻止GCC
删除源文件和头文件中的注释:gcc -E -C
gcc -E 常见的选项
2.2 编译
- 所谓编译,简单理解就是将预处理得到的程序代码,经过一系列的词法分析、语法分析、语义分析以及优化,加工为当前机器支持的汇编代码。
- 通过 gcc -S 即可命令GCC编译器将指定的文件加工至编译阶段,但是不进行汇编。
#include<string>
int main() {
printf("hello world");
return 0;
}
gcc -S main.cpp -o mainS.txt
// 编译之后的代码
.file "main.cpp"
.section .rdata,"dr"
__ZStL19piecewise_construct:
.space 1
.def ___main; .scl 2; .type 32; .endef
LC0:
.ascii "hello world\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB935:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
call ___main
movl $LC0, (%esp)
call _printf
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE935:
.ident "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
.def _printf; .scl 2; .type 32; .endef
2.3 汇编
- 简单地理解,汇编其实就是将汇编代码转换成可以执行的机器指令。大部分汇编语句对应一条机器指令,有的汇编语句对应多条机器指令。相对于编译操作,汇编过程会简单很多,它并没有复杂的语法,也没有语义,也不需要做指令优化,只需要根据汇编语句和机器指令的对照表一一翻译即可。
-c
选项只是令GCC
编译器将指定文件加工至汇编阶段,但不执行链接操作。这也就意味着:- 如果指定文件为源程序文件(例如 main.cpp),则
gcc -c
指令会对 main.cpp 文件执行预处理、编译以及汇编这 3 步操作.
2.4 链接
总的来说链接阶段要完成的工作,就是将同一项目中各源文件生成的目标文件以及程序中用到的库文件整合为一个可执行文件。
目标文件已经是二进制文件,与可执行文件的组织形式类似,只是有些函数和全局变量的地址还未找到,因此还无法执行。链接的作用就是找到这些目标地址,将所有的目标文件组织成一个可以执行的二进制文件。
完成链接操作,并不需要给 gcc 添加任何选项,只要将汇编阶段得到的 test.o 作为参数传递给它,g++就会在其基础上完成链接操作。例如:g++ MainDemo.o
2.4 执行