Linux下的编译工具 —— gcc、g++
目录
1.编译器的发展过程
2.gcc、g++的使用
完成预处理
完成编译
完成汇编
完成链接
直接编译生成可执行二进制文件
3.动静态库
认识动静态库
动静态库的使用
使用动态链接
使用静态连接
1.编译器的发展过程
我们都知道编程是需要依靠编程语言的,编程语言的发展史可以打开概括成这样:二进制 -> 汇编语言 -> C语言 -> 各种面向对象的语言(C++、java、Python……)
计算机能识别的是二进制的语言,所以,我们用汇编语言及汇编语言以上的语言写的程序都需要翻译成二进制,才能被计算机所执行。
汇编语言编译器的自举:
当汇编语言被发明出来之后,汇编语言写的程序是需要被汇编语言的编译器翻译成二进制的,那么汇编语言的编译器是用什么写的呢?
首先我们需要明确的是:编译器也是程序,也需要被编译。
如果第一版的汇编语言编译器是用汇编语言写的,那么问题就变成 先有鸡还是先有蛋了。所以,第一版的汇编语言编译器是用二进制语言写的,这个时候,我们就能够编译汇编语言写的程序了,此时,我们再用汇编语言写一个汇编语言的编译器,汇编语言写的编译器也是程序,那么该程序就能被第一版的用二进制写的汇编语言编译器编译,我们就得到了用汇编语言写的汇编语言编译器。之后,我们再对汇编语言写的编译器进行优化,写一个新版本的用汇编语言写的编译器,再用老版本的汇编语言的编译器编译新版本的编译器,得到新版本的汇编语言的编译器。这个过程就是汇编语言编译器的自举过程。
C语言编译器的自举过程:
同样的道理,当C语言被发明出来之后,我们也是先用C语言前面的语言写一个C语言的编译器,也就是汇编语言写的C语言的编译器,此时我们就能够编译C语言程序了,再用C语言写一个C语言的编译器,然后用汇编语言写的编译器编译,就得到了C语言写的C语言编译器,再对其进行优化,用旧的C语言写的编译器编译新的C语言写的编译器、就得到了新的C语言编译器。这个过程就是C语言编译器的自举过程。
2.gcc、g++的使用
在Linux系统中,提供了两个编译器 —— gcc和g++。gcc是编译c语言的编译器,g++是编译C++的编译器,由于C++兼容C的缘故,g++编译器也能编译c语言,但是gcc不能编译C++。由于二者选项相同,我们以gcc为例进行讲解。
我们知道一个C/C++程序要被翻译成二进制的可执行文件,需要经历如下几步:
- 预处理:头文件展开,去注释,宏替换,条件编译
- 编译:将C/C++代码编译成汇编代码
- 汇编:将汇编代码变成二进制代码。注意,这个二进制代码是可重定位目标二进制文件,这是不能被直接执行的。
- 链接:和库里面的二进制代码进行链接,生成可执行的二进制文件。
下面,我们通过程序的翻译过程来学习gcc的使用。
我们新建一个code.c文件,并向其写入如下内容:
然后保存退出。
完成预处理
我们使用 gcc -E code.c -o code.i 命令完成预处理:
- 语法:gcc -E 起始文件名 -o 指定以 .i 结尾的文件名
- -E:该选项的作用是让 gcc 在预处理结束后停止编译过程
- 预处理结束后的文件以 .i 结尾
查看code.i文件:
- 我们可以看到里面多了很多内容,这就是预处理之后的结果。
完成编译
我们使用 gcc -S code.i -o code.s 命令完成编译,生成汇编代码:
- 语法:gcc -S 起始文件名 -o 指定以.s结尾的文件名
- -S:该选项的作用是让gcc完成编译工作后就停下来
- 编译之后的文件以 .s结尾
我们可以查看code.s文件中的内容:
- 该文件中全是汇编代码。
完成汇编
我们可以使用 gcc -c code.s -o code.o 命令完成汇编,生成可重定位二进制文件:
- 语法:gcc -c 起始文件名 -o 指定以.o结尾的文件名
- -c:该选项的作用是让gcc完成汇编后就停下来
- 汇编之后的文件以.o结尾
我们可以查看code.o文件中的内容:
- 生成的文件是二进制的,我们是看不懂的。
完成链接
我们使用命令 gcc code.o -o code.exe 命令完成链接,并生成可执行的二进制文件:
- 语法:gcc 起始文件名 -o 指定名称的文件名
注意:这里我们并没有手动的链接库文件。这是因为,平台要支持开发,默认就要在系统中安装语言的标准头文件和库文件。
为了验证是否使用了库,我们可以使用ldd命令来查看一个可执行文件链接了哪个库:
- libc.so.6,这个文件我们去掉头lib,去掉第一个点以及其后面的内容,得到的就是链接的库文件。可以看到,我们链接的是c库。
直接编译生成可执行二进制文件
我们使用命令:gcc code.c -o code.exe 命令编译code.c代码,直接生成可执行的二进制文件
- 语法:gcc 要编译的文件名 -o 指定生成的可执行文件名。
- 其中-o表明生成指定文件名的可执行文件。
- 如果不指定名称的话,默认生成的可执行文件名为 a.out。
我们使用 ./code.exe 命令执行这个文件:
3.动静态库
认识动静态库
当我们在编写C语言代码的时候,我们使用了printf这样的函数,但是我们并没有实现这个函数,这是因为,对于这些常用的功能,有人把它写好,并且编译好,打包形成库文件,当我们想要用的时候,直接调用就可以了。
库文件分为动态库和静态库:
- Linux下:动态库以 .so结尾,静态库以 .a结尾。
- Windows下:动态库以 .dll结尾,静态库以 .lib结尾。
动静态库的本质也是文件,而且是两个不同的文件,和动态库进行连接叫做动态链接,和静态库进行连接叫做静态链接:
- 动态链接:动态链接就是我们的可执行程序执行到库文件的代码时,编译器会告诉可执行程序库文件的地址,可执行程序跑过去执行库文件的代码,执行完之后在回来,接着执行可执行文件后面的代码。(动态库不允许被拷贝)
- 静态连接:静态连接就是将可执行文件中需要执行的 库文件中的代码从静态库中拷贝到可执行文件中。
动态库的优缺点:
- 优点:比较节省资源,不会出现太多的重复代码。这里所说的资源,不能狭隘的认为是磁盘资源,如果库需要被别人下载,那么体积小的文件也会节省网络资源;库被调用,也会节省内存资源。
- 缺点:对库的依赖性比较强,一旦库缺失,所有使用这个库的程序都无法运行。
静态库的优缺点:
- 优点:不依赖库,同类型平台中可以直接运行使用。
- 缺点:可执行文件的体积比较大,浪费资源。
动静态库的使用
使用动态链接
我们使用 gcc code.c -o code.exe 命令编译一下code.c文件,然后使用ldd命令查看code.exe文件关联的库:
- 可以看到一个以.so结尾的文件,说明,gcc默认是使用动态链接的。
使用静态连接
我们使用 gcc code.c -o code_static.exe -static 命令编译一下code.c文件:
- -static表明我们要进行静态链接。
我们使用ldd命令查看一下:
- ldd命令用于列出可执行文件所依赖的共享库(动态库)。如果一个程序没有列出任何共享库,或者某些库被静态链接,那么这些库很可能是静态库。
- ldd告诉我们链接的不是一个动态的可执行文件。那么它链接的就是静态库了。
最直观的表现还是动态链接和静态链接形成的可执行文件的大小,我们来看一下:
- 可以看到,code_static.exe文件比 code.exe文件的大小大得多。