1. 预处理(进行宏替换)
2. 编译(生成汇编)
3. 汇编(生成机器可识别代码)
4. 连接(生成可执行文件或库文件)
格式 gcc [ 选项 ] 要编译的文件 [ 选项 ] [ 目标文件 ]
先写好自己的文件,利用gcc编译好后会在当前目录下形成一个a.out文件,这个就是编译好的可执行程序。(上面程序有点小问题,格式不太好看)
gcc在编译的时候,后面加上-o,就可以指定生成文件的名字。
C语言可以用gcc和g++编译。c++只能用g++编译。
预处理功能主要包括头文件展开 ,宏定义 ,去注释,条件编译等。
预处理指令是以 # 号开头的代码行。
实例 : gcc –E hello.c –o hello.i
选项 “-E”, 该选项的作用是让 gcc 在预处理结束后停止编译过程。
选项 “-o” 是指目标文件 ,“.i” 文件为已经过预处理的 C 原始程序
头文件展开,宏定义,去注释的演示
上面的代码,在经过预处理后,生成了下面的文件
-E的意思就是,从现在开始程序编译,直到预处理停下。
所谓的头文件展开,就是把头文件在原文件里拷贝到原文件。 所以,我们看到了原本的几行代码,变成了几百行代码。
条件编译的演示
这个就是上面代码的运行过程
这个就是代码的经过预处理后的代码,一些未满足条件的代码,就直接未编译。
我们将两个宏定义直接全部注释 。
用 -D选项可以对编译器传递不同的宏值。在结合条件编译,可以对代码进行动态裁剪。
我们对编译器传递了verson2=2,这样的话,条件编译就只编译verson2=2的部分
应用:VS 的免费版和专业版 其实两个版本就是软件,免费版就是一部分的专业版。在维护的时候就只要维护一份代码。在程序内部利用类似条件编译的功能来对软件功能进行裁剪。
编译(生成汇编)
在这个阶段中 ,gcc 首先要检查代码的规范性、是否有语法错误等 , 以确定代码的实际要做的工作 , 在检查
无误后 ,gcc 把代码翻译成汇编语言。(此时C语言文件变成了汇编文件)
用户可以使用 “-S” 选项来进行查看 , 该选项只进行编译而不进行汇编 , 生成汇编代码。
实例: gcc –S hello.i –o hello.s
汇编(生成机器可识别代码)
汇编阶段是把编译阶段生成的 “.s” 文件转成目标文件(可重定目标的二进制文件)(但是不可执行)
读者在此可使用选项 “-c” 就可看到汇编代码已转化为 “.o” 的二进制目标代码了
实例 : gcc –c hello.s –o hello.o
变成二进制文件,vim无法查看,变成乱码了。
此时,二进制文件依旧无法执行。
此时,只是将自己写的代码变成了二进制文件,包含在头文件里的被我们使用的函数未被编译成二进制,所以还无法执行。
连接(生成可执行文件或库文件)
在成功编译之后 , 就进入了链接阶段。
实例 : gcc hello.o –o hello
我们在代码中使用一些库函数,我们并没有实现库函数,所以我们需要去连接库。 但是我们再使用gcc的时候并没有使用选项,来指明库,这是为什么? 这是因为gcc默认连接C语言标准库。我们来查看一下。
C语言编译器优化成V2----->用C语言编译器v1编译------->v2形成软件(语言的自举)
那么:程序在翻译的过程,是翻译成低级语言,还是直接翻译为二进制文件? 站在巨人的肩膀上,翻译成低级语言,然后再用低级语言翻译成二进制文件。
不同系统下动静态库的后缀
动态库就需要:动态链接 静态库就需要:静态链接
当然,在动态链接的时候,动态库可以被多个可执行程序同时访问。
动态库是被所有人共享的。同样的,一旦动态库缺失了,所以程序也都无法运行了!!!
静态链接:
就是把代码里需要用到的库函数从静态库里拷贝一份到可执行程序里就是静态链接。(从此之后,这份代码就是独立的,不会受到库缺少的影响)。
静态库与动态库
动静态库的本质就是文件,头文件也是文件。 允许你进行拷贝的库就是静态库。 允许你进行关联的库就是动态库。
动静态库的优缺点
动态库的优点 :比较节省资源,不会出现太多重复代码 。当自己的一份代码出现多个相同的库函数时,不需要每一个都拷贝一份库函数,只需要在动态库中有一份,能够形成链接就行。( 这里的资源不仅仅是储存这份代码时的磁盘资源,也是代码加在进入内存的内存资源,还有别人在下载你的代码时的网卡资源。这里的资源是系统性的,不仅仅节省单一资源) 动态库的缺点 :对库的依赖性比较强。一旦库丢失,所有使用这个库的程序都无法运行。 :效率相对低下,和静态库比较(不是主要矛盾)
静态库的优点 :不依赖库,同类型平台上都可以直接运行。会把代码拷贝到可执行程序内。 静态库的缺点 :可执行程序代码比较大,比较浪费资源