Linux编译器-gcc/g++使用 make/makefile最基础的功能 Linux调试器-gdb的使用

一.引例:

C语言

下面代码可能编不过去
在这里插入图片描述
因为编译器的版本没达到,需要手动改版本
在这里插入图片描述
默认形成的可执行文件是a.out
在这里插入图片描述
也可以更改形成的文件名-o 是object的意思,形成的可执行文件后缀随便加

gcc test.c -o my.exe -std=c99

在这里插入图片描述

C++

C++文件后缀不仅仅有.cpp和.cc,还有很少见到的.cxx
gcc不能编译C++的代码,但g++能编译C的代码

g++ test.cpp -o my.exe -std=c++11

二.程序翻译的过程

在这里插入图片描述

预处理

预处理把头文件直接展开,10来行变成800多行
在这里插入图片描述
Linux里面有库才能使用头文件,并且预处理之后头文件展开的位置能直接看到库存在的位置
在这里插入图片描述
预处理阶段宏会被替换,注释也被去掉
在这里插入图片描述

条件编译

引例:同样一种软件,有社区版,专业版,免费版。
那么维护代码的时候需要维护几份源代码?
维护一份代码即可,怎么做到的?
以以下代码为例
在这里插入图片描述
运行就会发现只打印了一部分
在这里插入图片描述
预处理就被裁了
在这里插入图片描述
然后再做一下改变,V1随便给个值

#include <stdio.h>                                                                                                
#define V1 1                               
                                 
int main()                                 
{                                          
#ifdef V1                                  
	printf("功能1\n");                     
#elif V2                                   
	printf("功能1\n");                     
	printf("功能2\n");                     
#else                                      
	printf("功能1\n");                     
	printf("功能2\n");                     
	printf("功能3\n");                     
#endif                                     
	return 0;                              
}    

运行
在这里插入图片描述
其中#ifdef #elif 等就是条件编译
这样裁剪可以定义出不同版本的软件
宏也可以在外面定义

gcc -D V1=1 proj.c -o filename

在这里插入图片描述

编译

gcc -S test.i -o test.s -std=c99

很明显,就是汇编语言
在这里插入图片描述

汇编

gcc -c test.s -o test.o

就是.obj结尾的文件
在这里插入图片描述
内容是形成二进制的目标文件
在这里插入图片描述
还是运行不了
在这里插入图片描述

链接

gcc test.o -o my.exe

就可以直接运行了
在这里插入图片描述

三.链接–动静态链接

链接是什么?

程序与库结合的过程
引例:从零开始写代码,假设设计一个printf函数,设计好了大半个月过去了,效率比较低,所以语言设计者把一些公共的方法抽取出来,放进自己的标准库
下面的指令是查看文件调用了哪些动态库

ldd filename

在这里插入图片描述
查看画横线的文件,没形成C语言库之前(就是C语言的源代码),被顶级程序员打包成库
在这里插入图片描述
我们怎么知道C标准库里面有什么?所以就有一批对应的头文件,方便调用
在这里插入图片描述
所以安装开发环境:安装C标准库 + C头文件

动静态库

在这里插入图片描述

为什么要有库?

让开发站在巨人的肩膀
提高开发效率

怎么办?

故事:

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
故事来到第二阶段:张三去不了网吧了(无法调用动态库),计划不能完整的执行了
在这里插入图片描述
第三阶段:
在这里插入图片描述

总结:所有动态链接都需要跑到库中执行,然后返回,静态链接不需要

证明:

C动态库,是Linux默认提供的
gcc默认形成的可执行程序,默认采用动态链接
在这里插入图片描述
ls命令也是同理
在这里插入图片描述
要是删掉的话,就相当于上述故事中的警察查封网吧,大部分指令都运行不了了
静态库的位置,文件以.a结尾
在这里插入图片描述

优缺点

下面的指令是查看文件详细信息

file filename

在这里插入图片描述
动态库与动态链接的优缺点:
1.不能丢失(网吧不能去了就玩不了)
2.节省资源(网吧的电脑共用)
静态库与静态库的优缺点:
1.一旦形成就与库无关(自己有电脑就不用去网吧了)
2.浪费资源(自己的电脑自己用)
验证:
查看静态库的指令(mytest-static是形成test.c静态库的名称,名字随便取)

gcc -o mytest-static test.c -static -std=c99

