gcc/g++编译器的使用
gcc如何使用
语法: gcc [选项] 编译文件
功能: 用于编译C语言程序,编译C++程序使用g++。
选项:
指令 | 说明 |
---|---|
-E | 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面 |
-S | 编译到汇编语言不进行汇编和链接 |
-c | 编译到目标代码 |
-o | 文件输出到文件 |
-static | 此选项对生成的文件采用静态链接 |
-g | 生成调试信息。GNU 调试器可利用该信息 |
-shared | 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库 |
-O0(0-3) | 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高 |
-w | 不生成任何警告信息。 |
-whall | 生成所有警告信息。 |
注意:gcc编译器编译不是直接生成可执行程序的,什么参数都不给的情况下它默认执行:预处理->编译->汇编->链接
预处理
预处理是程序执行的第一步,这一步主要是用于删除注释、头文件展开、宏替换和条件编译这几项操作,我们可以使用gcc的
-E
选项来进行查看它对源代码进行了那些处理。
我们可以从图中观察到,这个阶段gcc确实进行了以上处理。
编译
编译这个阶段主要做的工作就是将C代码编译成汇编代码。
汇编
这个阶段主要是生成目标文件,也就是windows下的obj文件而obj文件是二进制的因为已经成为机器码了,所以我们显示器上是无法显示出来的,通常能显示出来的都是乱码。
链接
这个阶段主要是用于链接,虽然代码已经生成为机器码了,cpu能看懂但是它找不到里面的函数调用的地址,所以我们只需进行链接obj文件即可找到对应的函数地址,这个时候代码才是可执行程序。
由于链接不要参数直接输入
gcc obj文件
这个时候会得到a.out的可执行程序,如果有重命名的话那么就不是a.out。
[ls@VM-4-7-centos 11]$ ll
total 44
-rwxrwxr-x 1 ls ls 8400 Dec 11 22:23 a.out
-rw-rw-r-- 1 ls ls 401 Dec 11 22:08 test.c
-rw-rw-r-- 1 ls ls 16921 Dec 11 22:13 test.i
-rw-rw-r-- 1 ls ls 1569 Dec 11 22:19 test.o
-rw-rw-r-- 1 ls ls 507 Dec 11 22:19 test.s
[ls@VM-4-7-centos 11]$ ./a.out
heello centos
hello world
[ls@VM-4-7-centos 11]$
注意: gcc默认生成调用的是动态链接,如果需要静态链接需要带上gcc -static
编程静态链接
库
什么是库?你可能会说我没听过库啊?而且也没使用过!
其实你一直都在使用库像C语言的头文件或者C++的头文件等等……你日常的写练习代码都是用过的,只是你没察觉到而那个阶段也不会有人跟你讲库的概念,所以你就会认为我从来没使用过库,而库又分为两种。
- 静态库
- 动态库
静态库
静态库顾名思义就是静态的,那么怎么个静态法呢?
像刚才上面的预处理阶段,我们就看到头文件展开了而且展开了很多直接让我们的代码从几十行增加到八百多行,而这是为什么呢?这其实就是静态库,静态库就是使用自己代码下面的函数时候不会去调外面的,而这样做的代价就是内存和磁盘占用就很大因为它是直接从动态库内拷贝过来的代码,但是它有一个优点不依赖第三方库任何平台都能跑移植性高。
总结:
- 优点
- 移植性高
- 不依赖第三方库
- 缺点
- 体积大
- 加载慢
- 存在空间浪费
动态库
动态库本质就是使用官方库里的函数,但是不会对其进行拷贝到本地,具体过程是:代码使用库函数->调用库->库返回调用结果。
这样做的优点就是不用会产生重复拷贝,使得代码占用体积小,内存加载更快,但是依赖第三方库平台不同库里面的内容也会不些小变动这使得移植性较差。
总结:
- 优点
- 内存加载快
- 体积小
- 不存在空间浪费
- 缺点
- 依赖第三方库
- 可移植性较差
gdb调试工具
注意: gcc编译出来的默认是release
版本,这个版本是没有 调试信息的,我们需要手动调整成debug
版本才有调试信息,需要在编译时加上-g
选项才是debug
版本。
- list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
- list/l 函数名:列出某个函数的源代码
- r或run:运行程序
- n 或 next:单条执行
- s或step:进入函数调用
- break(b) 行号:在某一行设置断点
- break 函数名:在某个函数开头设置断点
- info break :查看断点信息。
- fifinish:执行到当前函数返回,然后挺下来等待命令
- print§:打印表达式的值,通过表达式可以修改变量的值或者调用函数
- p 变量:打印变量值。
- set var:修改变量的值
- continue(或c):从当前位置开始连续而非单步执行程序
- run(或r):从开始连续而非单步执行程序
- delete breakpoints:删除所有断点
- delete breakpoints n:删除序号为n的断点
- disable breakpoints:禁用断点
- enable breakpoints:启用断点
- info(或i) breakpoints:参看当前设置了哪些断点
- display 变量名:跟踪查看一个变量,每次停下来都显示它的值
- undisplay:取消对先前设置的那些变量的跟踪
- until X行号:跳至X行
- breaktrace(或bt):查看各级函数调用及参数
- info(i) locals:查看当前栈帧局部变量的值
- quit:退出gdb
makefile自动化编译工具
makefile自动化工具能使我们减少编译时要敲的代码,同时会不会用makefile也是考察你是否具有大型项目编程的能力,因为在LInux下大型项目有上百个obj文件,如果手打的话你可以想象一下效率有多低,其次如果除了bug调试的时候这样又打一遍岂不是折磨?
所以我们有必要掌握makefile这款自动化编译工具,有了它我们开发效率会比没有使用它更快,特别是当做大项目的时候有明显的差距。
make指令
语法: make
功能: 用于执行makefile
的内容,使用make前必须有个makefile
不然无法使用。
makefile的语法
使用makefile必须明白依赖关系和依赖方法,什么是依赖关系呢?依赖关系就好比你使用某个库的函数你必须包含这个库的头文件,这就是依赖关系那么依赖方法呢?
依赖方法也很简单可以理解成调用库函数的具体操作,在Linux下以上操作都是类似的。
具体操作我们先创建一个makefile文件,接着在里面写上如下指令:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AqXW8IH8-1675340707262)(https://cdn.jsdelivr.net/gh/ls02/Image/img/image-20211212181613038.png?raw=true)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JlMMwr8s-1675340709825)(null)]
这些已经可以完成绝大多数场景的使用了,暂时先讲到这,后续的文章会补充这个工具的其它操作。
生成多个可执行文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nSND3KDU-1675340709792)(null)]