-v:两个功能:查看gcc编译器的版本(单独gcc -v);显示gcc执行时的详细过程,(和其它搭配,如gcc -v -o hello hello.o)。
(1)预处理 Preprocess only; do not compile, assemble or link
C/C++源文件中,以#开头的命令被称为预处理命令,如包含命令#include、宏定义命令#define、条件编译命令#if、#ifdef等。预处理就是将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些东西输出到一个.i文件中等待进一步处理。(把h内容插入,生成.i文件,一般很长)
(2)编译
编译就是把C/C++代码(比如上述的.i文件)翻译成汇编代码。
(3)汇编
汇编就是将第二步输出的汇编代码翻译成符合一定格式的机器代码,在Linux系统上一般表现为ELF目标文件(OBJ文件,.o文件)。反汇编是指将机器代码转换为汇编代码,这在调试程序时常常用到。
(4)链接
链接就是将上步生成的OBJ文件和系统库的OBJ文件、库文件链接起来,最终生成了可以在特定平台运行的可执行文件。
方式1:
gcc hello.c(直接接要编译的文件) 注意前提是进入到了文件所在目录,ls命令要可以看到所在位置有本文件。
ls查看编译后文件发现多了一个a.out,是个可执行程序,然后./a.out来执行该应用程序。(要执行程序必须有./!!!有些指令呢就不需要./直接用文件名就行)
上面这个可以,不好。常使用这个:
gcc -o hello hello.c 输出hello,然后./hello来执行该应用程序。
方式2:
gcc -E -o hello.i hello.c
gcc -S -o hello.s hello.i
gcc -c -o hello.o hello.s
gcc -o hello hello.o
小结:
1)输入文件的后缀名和选项共同决定gcc到底执行那些操作。
编译器源文件的后缀名(!!!)表示源文件用的语言(支持多种语言,也包括上述c语言编译过程的中间语言),决定了编译器的默认动作(比如i文件只执行编译和汇编)
.o及.a文件呢会被传递给连接器,.o文件就只能执行链接操作了。
2)在编译过程中,除非使用了-E、-S、-c选项(或者编译出错阻止了完整的编译过程)
否则最后的步骤都是链接。
方式3:
gcc -c -o hello.o hello.c(上述方式复杂,通过这两句配合简化整个过程)
gcc -o hello hello.o
重:gcc会对.c文件默认进行预处理操作(后缀名决定了编译器进行哪些操作),
-c再来指明了编译、汇编,从而得到.o文件
。再通过gcc -o hello hello.o将.o文件进行链接,得到可执行应用程序。
链接就是将汇编生成的OBJ文件、系统库的OBJ文件、库文件链接起来,
最终生成可以在特定平台运行的可执行程序。
一个十几行小程序编译出来可能会有几千字节,为什么呢?
因为为了程序在linux平台运行,在链接时肯定会链接很多必要的函数
crt1.o、crti.o、crtbegin.o、crtend.o、crtn.o是gcc加入的系统标准启动文件,
对于一般应用程序,这些启动是必需的。
-lc:链接libc库文件,其中libc库文件中就实现了printf等函数。(-L大L是指明链接的路径的,-l指明链接哪个库文件,c是libc简写,所以是lc)
gcc -v -nostdlib -o hello hello.o会提示因为没有链接系统标准启动文件和标准库文件,而链接失败。(-nostdlib表示的不链接)
我们的程序这样编译的话会有很多错误,比如cannot find symbol_start; undefined reference to ‘puts’ 'prin