程序编译分为预编译、编译、汇编和链接四个阶段。在Windows操作系统中,编译工具用的是集成的开发环境,在Linux系统中没有很好的继承开发环境,用的是gcc编译器或者g++,gcc用于C语言代码的编译,g++用在C++的编译过程中。在Linux下的编译有一步到位和分布进行两种方式,接下来就以一个简单的示例演示一个两种编译
//hello.cpp
#include<iostream>
using namespace std;
#define MAX 1024
int main()
{
cout<<MAX<<endl;
return 0;
}
1.一步到位式的编译:
gcc hello.c //默认会生成一个名为a.out的可执行文件
gcc hello.c -o hello //会生成一个名hello的可执行文件
2.分步编译
gcc -E hello.c -o hello.i //预处理
gcc -S hello.i -o hello.s //编译
gcc -c hello.s -o hello.o //汇编
gcc hello.o -o hello //链接
01.预编译阶段:完成头文件和宏定义的替换,生成hello.i
在预编译阶段会把头文件的内容都包含进来,预编译阶段的另一个工作就是进行宏替换
# 996 "/usr/include/c++/11/istream" 2 3
# 41 "/usr/include/c++/11/iostream" 2 3
namespace std __attribute__ ((__visibility__ ("default")))
{
# 60 "/usr/include/c++/11/iostream" 3
extern istream cin;
extern ostream cout;
extern ostream cerr;
extern ostream clog;
extern wistream wcin;
extern wostream wcout;
extern wostream wcerr;
extern wostream wclog;
static ios_base::Init __ioinit;
}
# 2 "hello.cpp" 2
# 2 "hello.cpp"
using namespace std;
//上面有好几页是头文件的内容,在预编译阶段会把头文件的内容都包含进来
int main()
{
cout<<1024<<endl; //预编译阶段的另一个工作就是进行宏替换
return 0;
}
02.编译阶段:生成汇编文件hello.s
这是生成的hello.s 文件,可以看到C语言的代码已经被翻译成了汇编指令
.file "hello.cpp"
.text
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.globl main
.type main, @function
main:
.LFB1731:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $1024, %esi
leaq _ZSt4cout(%rip), %rax
movq %rax, %rdi
call _ZNSolsEi@PLT
movq _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E@PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1731:
.size main, .-main
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2231:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L5
cmpl $65535, -8(%rbp)
jne .L5
leaq _ZStL8__ioinit(%rip), %rax
movq %rax, %rdi
call _ZNSt8ios_base4InitC1Ev@PLT
leaq __dso_handle(%rip), %rax
movq %rax, %rdx
leaq _ZStL8__ioinit(%rip), %rax
movq %rax, %rsi
movq _ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
movq %rax, %rdi
call __cxa_atexit@PLT
.L5:
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2231:
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB2232:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $65535, %esi
movl $1, %edi
call _Z41__static_initialization_and_destruction_0ii
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2232:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_main
.hidden __dso_handle
.ident "GCC: (Debian 11.3.0-5) 11.3.0"
.section .note.GNU-stack,"",@progbits
03.汇编阶段:生成目标文件hello.o,在Windows操作系统中生成的是hello.obj
汇编阶段将hello.s的汇编语言代码翻译成了计算机能够识别的二进制代码,我们使用vim直接打开显示的是乱码,只有使用特定的软件显示的就全是由“0”和“1”组成的二进制数据。
04.链接:用所有的目标文件链接生成可执行文件