GCC编译过程 .c -----> .exe .elf
以hello.c为例
#include <stdio.h>
#include <math.h>
#define NAME "CD"
#define AGE 25
int main(void)
{
int a;
a = sqrt(AGE);
printf("hello %s", NAME);
printf(" %d\n", a);
return 0;
}
预处理 -E
把.h .c展开形成.i文件 宏定义直接替换,包含头文件,库文件
gcc -E hello.c -o hello.i
生成.i文件,部分代码如下
# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 375 "/usr/include/features.h" 3 4
# 1 "/usr/include/sys/cdefs.h" 1 3 4
# 392 "/usr/include/sys/cdefs.h" 3 4
# 1 "/usr/include/bits/wordsize.h" 1 3 4
# 393 "/usr/include/sys/cdefs.h" 2 3 4
# 376 "/usr/include/features.h" 2 3 4
# 399 "/usr/include/features.h" 3 4
# 1 "/usr/include/gnu/stubs.h" 1 3 4
# 10 "/usr/include/gnu/stubs.h" 3 4
# 1 "/usr/include/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/gnu/stubs.h" 2 3 4
# 400 "/usr/include/features.h" 2 3 4
# 28 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stddef.h" 1 3 4
# 212 "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stddef.h" 3 4
typedef long unsigned int size_t;
# 34 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/bits/types.h" 1 3 4
# 27 "/usr/include/bits/types.h" 3 4
# 1 "/usr/include/bits/wordsize.h" 1 3 4
# 28 "/usr/include/bits/types.h" 2 3 4
typedef unsigned char __u_char;
typedef unsigned short int __u_short;
typedef unsigned int __u_int;
typedef unsigned long int __u_long;
typedef signed char __int8_t;
typedef unsigned char __uint8_t;
typedef signed short int __int16_t;
typedef unsigned short int __uint16_t;
typedef signed int __int32_t;
typedef unsigned int __uint32_t;
typedef signed long int __int64_t;
typedef unsigned long int __uint64_t;
汇编 -S
.i 文件生成一个汇编代码文件 .S
gcc -S hello.i -o hello.S
生成 .S文件,代码如下
.file "hello.c"
.section .rodata
.LC0:
.string "CD"
.LC1:
.string "hello %s"
.LC2:
.string " %d\n"
.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
subq $16, %rsp
movl $5, -4(%rbp)
movl $.LC0, %esi
movl $.LC1, %edi
movl $0, %eax
call printf
movl -4(%rbp), %eax
movl %eax, %esi
movl $.LC2, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-44)"
.section .note.GNU-stack,"",@progbits
编译 -c
gcc -c hello.S -o hello.o
生成.o文件,为二进制代码
链接 -o
gcc hello.o -o hello
生成可执行文件hello
总结:
- 将C语言源程序预处理,生成
.i
文件。 - 预处理后的.i文件编译成为汇编语言,生成
.s
文件。 - 将汇编语言文件经过汇编,生成目标文件
.o
文件。 - 将各个模块的.o文件链接起来生成一个可执行程序文件
图片来源网络。
.i
文件、.s
文件、.o
文件可以认为是中间文件或临时文件,如果使用 GCC 一次性完成C语言程序的编译,那么只能看到最终的可执行文件,这些中间文件都是看不到的,因为 GCC 已经将它们删除了。
gcc 几个常用选项
- -c 编译、汇编指定的源文件,但是不进行链接
- -S 编译指定的源文件,但是不进行汇编
- -E 预处理指定的源文件,不进行编译
- -o [file1] [file2] 将文件 file2 编译成可执行文件 file1
- -g 生成调试信息,该程序可以被调试器调试
- -Wall 生成所有警告信息