1.gcc的介绍
gcc是linux中的c语言编译器,我们在linux下要编译我们的写的c语言代码,就需要使用gcc编译器。
那么,我们写的test.c编译生成可执行程序经过以下步骤:
下面我将通过gcc中的选项来说明预处理和,编译,汇编,链接这4个过程中到底做了什么工作。
2.预处理
头文件的展开
下面是test.c的内容:
#include<stdio.h>
int main()
{
printf("hello sjp\n");
return 0;
}
我们执行gcc -E test.c -o test.i后,产生test.i文件。
【 gcc -E test.c】是将test.c这个程序进行翻译,当执行预处理工作完后就停下来
【 -o test.i 】是将前面的处理结果保存在test.i
所以就test.i是test.c是预处理完成后产生的文件。当我们将这两个文件打开后进行对比:
我们发现test.c文件只有8行,test.i文件有800多行,而且test中的#include<stdio.h>不见了。
其实test.i中的int main()前面的都是#include<stdio.h>的内容,也就是说,在预处理阶段,
编译器会将头文件中的内容在源文件中展开,就是将头文件的内容拷贝到源文件
。
去掉注释
我们在对源文件修改一下,然后在将源文件进行预处理后就停下来。再进行对test.c文件和test.i文件进行对比。
经过对比我们发现,test.c中的注释经过预处理后就没有了,所以, 在预处理阶段,编译器会将注释给删除掉。所以说,我们平常写的注释不会影响到程序。因为在预处理节段就将注释给删除掉了
。
宏替换
我们在将test.c中的代码在改一下,这次我在定义一个宏NUM,然后再生成test.i,看发生了什么。
我们通过上面的红方框可以发现, test.c中的NUM在test.i中都被替换为100,add(2)被替换为((2)+(2)),所以,在预处理阶段会将我们宏定义的东西进行替换。
条件编译
我们在来看一下这种情况,下面是条件编译的内容。
经过预编译以后,我们发现test.i文件中的条件编译代码只剩下printf(“hello windows\n"); 因为如果#if为真(NUM不为0),就将其它代码裁剪掉,只剩下printf(“hello windows\n");,为假(为0)只就留下printf(”hello Linux\n");我们在定义的时候就将NUM定义为1,所以#if判断为真,就留下printf(“hello window\n”);因此,在预编译阶段,也进行条件编译,条件编译的好处是,在不同环境下,可以执行不同的代码。
总结:
在预编译阶段执行任务有:
1.头文件的展开,将头文件的内容复制给源文件,
2.去掉注释
3.宏替换
4.条件编译。
5.gcc -E 生成预处理完成后就停止
3.编译
编译是将我们的语言代码翻译成为汇编代码,
指令:gcc -S test .i
【 gcc -S test.i 】 是将我们的文件进行翻译,当进行到编译结束时就停下来,如果不指定产生文件名,则默认相对应.s文件
[sjp@iZwz97d32td9ocseu9tkn4Z 9_24]$ gcc -S test.c # 将test.c文件进行编译后停下来
[sjp@iZwz97d32td9ocseu9tkn4Z 9_24]$ ll
total 8
-rw-rw-r-- 1 sjp sjp 137 Sep 24 17:28 test.c
-rw-rw-r-- 1 sjp sjp 449 Sep 24 20:58 test.s #生成.s文件
我们来看一下test.s中的文件内容:
里面都是汇编代码。
4.汇编
汇编就是将我们之前编译生成的.s文件中的汇编代码翻译成二进制数据生成我们的二进制目标文件,为什么要翻译成二进制数据,因为计算机只能识别二进制数据,是我们看不懂的,但是该二进制文件还是不能够被执行,为什么呢?我们下面会说。
命令: gcc -c test.s
【 gcc -c test.s 】 是将我们的文件进行翻译,直到执行完汇编后就停止,如果不指定生成文件的名字,则默认生成相对应的.o文件
[sjp@iZwz97d32td9ocseu9tkn4Z 9_24]$ gcc -c test.s # 将我们的test.s文件进行汇编
[sjp@iZwz97d32td9ocseu9tkn4Z 9_24]$ ll
total 12
-rw-rw-r-- 1 sjp sjp 137 Sep 24 17:28 test.c
-rw-rw-r-- 1 sjp sjp 1504 Sep 24 22:17 test.o #汇编完成后生成test.o文件
-rw-rw-r-- 1 sjp sjp 449 Sep 24 20:58 test.s
5.链接
虽然.o文件它是二进制文件,但是依然执行不了,为什么呢?
我们先来看源文件中的内容。
1 #include<stdio.h>
2
3 int main()
4 {
5 printf("hello Linux\n");
6 return 0;
7 }
在上面代码中我们调用了printf接口,可是printf这个接口不是我们实现的,当我们去编译的时候只是翻译我们的test.c中的代码,而stido.h中也只是包含printf函数的声明,并没有将我们的printf函数代码关联起来,所以我们.o文件不能够被执行,要想让它执行,就需要先将文件里调用的printf接口与printf接口实现的代码相关联起来后才能被执行。
所以,这时候就需要将我们的.o文件和我们的库文件链接起来形成我们的可执行程序,最后才能被我们执行, 链接的本质是将自己实现的代码和调用接口的代码和过程相关联起来
命令;
gcc test.o
【 gcc test.o 】是进行翻译工作,直到链接完成结束,如果不指定生成的文件名字,则默认生成的可执行文件为a.out
[sjp@iZwz97d32td9ocseu9tkn4Z 9_24]$ gcc test.o # 直接将test.o文件进行链接生成a.out文件
[sjp@iZwz97d32td9ocseu9tkn4Z 9_24]$ gcc test.c -o test # 将test直接预处理 编译 汇编 链接 生成test.c文件
[sjp@iZwz97d32td9ocseu9tkn4Z 9_24]$ ll
total 24
-rwxrwxr-x 1 sjp sjp 8360 Sep 24 22:37 a.out
-rwxrwxr-x 1 sjp sjp 8360 Sep 24 22:38 test
-rw-rw-r-- 1 sjp sjp 73 Sep 24 22:36 test.c
-rw-rw-r-- 1 sjp sjp 1504 Sep 24 22:17 test.o
-rw-rw-r-- 1 sjp sjp 449 Sep 24 20:58 test.s
总结:
1.gcc常用的3个选项-E -S -c,如果我们记不住这三个选项的话,你可以一下你的键盘左上角的[【 Esc 】键
2.gcc 中的选项 :
-E是将文件预处理完毕就停止产生.i文件
-s是将将文件编译完毕后停止产生.s文件
-c 是将文件汇编完毕后停止产生.o文件
-o是指定将结果输出到另一个文件。
-g产生调试信息
不带任何选项就链接。
3.g++是在linux翻译c++或c代码的编译器,它的使用只需要将上面的gcc换成g++就行。
好啦,今天的分享就到这里了,喜欢的朋友帮我点个赞呀~