Linux编译器——gcc/g++使用
当我们使用vim写了一段代码后,那么我们应如何去编译代码呢?
1. gcc/g++
C语言的代码可以用gcc/g++来编译,而C++的代码只能用g++来编译,因为C++兼容C语言。
- 众所周知C语言的后缀是
.c
,而C++的后缀却不止一种,分别是:.cpp/.cc/.cxx
- 一些细节问题:在Linux系统中文件后缀无意义,但是当我们修改文件后缀不为.c时,此时会出现编译不通过的现象,这是为什么捏?
如图所示:系统不关心后缀并不代表编译器不关心后缀,gcc/g++会检查文件的后缀是否符合当前编译器。
- 指令:
gcc/g++ [选项] 要编译的文件 [选项] [目标文件]
gcc选项:
-E
只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面。-S
编译到汇编语言不进行汇编和链接。-c
编译到目标代码。-o
文件输出到文件。-static
此选项对生成的文件采用静态链接。-g
生成调试信息。GNU 调试器可利用该信息。-shared
此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.编译器的优化选项的4个级别O0、O1、O2、O3:
-O0
表示没有优化-O1
表示优化级别 1-O2
表示优化级别 2-O3
表示优化级别最高-w
不生成任何警告信息。-Wall
生成所有警告信息。
2. 程序的翻译过程
-
预处理
- 头文件展开
- 去注释
- 条件编译
- 宏替换
-
编译(C->汇编)
-
汇编(汇编->可重定位二进制文件)
-
连接(形成可执行文件)
当大佬们认为C/C++语言还是比较麻烦的时候,这时候大佬发明一种更易懂的语言,解释性语言。
如:JAVA(半解释型)、php、Python等。
这些高级语言不会再被编译而会通过解释器被解释,而解释器又是通过C/C++写出来的,所以我们经常会说C生万物。
2.1 预处理
预处理功能主要包括宏定义,文件包含,条件编译,去注释等。
预处理指令是以#号开头的代码行。
实例:
gcc -E code.c
会将预处理的结果直接显示到显示器上。如图:
gcc -E code.c -o code.i
指明将预处理的结果生成code.i文件。如图:
此时我们用vim打开code.i
重要选项:
- 选项
-E
,作用是让 gcc 帮我进行程序的翻译,等预处理做完停下来,就不用再继续往后走了。- 选项
-o
,作用是指明要形成的目标文件,“.i”文件为已经过预处理的C原始程序。(不带-o则会将预处理的结果直接打印到屏幕上)
2.2 编译(C->汇编)
- 在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查。
- 无误后,gcc 把代码翻译成汇编语言,就是把预处理阶段的.i文件转成目标文件。
重要选项:
- 选项-S,作用是只进行编译而不进行汇编,等编译工作做完,生成汇编代码就停下来。
- 实例:
gcc -S code.i -o code.s
2.3 汇编(汇编->可重定位二进制文件)
- 汇编阶段是把编译阶段生成的.s文件转成目标文件。
- 我们在此可使用选项-c就可看到汇编代码已转化为.o的二进制目标代码了。
重要选项:
- 选项
-c
,作用是汇编做完就停下来,形成可重定位二进制文件。- 实例:
gcc -c code.s -o code.o
用vim打开这个二进制文件,我们会发现这是一堆乱码
用个命令:od code.o
这时候我们能清楚的发现,code.o这个文件里全是二进制代码
但是此时code.o这个文件并不能直接执行
2.4 连接(形成可执行文件)
*(所有的).o + 系统库 = 可执行程序
- 在成功编译之后,就进入了链接阶段。
- 实例:
gcc code.o –o execute(指定名称形成可执行文件)
助记:选项,看看键盘的左上角esc键,-ESc,ES是大写的,c是小写的;文件后缀:code.c、code.s、code.o->.iso,镜像文件的后缀或者是国际标准ISO。
2.5 函数库
- 我们现在所写的所有代码都是站在巨人的肩膀上的,因为已经有人给我们写好了对应可以直接使用的函数了。
- 那么我们在日常中所使用的这批功能函数,在哪里,又是以什么方式呈现给我们的呢?
这时候我们就得提到一个概念了——函数库!
下面介绍一条指令:ldd
。
ldd [可执行程序]
ldd指令是用来查看可执行程序所依赖的第三方库的。
我们的代码+头文件+库=我们的可执行程序
我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数 printf 了,而这也就是链接的作用。
所以所谓的开发环境的安装一定要下载并拷贝头文件和库文件到开发环境中的特定路径下,并且一定要能够被编译器自己所找到!
函数库一般分为静态库和动态库两种。
- 静态库:是C/C++或者其他第三方提供的所有方法的集合,被所有程序以拷贝的方式,将需要的代码拷贝到自己的可执行程序中!因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为==.a==。静态库是静态链接。
- 动态库:是C/C++或者其他第三方提供的所有方法的集合,在编译链接时并没有把库文件的代码拷贝到可执行文件中,而是被所有程序以链接的方式关联起来,这样可以节省系统的开销。动态库一般后缀名为==.so==,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如下所示。
gcc hello.o –o hello
。动态库是动态链接,库中所有的函数都有入口地址,所谓的动态链接其实就是把要链接的库中的函数地址拷贝到我们的可执行程序中的特定位置中去!- gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证。
动态链接的优缺点:
- 优点:形成的可执行程序体积比较小,是比较节省资源的~
- 缺点:强依赖动态库,动态库没了,所有依赖这个库的程序都无法运行了!
静态链接的优缺点:
- 优点:无视库,可以独立运行。
- 缺点:体积太大,浪费资源。
默认情况下的云服务器是没有安装C静态库的,只有动态库的,如果要安装:
sudo yum install -y glibc-static
安装C++静态库:
sudo yum install -y libstdc++-static
安装完成之后我们使用这两条指令生成一下动态链接和静态链接的可执行程序,默认情况下,gcc就是动态链接的。
- 动态链接:
gcc code.c -o code-d
- 静态链接:
gcc code.c -o code-s -static
不难看出,由静态链接生成的可执行程序所占的空间要比动态链接生成的可执行程序大的多。
最后,总结一下问题,我们的开发环境默认都要为我们做些什么?
- 下载开发环境,如:include、lib等。
- 设置合理的查找路径。
- 规定好我们形成可执行程序的链接方式!