【Linux】编译器 - gcc && 函数库
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
目录
一、背景知识
使用 vim 编辑器完成代码书写之后我们需要使用 Linux 中的编译器 gcc 来对程序进行翻译其本质是把程序从文本类文件翻译成二进制可执行文件翻译过程包括预处理进行宏替换、编译生成汇编、汇编生成机器可识别代码、连接生成可执行文件或库文件。
二、gcc如何执行
1、预处理
主要完成头文件展开、条件编译、宏替换、去注释等等工作。
预处理结构后C语言还是C语言没有发生变化。
预处理指令
gcc -E [目标文件名] -o [生成文件名]
-E 从现在开始进行程序的翻译预处理做完就停下来。
-o 指定生成文件的文件名。-o 选项的位置可以不固定但是 -o 选项后面紧跟着的一定是生成的文件的名称。
我们先创建一个文件 myfile.c 使用 vim 编译器写入代码
保存退出后对其进行预处理操作
[ljb@iZuf69tfiox41j76yf0416Z lesson5]$ vim myfile.c
[ljb@iZuf69tfiox41j76yf0416Z lesson5]$ gcc -E myfile.c -o myfile.i
[ljb@iZuf69tfiox41j76yf0416Z lesson5]$ ll
total 24
-rw-rw-r-- 1 ljb ljb 525 Jan 12 17:56 myfile.c
-rw-rw-r-- 1 ljb ljb 17086 Jan 12 17:57 myfile.i
可以看到已经生成了一个可执行文件 myfile.i 。我们把它打开并与 myfile.c 对比
可以看到原本 33 行的代码预处理之后变成了 800 多行。这是因为 预处理 工作把程序里的头文件都拷贝到了源文件之中这个工作叫做头文件展开。
这里打印了 "hello PRINT" 而没有打印 "hello None"是因为在程序开始时定义了宏 PRINT 所以 "hello None" 被条件编译裁剪掉了。
2、编译
完成将c语言编译形成汇编语言。
编译指令
gcc -S [目标文件名] -o [生成文件名]
-S 从现在开始进行程序的翻译编译做完就停下来。
我们对刚刚预处理形成的 myfile.i 文件进行编译
[ljb@iZuf69tfiox41j76yf0416Z lesson5]$ gcc -S myfile.i -o myfile.s
[ljb@iZuf69tfiox41j76yf0416Z lesson5]$ ll
total 28
-rw-rw-r-- 1 ljb ljb 525 Jan 12 20:16 myfile.c
-rw-rw-r-- 1 ljb ljb 17086 Jan 12 19:56 myfile.i
-rw-rw-r-- 1 ljb ljb 681 Jan 12 20:17 myfile.s
生成了文件 myfile.s 我们对比一下 myfile.s 与 myfile.i
此时C语言已经变为了汇编语言有效代码变为 42 行。
3、汇编
将汇编语言文件转换成可重定位目标二进制文件这些二进制文件不可以被执行文件的后缀为 .o 。对应到 Windows 系统中相同类型文件的后缀为 .obj 有几个源文件就有几个 obj 文件。
汇编指令
gcc -c [目标文件名] -o [生成文件名]
-c 从现在开始进行程序的翻译汇编做完就停下来。
我们对刚刚预处理形成的 myfile.s 文件进行编译
[ljb@iZuf69tfiox41j76yf0416Z lesson5]$ gcc -c myfile.s -o myfile.o
[ljb@iZuf69tfiox41j76yf0416Z lesson5]$ ll
total 32
-rw-rw-r-- 1 ljb ljb 525 Jan 12 20:16 myfile.c
-rw-rw-r-- 1 ljb ljb 17086 Jan 12 19:56 myfile.i
-rw-rw-r-- 1 ljb ljb 1920 Jan 12 20:27 myfile.o
-rw-rw-r-- 1 ljb ljb 681 Jan 12 20:26 myfile.s
生成了文件 myfile.o 我们打开该文件
可以看到都是看不懂的乱码这就说明该文件已经不是文本文件而是一个二进制文件了。这个二进制文件是不可执行的。
4、链接
将我们自己形成的 .obj 文件和库文件进行某种合并形成可执行程序。在我们进行链接的时候把函数库对应的程序函数的地址拷贝到可执行程序里。
链接 / 编译指令
gcc [目标文件名] -o [生成文件名]
我们对刚刚生成的 myfile.o 文件进行链接
[ljb@iZuf69tfiox41j76yf0416Z lesson5]$ gcc myfile.o -o myfile1
[ljb@iZuf69tfiox41j76yf0416Z lesson5]$ ll
total 44
-rwxrwxr-x 1 ljb ljb 8440 Jan 12 20:40 myfile1
-rw-rw-r-- 1 ljb ljb 525 Jan 12 20:16 myfile.c
-rw-rw-r-- 1 ljb ljb 17086 Jan 12 19:56 myfile.i
-rw-rw-r-- 1 ljb ljb 1920 Jan 12 20:27 myfile.o
-rw-rw-r-- 1 ljb ljb 681 Jan 12 20:26 myfile.s
至此我们就完成了一个程序翻译的完整过程最终生成了可执行程序 myfile1 。
事实上我们正常对一个程序进行编译只需要执行 第四步链接 的指令就可以前三步都可省略这里写出来是为了方便大家了解 gcc 编译的过程。
提示
为了方便大家记住这些命令选项 与 后缀我们可以来总结一下规律预处理、编译、汇编的命令选项连起来是 -ESc 与我们大家键盘最左上角的按键名字相同而它们所生成的文件的后缀连起来是 .iso 与镜像文件的后缀相同。这样是不是很好记忆了呢
三、函数库
1、初识函数库
我们为什么能够在 Linux 下进行 C/C++ 代码的编写和编译呢这是因为 Linux 系统默认已经携带了语言级别的 头文件 和 语言相应的库。
例如我们的C程序中并没有定义 "printf" 的函数实现且在预编译中包含的 "stdio.h" 中也只有该函数的声明而没有定义函数的实现那么是在哪里实 "printf" 函数的呢最后的答案是系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了在没有特别指定时gcc 会到系统默认的搜索路径 "/usr/lib" 下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数 "printf" 了,而这也就是链接的作用。
这些库所处的位置是 /usr/include/ 我们可以使用 ls 指令看一下
是不是有许多我们熟悉的头文件名称呢
ldd 指令用于打印程序或者库文件所依赖的共享库列表。
ldd [可执行程序]
ldd 指令可以帮我们检测这个可执行程序被形成的时候都依赖了哪些库
这个就是函数库。库分为两种静态库 与 动态库库也是文件。
静态库 一般以 lib 开头以 .a 结尾。形如 libXXXXX.a 。
动态库 一般以 lib 开头以 .so 结尾。形如 libXXXXX.so 。
这种后缀划分是 Linux 下特有的。在 Windows 下静态库的后缀是 .lib 动态库的后缀是 .dll 。
我们区分一个库的名字是去掉它的前缀与后缀看中间部分。
我们看一下上面图片中用红框圈起来的库函数是什么名字
所以该库的名字是 c - 2.17 是C语言的C标准库。
实际上现在我们学习 Linux 时所用的指令有很大一部分都是使用C语言写的。
以 ls 指令为例
我们使用 which 指令找到 ls 指令所在的位置
[ljb@iZuf69tfiox41j76yf0416Z lesson5]$ which ls
alias ls='ls --color=auto'
/usr/bin/ls
再使用 ldd 指令查看它所依赖的函数库
可以看到也是C标准库。
2、动静态库
2.1、动态库
动态库是专门让编译器对用户的程序进行动态链接的。动态库又被称为 共享库 在连接的时候如果是动态链接就找到动态库拷贝用户所需要的代码的地址到用户自己的可执行程序中相关的位置。动态链接成功用户的程序还是依赖动态库一旦动态库缺失用户的程序便无法运行操作系统中绝大多数指令也都无法再运行了。
以上是动态库的缺点那么它有什么优点呢
因为动态库可以做到被大家共享所以代码真正的实现永远都是在库中程序内部只有地址比较节省空间。
2.2、静态库
静态库是专门让编译器对用户的程序进行静态链接的。在连接的时候如果是静态链接就找到静态库拷贝用户所需要的代码到用户自己的可执行程序中。静态链接成功用户的程序不再依赖任何库程序可以独立运行。
以上是静态库的优点那么静态库有什么缺点呢
使用静态库的话因为程序自身拷贝代码的问题会比较浪费空间。
2.3、动静态库的选择
Linux 下默认使用的是 动态链接 和 动态库。我们接下来就来证明一下。
我们使用 file 指令来查看可执行文件 myfile1 的文件类型
ELF 是可执行程序的一种格式 dynamically linked 表明该可执行程序使用的是动态链接。
一般我们安装的操作系统一般默认都只有动态库没有安装静态库。那么如果我们想进行静态链接需要先安装一下静态库
sudo yum install -y glibc-static
安装完成后我们就可以通过添加命令选项 -static 来使用静态链接了
gcc [目标文件名] -o [生成文件名] -static
可以看到使用静态链接生成的可执行文件大小要比动态链接生成的大的多这还仅仅只是一个非常简单的可执行程序如果再复杂一点这个差距会更大。因此我们选择使用动态链接。
我们再来使用 ldd 指令 和 file 指令观察一下静态链接生成的可执行文件
可以看到该文件没有依赖任何库为静态链接。
关于 Linux 下编译器 gcc 与 函数库 的基本知识就讲到这里希望同学们多多支持如果有不对的地方欢迎大佬指正谢谢