Linux 编译器--gcc
目录从代码到可执行程序1. 预处理 2.编译 3. 汇编 4. 链接编译器gccgcc 命令集说到编译器, 就不得不说一个程序从代码到可执行的过程了从代码到可执行程序我们的源代码到一个可执行程序可不是一步到位, 需要以下几个步骤:预处理 --> 编译 --> 汇编 --> 链接1. 预处理对源代码进行宏替...
目录
说到编译器, 就不得不说一个程序从代码到可执行的过程了
从代码到可执行程序
我们的源代码到一个可执行程序可不是一步到位, 需要以下几个步骤:
预处理 --> 编译 --> 汇编 --> 链接
1. 预处理
对源代码进行宏替换, 文件展开, 去注释
我们在linux下看一下各个过程, 我们写一个简单的.c文件, 如下图 , 注意, 我故意写错了一个, 在第一个printf后面没写分号
我们保存后, 可以用gcc将a.c中的代码预处理成一个新的a.i文件,命令为gcc -E a.c -o a.i , 这里不用关注这句命令具体的含义, 只需要知道是预处理就可以了, 下面我们会详细讲gcc/g++的用法
我们打开生成的a.i文件, 如下图 :
可以看到, 注释被去掉了, 宏被原地展开了, 但a.i文件竟然变成了800多行, 前面a.c可只有12行, 这是因为, 预处理还会文件展开嘛, 预处理将头文件stdio.h展开了
2.编译
语法语义检测, 优化代码, 并生成汇编代码
前面a.c中写错, 就是为了方便编译测试, 我们可以用gcc -S a.i -o a.s 这个命令来用a.i 文件编译生成新的a.s文件, 我们来试一下, 能否编译通过, 结果如图
可见, 编译时进行了语法语义的检测, 再将a.c改正确, 删掉原来的a.i, 将a.c重新预处理成a.i, 再编译, 结果生成了a.s文件, 编译通过, a.s内容如下图
可以看到, 编译将高级语言C转换成了汇编语言
3. 汇编
将汇编代码生成机器可识别代码
我们用gcc -c a.s -o a.o 这个命令用a.s汇编成新的a.o文件, 打开a.o文件如下图
可以看到, 全是些人看了头大, 却是计算机唯一能读懂的二进制, 这就是汇编代码生成的机器语言
4. 链接
什么是连接?
简单来说,就是把程序用到的所有源文件的目标文件合成一个可执行目标文件,是一个“多合一”的过程。
两种链接: 链接分为两种, 动态链接和静态链接, 下面简单介绍一下这两种
函数库: 函数库依照是否被链接到程序内部而分为静态库和动态库(共享库)
静态库扩展名: lib***.a 动态库扩展名: lib***.so
静态链接: 在链接时会将静态库直接整合到执行程序中,所以生成的可执行文件比较大, 编译成功的可执行文件可以独立执行, 不再需要外部的函数库
动态链接:在链接后生成的可执行文件中只有对动态库的引用(只链接了指针),当文件要使用函数库里的功能时,程序才会读取出函数库来使用。由于执行文件当中仅仅具有指向动态函数库的指针, 所以生成的可执行文件较小。生成的可执行程序不能独立执行,要执行的话, 动态库文件必须存在,且动态库所在的目录也不能被改变, 使用时,程序才会主动去某个路径下读取,所以动态库不能被随意删除。
静态链接库对程序的更新、部署、发布带来麻烦。如果静态库更新了, 使用它的应用程序都需要重新编译, 发布给用户(好比游戏, 对于玩家来说, 可能是一个很小的改动,却要导致整个程序重新下载,全量更新). 或当静态链接的程序多份运行或有多个源文件链接了静态库, 则会在内存中造成代码冗余
动态库在程序编译时并不会被链接到目标代码中,而是在执行文件中记录对动态库的引用,在程序运行时才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行时才被载入,也解决了静态库对程序的更新、部署和发布带来的麻烦,用户只需要更新动态库即可,增量更新。
Linux下默认是动态链接
我们执行gcc a.o -o a 生成可执行程序a , 执行程序如下图 :
编译器gcc
GCC(GNU Compiler Collection,GNU编译器套件),是由 GNU 开发的编程语言编译器。
GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理C语言。GCC 很快地扩展,变得可理 C++。后来又扩展能够支持更多编程语言, 所以改名GNU编译器套件(GNU Compiler Collection)。
gcc的安装命令 yum install gcc
g++的安装命令 yum install gcc-c++
gcc 命令集
- -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
- -S 编译到汇编语言不进行汇编和链接
- -c 编译到目标代码
- -o 文件输出到 文件
- -static 此选项对生成的文件采用静态链接
- -g 生成调试信息。生成debug版本的可执行文件. GNU 调试器可利用该信息。
- -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
- -O0, -O1, -O3, 编译器的优化选项的4个级别,依次优化级别越高, 也就编译越慢
- -w 不生成任何警告信息。
- -Wall 生成所有警告信息。
- -L 添加优先搜索库文件路径
实例 :
假设源程序文件名为test.c
无选项编译链接 gcc test.c ,将test.c预处理、汇编、编译并链接形成可执行文件。这里未指定输出文件,默认输出为a.out。
选项 -o
gcc test.c -o test将test.c预处理、汇编、编译并链接形成可执行文件test , -o选项用来指定输出文件的文件名。
选项 -E
gcc -E test.c -o test.i 将test.c预处理输出test.i文件
选项 -S
gcc -S test.i将预处理输出文件test.i汇编成test.s文件
选项 -c
gcc -c test.s将汇编输出文件test.s编译输出test.o文件
无选项链接 gcc test.o -o test将编译输出文件test.o链接成最终可执行文件test
选项 -O
gcc -O1 test.c -o test使用编译优化级别1编译程序
多源文件的编译方法:
如果有多个源文件,基本上有两种编译方法: 假设有两个源文件为test.c和testfun.c
多个文件一起编译
gcc testfun.c test.c -o test 将 testfun.c和test.c分别编译后链接成test可执行文件。
分别编译各个源文件, 之后对编译后输出的目标文件链接。
gcc -c testfun.c //将testfun.c编译成testfun.o
gcc -c test.c //将test.c编译成test.o
gcc -o testfun.o test.o -o test //将testfun.o和test.o链接成可执行文件test
以上两种方法相比较,第一中方法编译时需要所有文件重新编译,而第二种方法可以只重新编译修改的文件,未修改的文件不用重新编译。
一起编译 如下:
分开编译再链接如下 :
再将其testfun.c改为如下, 保存, 再编译后, 与test.o链接成可执行文件
可以看到, 修改testfun.c文件后, 只需要把testfun.c重新编译一遍再与test.o链接就好了
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)