程序构建过程
- 下图从源码到到可执行程序程序的构建过程
- 预处理: 源文件(hello.c, stdio.h,…) 经过预处理器(cpp) 预处理得到 hello.i ,这个过程做了预处理指令(宏定义, 条件编译, 文件的包含)执行, 和注释消除
gcc -E hello.c -o hello.i
- 编译: gcc将C语言编译为汇编语言文件 hello.s ,这个过程中会检查语法错误, 但不会检查逻辑错误
gcc -S hello.i -o hello.s
- 汇编: 再将汇编语言文件hello.s做汇编形成机器指令文件(目标文件) hello.o
gcc -c hello.s
- 链接: 再将hello.s和 库文件, 运行时文件链接形成可执行文件 a.out
gcc hello.o
- 程序执行: 把可执行文件加载到内存就可以执行了
./a.out
需要记住的知识点:
- GCC 参数小结:
- -E 预处理
- -S 将源文件或预处理后的程序编译为汇编语言
- -c 汇编为目标文件
- -o 输出文件
- 文件名后缀小结
- .c 源程序
- .i 预处理后的文件
- .s 汇编语言文件
- .h 头文件
- .o 目标文件
代码说明
hello.c
#include <stdio.h>
//宏定义
//条件编译
//文件的包含 是三种常用的预处理
int main(void){
printf("hello beijing\n");
return 0;
}
↓
- 预处理
gcc -E hello.c -o hello.i #预处理
hello.i
# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
.... //前面都是头文件, 有printf函数的声明
# 6 "hello.c"
int main(void){
printf("hello beijing\n");
return 0;
}
↓
- 编译
gcc -S hello.i -o hello.s #编译为汇编语言文件
hello.s
.file "hello.c"
.section .rodata
.LC0:
.string "hello beijing"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
call puts
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits
↓
- 汇编
gcc -c hello.s -o hello.o #汇编
hello.o 不是可执行的二进制文件, 可以浅显的理解为还需要给它链接配执行所需要的环境
↓
- 链接
gcc hello.o;
./a.out #hello beijing
补充
#如果include的文件和main.c不在同一个目录, 加-I就可以改变编译目录
$ gcc -c main.c -Idir