会发现运行不了,找不到C语言的静态库
在这里插入图片描述
是因为默认情况下,Linux上一般静态库都是默认没有安装的
yum安装即可

sudo yum install -y glibc-static libstdc++-static

对比一下:
在这里插入图片描述
文件大小上静态比动态大很多
在这里插入图片描述

静态链接的应用场景

提高可移植性,比如直接把二进制代码从这个A机器到B机器,跨平台性提高,不依赖动态库,不用做过多的环境监测

四.make/makefile

原理:

make是一个命令
makefile是一个文件
创建makefile和创建普通文件的命令是一样的

touch makefile

以下代码为例
在这里插入图片描述
效果就是下面
在这里插入图片描述
在这里插入图片描述

关于依赖关系和依赖方法的理解,这里讲一个小故事:
月底了,身为大学生的你没钱了,拨通了父亲的电话:我是你儿子,然后你挂了电话。
我是你儿子这句话就表明了依赖关系,但是目的没达成,所以只有依赖关系没用
然后又打通电话:我是你儿子,没钱了打钱。你父亲就明白了
这就是表明了依赖关系又表明了依赖方法
发现在前面使用的时候,make是运行的第一段,make clean是第二段,同时make mytest也可以运行第一段
在这里插入图片描述
因为make是从上面向下运行的
在这里插入图片描述
把makefile反过来验证一下:
在这里插入图片描述
在这里插入图片描述
至于.PHONY是什么意思这里讲解一下:
以下面的makefile文件为例

mytest:test.c
	gcc -o mytest test.c                                                                                            
clean:
	rm -f mytest 

会发现第二次make的时候会说:此文件已经是最新的
在这里插入图片描述
但如果更改test.c的内容后,就可以再次make。
因为系统认为test.c已经编译过了,且没有最新的更改,所以没必要执行。
加了.PHONY后就可以一直执行

.PHONY:mytest                                                                                                     
mytest:test.c
	gcc -o mytest test.c
clean:
  	rm -f mytest

在这里插入图片描述
.PHONY:XXX
表示XXX对应的方法,总要被执行的
一般写makefile的时候,形成可执行程序,源代码没有更新,没有必要编译
清理项目希望是被总是执行的,因为有些文件想要清理干净

为什么makefile对最新的可执行程序,默认不想重新生成呢?

假设一个项目有两千个源文件,很小的一部分需要做改动,如果全部编译效率很慢。
提高编译效率

makefile怎么知道我的程序需要被编译了呢?

源文件的更改时间和可执行程序更改的时间是不一样的
源文件:
在这里插入图片描述
可执行文件:
在这里插入图片描述
对比可执行文件的最近修改时间和源文件最近的修改时间,判断谁最新?
以此来判断是否需要编译文件

在这里插入图片描述
.PHONY的意思是直接编,不用比较
平时在写VS的时候,有时候会出错,清理重新编译后,就可以执行。原因就是不同的环境更新时间的策略不一样。

makefile中的内置符号

在这里插入图片描述
make的时候,他会自动替换目标文件
在这里插入图片描述
在makefile中注释是#
在这里插入图片描述
多个指令的实现也可以
引例:

test.exe:test.o
  gcc test.o -o test.exe
test.o:test.s
  gcc -c test.s -o test.o
test.s:test.i
  gcc -S test.i -o test.s
test.i:test.c
  gcc -E test.c -o test.i
  
.PHONY:clean
clean:
   rm -f test.s test.i test.o test.exe

在这里插入图片描述
在这里插入图片描述

假设依赖文件找不到就会报错
在这里插入图片描述
在这里插入图片描述
重要的一点来了,可以乱序,但最终的目标文件必须是第一个
在这里插入图片描述
在这里插入图片描述
最终要的test.exe可执行程序并没有形成

makefile也支持变量

bin=mytest
src=test.c
$(bin):$(src)
  gcc -o $@ $^
.PHONY:clean
clean:
  rm -f $(bin)

在这里插入图片描述
在这里插入图片描述
就跟宏一样

不想见到执行的命令

加个@就行
在这里插入图片描述
在这里插入图片描述
又想隐藏程序,又想看到执行的什么命令,可以自己设置
这里echo也是一个命令,所以要加上@
在这里插入图片描述
在这里插入图片描述

五.gdb调试器

在这里插入图片描述
如何证明有调试信息?

readelf -S test.debug

