GCC 的意思也只是 GNU C Compiler 而已。经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言;它现在还支持 Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL语言,以及支持函数式编程和逻辑编程的 Mercury 语言,等等。而 GCC 也不再单只是 GNU C 语言编译器的意思了,而是变成了 GNU Compiler Collection 也即是 GNU 编译器家族的意思了。另一方面,说到 GCC 对于操作系统平台及硬件平台支持,概括起来就是一句话:无所不在。

简单编译

//test.c
#include <stdio.h>
int main(void)
{
    printf("Hello World!\n"); // 注释
    return 0;
}

编译输出

gcc test.c -o test

实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编 (Assembly)和连接(Linking)。

编译过程

预处理

在C里预处理只是简单的头文件替换、宏替换 和 删除注释

  gcc -E test.c -o test.i
#或
  gcc -E test.c

结果如下:

# 1 "main.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "main.c"

# 省略接近 800 行代码

  __attribute__ ((__dllimport__)) size_t __attribute__((__cdecl__)) _fread_nolock_s(void *_DstBuf,size_t _DstSize,size_t _ElementSize,size_t _Count,FILE *_File);
# 992 "C:/Strawberry/c/i686-w64-mingw32/include/stdio.h" 2 3

# 1 "C:/Strawberry/c/i686-w64-mingw32/include/_mingw_print_pop.h" 1 3
# 994 "C:/Strawberry/c/i686-w64-mingw32/include/stdio.h" 2 3
# 3 "main.c" 2
int main(void)
{
    printf("Hello World!\n");
    return 0;
}

编译为汇编代码

预处理之后,可直接对生成的test.i文件编译,生成汇编代码

gcc -S test.i -o test.s

结果如下

.file    "main.c"
.def    ___main;    .scl    2;    .type    32;    .endef
.section .rdata,"dr"
LC0:
.ascii "Hello World!\0"
.text
.globl    _main
.def    _main;    .scl    2;    .type    32;    .endef
_main:
pushl    %ebp
movl    %esp, %ebp
andl    $-16, %esp
subl    $16, %esp
call    ___main
movl    $LC0, (%esp)
call    _puts
movl    $0, %eax
leave
ret
.ident    "GCC: (i686-posix-sjlj, built by strawberryperl.com project) 4.9.2"
.def    _puts;    .scl    2;    .type    32;    .endef

汇编

对于生成的汇编代码文件test.s,gas汇编器负责将其编译为目标文件,如下:

gcc -c test.s -o test.o

连接

gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。

对于之前生成的test.o,将其与C标准输入输出库进行连接,最终生成程序test

gcc 命令行

参数说明
-E预处理
-S编译
-c汇编
-o指定输出文件名
-g产生gdb调试的必要信息(后加1、2、3控制调试信息量)
-g1不包含局部变量和与行号有关的调试信息,只能够用于回溯跟踪和堆栈转储
-g2产生的调试信息包括:扩展的符号表、行号、局部或外部变量信息(默认g2)
-g3包含级别2中的所有调试信息以及源代码中定义的宏
-O对程序进行优化编译、链接,编译过程慢
-O2比-O更好的优化编译、链接。速度更慢
-p会将剖析(Profiling)信息加入到最终生成的二进制代码中,利于优化性能
-v输出gcc工作的详细过程
-pedantic帮助程序员发现一些不符合 ANSI/ISO C标准的代码
-Wall能够使GCC产生尽可能多的警告信息
-WerrorGCC会在所有产生警告的地方停止编译
–I指定头文件路径
–L指定库文件路径
–l指定编译时依赖的库文件
-static强制编译时使用静态链接库(默认gcc优先使用动态库)
-shared生成一个共享的目标文件,能与其他目标一起链接生成可执行文件

静态库链接时搜索路径顺序:

  1. ld会去找GCC命令中的参数-L
  2. 再找gcc的环境变量LIBRARY_PATH
  3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的

动态链接时、执行时搜索路径顺序:

  1. 编译目标代码时指定的动态库搜索路径
  2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
  3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
  4. 默认的动态库搜索路径/lib
  5. 默认的动态库搜索路径/usr/lib

GCC 环境变量

  • LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
  • LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
文章目录