济南网站排名外包正规app开发和制作公司
web/
2025/10/2 7:00:01/
文章来源:
济南网站排名外包,正规app开发和制作公司,app 开发软件,网站建设天乐大厦Linux#xff1a;gcc gcc概述语言发展史gcc的编译过程预处理编译汇编 gcc的链接过程动态库与静态库 gcc概述
GCC#xff08;英文全拼#xff1a;GNU Compiler Collection#xff09;是 GNU 工具链的主要组成部分#xff0c;是一套以 GPL 和 LGPL 许可证发布的程序语言编译… Linuxgcc gcc概述语言发展史gcc的编译过程预处理编译汇编 gcc的链接过程动态库与静态库 gcc概述
GCC英文全拼GNU Compiler Collection是 GNU 工具链的主要组成部分是一套以 GPL 和 LGPL 许可证发布的程序语言编译器自由软件由 Richard Stallman 于 1985 年开始开发。
gcc是GCC中的C语言编译器而g是GCC中的C编译器。本博客只讲解gccg的语法和选项和gcc都是一致的。
gcc编译C语言最基本的语法
gcc test.c你就完成了对test.c文件的编译操作该过程会默认生成一个名为a.out的可执行文件你可以直接运行a.out。
如果你想要编译的同时指定编译后生成文件的名称就加上-o选项后紧跟着编译后文件的名称
gcc test.c -o test.out此时生成的可执行文件就叫做test.out了。
gcc默认不支持C99标准如果你希望以C99标准编译该文件加上选项-stdc99
gcc test.c -o test.out -stdc99编译C语言要经过预处理编译汇编链接的过程可是为什么我们需要这些过程呢这涉及到计算机语言的发展史了。 语言发展史
在最早的时候对计算机编程是通过打孔纸带的如果有孔就是1没孔就是0。但是这种二进制编程的效率太低了于是计算机的从业者就开始对二进制进行改进诞生了汇编语言。汇编语言的诞生具有划时代的意义于是就发展出了操作系统编译器等东西。比如Unix的最初版就是通过汇编语言写的。后来为了方便又诞生了面向过程语言其中最大名鼎鼎的毫无疑问是C语言。再后来人们觉得面向对象思想是符合人类的编程习惯的就又有了CpythonJava这样的高级语言。
现在问题就来了不论是汇编语言C语言还是如今的高级语言。是先有语言还是先有编译器这就涉及到编译器自举过程。
编译器自举
在出现汇编语言之前我们只有二进制语言来编程。但是人们发明汇编语言后就要有对应的编译器来编译汇编语言不然计算机就无法理解汇编语言。因为汇编语言无法被编译所以我们就要先用二进制语言写一个汇编语言的编译器然后我们的汇编语言就可以被计算机理解了。但是一旦我们的汇编语言可以被解释了可以用于编程了那么我就可以再用汇编语言写一个编译器了。从此以后编译器就用汇编语言再来书写了。这个过程就叫做编译器自举。
同理在C语言出来之前我们无法用C语言写一个C语言的编译器所以就用汇编语言写一个C语言的编译器。现在C语言就可以被计算机编译了于是我们就可以再用C语言写一个C语言的编译器。
那么现在又有一个问题了请问我们在解释C语言的时候是直接把C语言解释为二进制指令让计算机理解。还是先把C语言解释为汇编语言然后再让汇编语言解释为二进制呢不得不说如果直接让C语言变成二进制语言这个过程未免太麻烦了二进制语言过于反人类了相应的汇编语言就好很多。我们要在巨人的肩膀上看世界汇编语言已经把转换为二进制这个工作做完了那么我们只需要在汇编语言的基础上改进就好。因此C语言要先变成汇编语言最后变成二进制。这就是为什么C语言要有预处理编译汇编的过程。 gcc的编译过程
现在我们再简单回顾一下以上三个阶段的功能并且讲解gcc的编译相关选项。
在test.c中有如下代码 预处理 头文件展开去注释宏替换条件编译 那么经过这个过程还是C语言吗答案是是的该过程只是预先处理了一下C语言把一些没必要的内容删除减少后续工作的工作量。处理后依然是C语言代码。
使用gcc时带上-E选项就可以得到预处理后的文件
gcc -E test.c -o test.i此处我把编译后的文件命名为test.i一般而言经过预处理的文件都以.i为后缀。
左侧是预处理后的文件test.i右侧是源文件test.c 可以看到左侧代码经过了800多行才到main函数这800行就是stdio.h头文件的展开语句#define M 100没有了并且main函数中的M被替换为了100也就是完成了宏替换被注释的四行//printf(hello world!\n);被删除了也就是注释的删除
现在再看看条件编译我把代码改为以下代码 如果我们把VERSION2注释掉那么就会输出hello VERSION1.0如果把VERSION1注释掉那么就会输出hello VERSION2.0如果都注释掉那么就输出hello free。我们确实可以在编写代码的时候通过注释来进行条件编译控制代码的版本。但是这个过程在编译的时候就可以直接控制。gcc可以在命令行中定义宏。
语法
gcc test.c -o test1.exe -D VERSION11以上命令中-D VERSION1就相当于在代码中写了一句#define VERSION1 1-D选项用于指定一个宏。
看到以下现象
第一次-D VERSION1最后一行输出的hello VERSION1.0第二次-D VERSION2最后一行输出hello VERSION2.0第三次没有加任何宏定义就输出了hello free。
也就是说我们可以通过指令给编译器传递不同的宏来动态裁剪代码。
比如说visual studio有社区版和专业版社区版是免费的专业版要收费毫无疑问社区版是专业版经过一定阉割后产生的。那么在微软公司内部这两个版本要分开维护吗这样的话如果一个版本出现了问题那还要去看看另外一个版本有没有相同的问题然后修改两份代码未免太麻烦了。因此完全可以采用条件编译把需要被阉割掉的部分用条件编译包起来。最后在编译的时候使用命令行传入不同的宏实现一份代码两个版本。因此条件编译的最重要的应用场景就是一份代码发行多个版本的软件。 编译
该过程是把C语言的代码变成汇编语言的过程。
想要生成该阶段的文件需要通过-S选项
gcc -S test.c -o test.s该阶段的文件一般以.s结尾。
test.s内部 可以看到内部确实已经变成汇编语言的文件了。 汇编
该过程是把汇编语言变成二进制的过程这个过程生成的文件叫做目标文件全称为可重定位目标二进制文件。这个文件虽然已经是二进制文件了但是它还不是一个可执行文件。
想要获得该阶段的文件需要使用选项-c
gcc -c test.c -o test.o一般而言该阶段的文件以.o或者.obj结尾。在Linux中习惯用.o在Windows中习惯用.obj.
test.o内部 可以看到其内部都是一些乱码因为我是用的vim是一款文本编辑器其不能解析二进制文本所以会出现乱码。
为什么该过程已经是一个二进制文件了还是不能被计算机执行计算机不是可以识别二进制吗
这是因为缺少链接的过程接下来我们看看链接都有哪些功能。 gcc的链接过程
我们的C语言内部调用很多库函数比如printfscanf等等。它们并没有在编译的时候展开不信你可以回去看看那个.i文件绝对没有展开一个叫做printf的函数。那么C语言要如何拿到这个函数并调用它呢这就涉及到链接的过程。
如果你想要让你的.c.i.s.o中的任意一个文件变成链接后的文件不用带任何选项直接执行gcc即可因为直接执行就是生成可执行文件这已经是链接后的文件了。
gcc -o test.exe test.c这样最后生成的test.exe就是可执行文件了在此.exe不是强制的只是我前面已经写过太多test命名的文件了使用后缀区分一下属性。在Windows中可执行文件的后缀就是.exe因为Windows通过后缀区分文件而Linux不通过后缀区分文件因此这个.exe可有可无只用于帮助我们自己快速判断文件属性。
那么这个可执行文件test.exe是如何链接到库的又链接了那些库呢
我们可以通过ldd指令来查看一个可执行文件链接了那些库
ldd test.exe输出结果 比如第二行的libc就是C语言的标准库。另外的它还指明了一些库在系统中的路径。也就是说我们的很多头文件都已经早早地在Linux中下载好了因此我们可以在Linux上运行C语言代码。
比如说/usr/include/路径下的文件 你可以看到很多非常非常熟悉的头文件比如stdio.hmath.hstdlib.h等等。
在链接到库时库分为两种动态库和静态库。 动态库与静态库
静态库编译链接时把库文件的代码全部拷贝到可执行文件中在运行时也就不再需要库文件了。 优点只要形成了可执行文件那么就脱离对库的依赖可以自主运行可移植性好 缺点相同的资源拷贝多份浪费资源生成的文件比较大 动态库在编译链接时并没有把库文件的代码加入到可执行文件中而是在程序执行时由运行时链接文件加载库编译器会提供动态库的地址当程序执行到指定的代码段就会去动态库内部查找对应的内容。 优点节省资源整个操作系统所有使用动态库的程序只需要一份库文件内存中也只加载一份 缺点一旦丢失所有链接该库的程序都无法执行了 通过动态库实现的链接叫做动态链接通过静态库实现的链接叫做静态链接。这里有一个注意点那就是虽然动态库和静态库内部的函数是一样的但是动态库和静态库是两份不同的文件并不是说把动态库拷贝一份到代码中就是静态链接了。这涉及到一些更深的知识就不在gcc的章节中介绍了。
动静态库的后缀 在Linux中动态库以.so为后缀静态库以.a为后缀 在Windows中动态库以.dll为后缀静态库以.lib为后缀 我们直接以一般的形式编译一个文件
gcc -o test1.exe test.c就可以得到一个名为test1.exe的文件。先通过ldd查看相关的库 可以看到每个库中都包含了.so的字段说明gcc在编译时默认使用动态库。
我们也可以通过file指令查看执行file test1.exe dynamically linked字段就表示这是一个动态链接的程序。
如果我们想要生成静态链接的文件则额外加上选项-static
[hxyiZ2zehtehrgzt3wqccrpyfZ gcc]$ gcc -o test2.exe test.c -static此时test2.exe文件就是一个静态链接的文件了。
但是你大概率无法执行该命令会出现以下报错 这是因为Linux默认是不带静态库的要手动安装
yum install -y glibc-static libstdc-static该指令需要root权限要么sudo要么以root身份执行。其中glibc-static是C语言静态库libstdc-static是C静态库。
先对比一下两个文件的大小 可以看到两个文件之间差不多相差了100倍的大小因此静态库非常浪费资源。
使用ldd 其很明确的告诉你这不是一个动态链接的可执行文件not a dynamic executeable。
再用file statically linked字段说明这是一个静态链接的可执行文件。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/85487.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!