其中有读取的debug信息
在这里插入图片描述
过滤一下
在这里插入图片描述
再看一下release版本的,没有
在这里插入图片描述
Linux下的任何可执行程序都是ELF格式(就是二进制的一种形式)
readelf就是读取可执行程序

使用gdp

下载gdb的指令

yum install -y gdb

为了方便演示,将以以下代码为例:

#include <stdio.h>

int AddToTarget(int start, int end)
{
    int i = start;
    int sum = 0;
    for(; i <= end; i++)
    {
        sum += i;
    }

    return sum;
}

int main()
{
    printf("run begin...\n");

    int result = 0;
    result = AddToTarget(1,100);
    printf("result: %d\n", result);

    printf("run end...\n");
}

进入gdb工作模式

gdb test.debug

在这里插入图片描述
quit 退出gdb

总结gdb基础指令

方便查表,先总结一下

功能全称简称
退出gdbquit
查看源代码list (文件名:)(行号/函数名)l (文件名:)(行号/函数名)
打断点b (文件名:)(行号/函数名)
查看断点infoi
删除断点d 断点编号
使断点关闭disable 断点编号
使断点打开enable 断点编号
在某个函数开头设置断点break 函数名
逐过程(F10)nextn
逐语句(F11)steps
查看变量内容或地址printp
显示变量内容或地址(相当于监视)display 变量或变量地址
删除显示的变量内容或地址(相当于监视)undplay 变量或变量地址的序号
运行至下一个断点处contiunec
运行结束所在函数,就停下来finish
跳转至指定行,中间的代码都是运行完的until 行数
查看当前栈帧局部变量的值info local
修改变量的值set var
查看调用栈(函数)bt

list

list 查看源代码
在这里插入图片描述
从头部开始查 list简写成l 跟个行号0
在这里插入图片描述
l 文件名:行号也可以,查其它文件也行
在这里插入图片描述
gdb会默认记住最近的一条命令的,直接按回车
在这里插入图片描述
查行数会发现默认15在中间
在这里插入图片描述
也可以查函数
在这里插入图片描述

运行与断点

run简写成r
就像VS点以下F5调试,不打断点就直接运行完成
在这里插入图片描述
打断点b (文件名:)序号
b (文件名:)函数名
可以看到断点的序号
在这里插入图片描述
查看断点info b
在这里插入图片描述
去掉断点d加断点序号
在这里插入图片描述
先打4个断点,发现断点序号不是从1开始,而是接着上次叠加
在这里插入图片描述
运行发现在17行停下来
在这里插入图片描述
如果想要禁掉断点,但不删
disable 断点序号
在这里插入图片描述
再次运行就是19行,因为18行是空行
在这里插入图片描述
打开断点
enable 断点序号
在这里插入图片描述
break 函数名:在某个函数开头设置断点
在这里插入图片描述

逐过程与逐语句

C语言就是面向过程,F10就是逐过程,遇到函数直接越过去执行完。逐语句就是遇到函数进去
在这里插入图片描述
17行已知先打一个断点
在这里插入图片描述
逐过程全称next 简写为n
因为18行没东西,所以直接到19行了
在这里插入图片描述
并没有进到函数内部
在这里插入图片描述
如果想进到函数内部,就是逐语言F11,指令为s
在这里插入图片描述
调试过程中我们还想要看到局部变量,也就是监视或自动窗口
在这里插入图片描述
想查看i变量print i 简写称p i
在这里插入图片描述
查看地址也可以
在这里插入图片描述
过于麻烦,需要每次输入
display 可以查看的变量 可以解决
在这里插入图片描述
如果想删掉监视的内容
undisplay 监视的序号
在这里插入图片描述

区域性的执行

运行至下一个断点处 contiune 简写为c
首先有三个断点
在这里插入图片描述
效果如下
在这里插入图片描述
假设问题可能在函数里面
finish指令 运行结束所在的函数,就停下来
在这里插入图片描述
假如被循环困住了
在这里插入图片描述
跳出去的指令为until 指定行
在这里插入图片描述

扩展

info local/locals 查看当前栈帧局部变量的值
在这里插入图片描述
set var 修改变量的值
在这里插入图片描述
bt 查看调用栈
在这里插入图片描述
查看函数用的
在这里插入图片描述
效果
在这里插入图片描述

  • 12
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浅碎时光807

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值