1.基本介绍2.基本工具、命令使用
(1)参数汇总(2)常见工具(GCC插件)(3)简单的例子
注:使用测试环境说明 3.稍复杂一点的例子
(1)代码
A base.hB Get_Finalscore.cppC Get_Meanscore.cppD Get_Finalscore.hE Get_Meanscore.hF Who_Passed.cppG Who_Passed.hH main.cpp (2)操作
A 逐一编译文件B 生成静态库文件C 生成可执行文件D 附:生成动态库 4.总结5.参考 1.基本介绍
GCC(GNU Compiler Collection )是由GNU项目产生的优化编辑器,支持各种编程语言、硬件架构和操作系统。gcc在编译程序程序时,分为以下四个阶段:预处理(Pre-Processing)、编译(Compiling)、汇编(Assembling)、链接(linking)。
2.基本工具、命令使用gcc的基本用法是:gcc [options] [filenames],[options]表示参数,[filenames]表示相关文件的名称,一些最基本的参数及含义下表所示:
(1)参数汇总 虚拟机:VM Ware 14
ubuntu:18.04 LTS
gcc版本:7.5.0
具体使用 gcc -v 命令查看如下图所示:
首先我们编写一个简单的例子,输出“Hello World”:
1 #include
然后使用 gcc -E Hello_World.c -o Hello.i 命令生成预处理文件并且使用vim查看 Hello.i 文件,其最后一部分如下图:
然后使用 gcc -S Hello.i -o Hello.s 命令将生成的预处理文件编译成汇编程序 Hello.s,并且使用vim查看 Hello.s 的内容如下图所示:
然后使用 gcc -c Hello.s -o Hello.o 命令将生成的汇编文件编译成目标文件 Hello.o
然后使用 gcc -static Hello.o -o Hello-s 命令将生成的目标文件静态链接最终生成可执行文件 Hello-s,同时使用 gcc Hello.o -o Hello-d 命令将生成的目标文件动态链接最终生成可执行文件 Hello-d (不指定参数默认动态链接),并且分别使用 size 命令查看文件大小如下图所示:(可以看到静态链接的代码大小比动态链接大得多)
分别运行 Hello-s 与 Hello-d 验证可执行文件正确性如下图所示,可以看到静态库的运行时间静态库快一点(注:使用命令:time 可查看运行时间)
此时我们如果直接用 vim 打开 目标文件(.o)或者是可执行文件,会看到一对乱码,可以使用前面表格2中提到的 objdump 工具。使用命令:objdump -S Hello.o>hello-o-ob.txt 将目标文件进行反汇编,并且添加显示汇编源码(-S),重定向输出到TXT文件(>)。使用命令objdump -S Hello-d>hello-d-ob.txt* 将链接了动态库的可执行文件反汇编。然后再查看两个TXT文件,内容如图6所示:
3.稍复杂一点的例子
上面使用的例子比较简单,只使用到了一个源文件,下面我们以简单的学生成绩管理为背景,演示如何使用gcc编译多个源文件、生成库文件、链接库文件等功能。
(1)代码 首先学生成绩管理文件的结构树如图7所示,为了方便演示功能,我特意建立了files1、files2文件夹存放不同的源文件,建立base、includes1、includes2文件夹存放不同的头文件,建立my_lib文件夹存放生成的库文件
base.h 文件主要定义了存放学生成绩的结构体,结构体包括学生姓名、卷面成绩、平时成绩、总成绩四个成员。源代码如下所示:
//===============================================================//// File Name: base.h// Project Name: My_MakeTest// Create Time: 2022-01-20// Creater : NightVoyager//===============================================================//#ifndef _base_#define _base_#ifdef _cplusplusextern "C"{#endif#define MAX_STU_NUM 10 //最大学生数#define PROPORTION 0.7f //卷面占比 typedef struct _STU_GRADE { char Name[4]; //姓名 float Testscore; //考试成绩 float Usualscore; //平时成绩 float Finalscore; //总成绩 } st_Stu_Grade;#ifdef _cplusplus}#endif#endif // _base_
B Get_Finalscore.cppGet_Finalscore.cpp文件主要是个根据传入Get_Finalscore函数的比例计算得到学生的总成绩,源码如下所示:
//===============================================================//// File Name: Get-Finalscore.cpp// Project Name: My_MakeTest// Create Time: 2022-01-20// Creater : NightVoyager//===============================================================//#include "/home/nvtu18/MyCodes/CPrimerCodes/Make_Demo/My_MakeTest/base/base.h"#include "/home/nvtu18/MyCodes/CPrimerCodes/Make_Demo/My_MakeTest/includes1/Get_Finalscore.h"#ifdef __cplusplusextern "C"{#endifshort Get_Finalscore(st_Stu_Grade infolist[], float Propor, int length){ if (Propor <= 1.0) { for (short i = 0; i < length; i++) { infolist[i].Finalscore = infolist[i].Testscore * Propor + infolist[i].Usualscore * (1.0 - Propor); } return 0; } else { return -1; }}#ifdef __cplusplus}#endif
C Get_Meanscore.cppGet_Meanscore.cpp文件主要是计算学生的平均成绩,源码如下所示:
//===============================================================//// File Name: Get_Meanscore.cpp// Project Name: My_MakeTest// Create Time: 2022-01-20// Creater : NightVoyager//===============================================================//#include "/home/nvtu18/MyCodes/CPrimerCodes/Make_Demo/My_MakeTest/includes2/Get_Meanscore.h"#include "/home/nvtu18/MyCodes/CPrimerCodes/Make_Demo/My_MakeTest/base/base.h"#ifdef __cplusplusextern "C"{#endiffloat Get_Meanscore(st_Stu_Grade infolist[], int lenth){ float temp; for (short i = 0; i < lenth; i++) { temp = temp + infolist[i].Finalscore; } return (temp / float(lenth));}#ifdef __cplusplus}#endif
D Get_Finalscore.h//===============================================================//// File Name: Get_Finalscore.h// Project Name: My_MakeTest// Create Time: 2022-01-20// Creater : NightVoyager//==============================================================//#ifndef _GET_FINALS_#define _GET_FINALS_#include "/home/nvtu18/MyCodes/CPrimerCodes/Make_Demo/My_MakeTest/base/base.h"#ifndef _cplusplusextern "C"{#endifshort Get_Finalscore(st_Stu_Grade infolist[], float Propor, int length);#ifndef _cplusplus}#endif#endif //_GET_FINALS_
E Get_Meanscore.h//===============================================================//// File Name: Get_Meanscore.h// Project Name: My_MakeTest// Create Time: 2022-01-20// Creater : NightVoyager//==============================================================//#ifndef _GET_MEANS_#define _GET_MEANS_#include "/home/nvtu18/MyCodes/CPrimerCodes/Make_Demo/My_MakeTest/base/base.h"#ifndef _cplusplusextern "C"{#endiffloat Get_Meanscore(st_Stu_Grade infolist[], int lenth);#ifndef _cplusplus}#endif#endif //_GET_MEANS_
F Who_Passed.cppWho_Passed.cpp文件主要判断哪些学生通过了考试(总成绩大于60分),并且将名字返回给name[][4]二维数组,源码如下所示:
//===============================================================//// File Name: Who_Passed.cpp// Project Name: My_MakeTest// Create Time: 2022-01-20// Creater : NightVoyager//===============================================================//#ifdef __cplusplusextern "C"{#endif#include
//===============================================================//// File Name: Who_Passed.h// Project Name: My_MakeTest// Create Time: 2022-01-20// Creater : NightVoyager//==============================================================//#ifndef _WHO_PASS_#define _WHO_PASS_#include "/home/nvtu18/MyCodes/CPrimerCodes/Make_Demo/My_MakeTest/base/base.h"#ifndef _cplusplusextern "C"{#endifvoid Who_Passed(st_Stu_Grade p[], char name[][4] , int length);#ifndef _cplusplus}#endif#endif //_WHO_PASS_
H main.cppmain.cpp文件创建了一个10个成员的学生成绩结构体数组,用来模拟学生的真实成绩,然后通过调用Get_Finalscore()函数得到并打印第六个学生的最终成绩;调用Get_Meanscore()函数得到并打印学生的平均成绩;调用Who_Passed()并且打印通过学生的名字。
//============================================================================// File Name: main.cpp// Project Name: My_MakeTest// Create Time: 2022-01-20// Creater : NightVoyager//============================================================================//Note(s):makefile 基础学习简单例子,有Get_Finalscore.cpp、// Get_Meanscore.cpp、Who_Aassed.cpp、main.cpp 四个源文件// 分别对应:根据比例求最终成绩,获取平均成绩,输出通过同学的名字,主程序四个// 功能,Who_passed.cpp被编译成lib文件。 //============================================================================#include
程序编写完毕后,我们开始一步步编译:
首先编译Get_Finalscore.cpp文件:
g++ -c ./files1/Get_Finalscore.cpp -o ./files1/Get_Finalscore.o -I includes1 -I base
同理编译Get_Meanscore.cpp文件:
g++ -c ./files2/Get_Meanscore.cpp -o ./files2/Get_Meanscore.o -I includes2 -I base
上面的用到的命令和前面在编译Hello World时的区别是:①上面编译的源文件不是在命令输入的路径下,所以需要使用到相对路径(或是绝对路径)指定;② -I includes1 -I base 指定了源文件所依赖的头文件路径。(注:由于是编译CPP文件,故使用的是g++)逐一编译文件截图如下所示:
有时候我们希望给别人提供函数接口调用但是又不想让别人知道我们是如何实现的,或者是方便对源文件进行管理,我们可以将一个或多个的源文件生成库文件,然后提供头文件给别人调用。将Who_Passed.cpp生成库文件演示此功能:
g++ -c ./my_lib/Who_Passed.cpp -o ./my_lib/Who_Passed.o -I my_lib -I base
ar -crv ./my_lib/libtools.a ./my_lib/Who_Passed.o
上面的命令中,使用到了ar用于库的创建,参数 -crv 的含义分别是:创建一个库,不管库是否存在,都将创建(-c);在库中插入模块(替换),当插入的模块名已经在库中存在,则替换同名的模块(-r);显示执行操作选项的附加信息(-v)。然后查看my_lib路径下确实生成了库文件,如下图所示:
根据A B的操作,我们已经生成了 Get_Finalscore.o、Get_Meanscore.o、libtools.a三个文件,接下来我们利用这三个文件生成可执行文件main:
g++ main.cpp -o main -I includes1 -I includes2 -I base ./files1/Get_Finalscore.o ./files2/Get_Meanscore.o -L./my_lib/ -ltools
上面的命令将main.cpp文件编译生成了可执行文件main,链接了两个目标文件,并且通过:-L./my_lib/ -ltools将库文件链接进来。需要注意的是,我们在B步时,对库文件的命名是 libtools.a,然后在此处链接的时候用的是 -ltools,需要去除前缀libs。查看结果,可以看到生成了可执行文件main,执行main,输出正确如下图所示:
前面提供了生成静态库的方法,那么如果要生成动态库要怎么做呢?这个时候就要用到表格2中提到的 -fPIC 参数了,待我一一道来:
同样的,首先逐一生成目标文件:(此时需要加上 -fPIC参数)
g++ -c ./files1/Get_Finalscore.cpp -o ./files1/Get_Finalscore.o -I includes1 -I base -fPIC
g++ -c ./files2/Get_Meanscore.cpp -o ./files2/Get_Meanscore.o -I includes2 -I base -fPIC
g++ -c ./my_lib/Who_Passed.cpp -o ./my_lib/Who_Passed.o -I my_lib -I base -fPIC
然后生成动态库文件:
gcc --share -fPIC -o ./my_lib/libtools.so ./my_lib/Who_Passed.o -Iincludes2 -Iincludes1 -Ibase
生成可执行文件:
g++ main.cpp -o main -I includes1 -I includes2 -I base ./files1/Get_Finalscore.o ./files2/Get_Meanscore.o -L./my_lib/ -ltools
使用ldd 查看链接的库,发现我们自己的库没有找到,运行main也报错了,如图11所示,为了解决错误,我们需要添加我们的库文件到库路径,采用如下命令:export LD_LIBRARY_PATH=./my_lib:$LD_LIBRARY_PATH 这样就可以找到了,如图12所示。
这样的话,我们的动态库便制作完成了,另外使用 -L./my_lib/ -ltools 的时候会优先使用动态库,我们可以使用参数 -static 指定使用静态库,即:g++ main.cpp -o main -I includes1 -I includes2 -I base ./files1/Get_Finalscore.o ./files2/Get_Meanscore.o -static -L./my_lib/ -ltools
4.总结整篇博客主要介绍了 gcc的一些基本操作方法、一些参数的含义以及其附带的一些组件的用法。采用一个简单的Hello World例子和稍微复杂的学生成绩列表例子对这些操作方法,组件使用进行了实际操作。整个过程下来,其实可以感觉得直接在命令行输入命令的效率比较低,而且当有了几十个以上的源文件时,这样做简直要命。这时,强大的makefile工具便出现了,具体见我下一篇博客。本人不才,必有疏漏,望指正。
5.参考https://gcc.gnu.org/onlinedocs/gcc-11.2.0/gcc.pdfhttps://sourceware.org/binutils/docs/https://zhuanlan.zhihu.com/p/456125607https://blog.csdn.net/superinzaghi747/article/details/32318249?_t=thttps://www.cnblogs.com/kele-dad/p/9394568.html