Linux | 调试器GDB的详细教程【纯命令行调试】
文章目录
- 一、前言
- 二、调试版本与发布版本
- 1、见见gdb
- 2、程序员与测试人员
- 3、为什么Release不能调试但DeBug可以调试❓
- 三、使用gdb调试代码
- 1、指令集汇总
- 2、命令演示
- ⌨ 行号显示
- ⌨ 断点设置
- ⌨ 查看断点信息
- ⌨ 删除断点
- ⌨ 开启 / 禁用断点
- ⌨ 运行 / 调试
- ⌨ 逐过程和逐语句
- ⌨ 打印 / 追踪变量
- ⌨ 查看函数调用
- ⌨ 修改变量的值
- 排查问题三剑客🗡
- ⌨ 指定行号跳转
- ⌨ 强制执行函数
- ⌨ 跳转到下一断点
- 四、实战演练:Swap Two Numbers
- 五、总结与提炼
一、前言
- 学习了【vim】知道了如何编辑一个代码文本
- 学习了【gcc】知道了如何编译一个代码文本
- 学习了【make/Makefile】知道了如何自动化构建一个代码文本
但是如何对一段代码去进行调试呢,此时就要使用到Linux下的调试器gdb
了。对于这个调试器来说,不像是VS中那样的图形化界面形式,而是采用纯命令行的形式进行调试。可能我的讲解会比较晦涩难懂,在学习的过程中主要是会一些gdb下基本的操作即可
二、调试版本与发布版本
1、见见gdb
下面是本次调试所要使用到的代码
1 #include <stdio.h>2 3 int AddToTop(int top)4 {5 printf("Enter AddToTop\n");6 7 int count = 0;8 for(int i = 1;i <= top; ++i)9 {10 count += i;11 }12 13 printf("Quit AddToTop\n"); 14 return count;15 }16 17 int main(void)18 {19 int top = 100;20 int ret = AddToTop(top);21 22 printf("ret = %d\n", ret);23 return 0;24 }
下面是Makefile中的内容,用于自动化编译
1 mytest:test.c2 gcc -o mytest test.c -std=c99 3 .PHONY:clean4 clean:5 rm -rf mytest
注:-std=c99
表示以c99的标准来编译代码
- 如果要进入gdb开始调试,那直接
gdb + 可执行程序
即可 - 不过进去之后发现似乎有一些奇怪的内容,【no debugging symbols found】,翻译过来就是没有调试信息。那这是为何呢?是gdb出问题了吗?
- 先不要着急,如果有经常调试的通过就可以知道只有在【DeBug】的环境下才会有我们想要的调试信息,所以可以初步推断这可能不是一个【DeBug】版本的可执行程序
- 先使用
q(quit)
退出gdb
让我们先看下去,了解一下其他的知识再来解决这个问题
2、程序员与测试人员
接下去我们就来说说有关【DeBug】和【Release】版本的不同之处
📚【Debug】—— 调试版本
📚【Release】—— 发布版本
- 在使用VS的时候我们可以直接使用鼠标来进行操作,当前程序以DeBug或者是Release的形式进行运行,那么运行出来的可执行程序版本也是不同的,我们程序员在编写代码后运行一般是使用【DeBug】环境进行运行。因为在企业里写软件项目,将代码写完后程序员自己要做简单的测试,保证代码没有问题
- 当程序员自己测试完没有问题之后,就会将这个可执行程序给到
测试人员
进行测试,而且会给出自己的单元测试报告。对于测试人员来说所处的模式是【Release】,也就是将来客户要使用的这款软件的发布版本 - 当测试在测的过程中,一定会发现一些问题。此时测试人员就会把报告再打回研发部。研发部做修改重新生成Release版本的可行性程序给到测试人员继续测试
- 最后只有当测试通过了,再将生成的【单元测试报告】与产品经理进行核对之后没有问题,那这个软件才可以真正地面向市场👉
3、为什么Release不能调试但DeBug可以调试❓
其实对于我们刚才直接make自动化生成的可执行程序是通过gcc直接编译产生得到的,它是一个【Release】版本的可执行程序,因此无法进行调试
- 若是我们想要使用
gcc/g++
去生成一个可执行程序时,默认是【Release】版本的,而不是【DeBug】 - 但若是我们想要去生成一个【DeBug】版本的可执行程序也是可以的,只需要修改一下我们的Makefile即可,给gcc后面带上一个
-g
的命令选项,此时再去make一下的话生成的就是【DeBug】版本的了
- 为了之前的【Release】版本不被覆盖,我们将其重命名一下为
mytest-release
- 在生成【DeBug】版本后一样对其进行一个重命名为
mytest-debug
-
通过观察上图中两个可执行文件的大小便可以发现虽然它们都是可执行程序,但是容量大小却不一样,这是为什么呢❓
-
因为以Release版本发布的软件是给客户的,客户是不需要调试信息的
-
往可执行程序里添加很多的调试信息意味着软件的体积会变大
- 一方面,用户下载需要时间了
- 另一方面,用户下载好之后将软件启动、运行都需要更多的时间,体验不好。一般能不加就不加
-
但是对于DeBug来说会自动加调试信息,容量体积比Release大
但是就这么说说太抽象了,我们得看看这个可执行文件里的调试信息究竟是怎样的💻
readelf -S mytest-debug
- 在C生万物 | 程序环境和预处理中,我有说到过对于【可执行文件】它是一个二进制文件,若是查看它的源码就可以发现里面都是一堆乱码
当我们面对一堆二进制乱码措手不及的时候,给大家提到过一个东西叫做readelf
,其实它是Linux中的一个指令,可以用来读取【elf】格式的文件
- 加上
-S
的命令选项以符号表的形式来进行读取这个文件,就以一个列表的形式展现出了这个带调试信息的可执行程序 - 下面展现几个比较常见的。例如这里的
.rodata
就是read only data即只读全局数据区.data
就是已初始化全局数据区.bss
就是未初始化全局数据区
- 不过这些呢是这个可执行文件中的所有内容,若是我们只是先要查看一些debug的调试信息,就要对这些东西进行一个筛选才行
- 此时就可以使用到
grep
命令来进行一个筛选。便可以查看到所有的debug调试信息了
- 上面是查看【DeBug】版本下的调试信息,在【Release】版本有没有呢
- 我们也是读取并搜寻一下这个文件便可以发现对于【Release】版本来说是不存在调试信息的,所以什么都没有被打印出来
【总结一下】
- 程序的发布方式有两种,debug模式和release模式
- Linux gcc/g++出来的二进制程序,默认是release模式
- 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上
-g
选项
好,说到这里,对于调试相关背景就全部讲完了,接下去我们正式进入【gdb】的学习⌨️
三、使用gdb调试代码
1、指令集汇总
因为这个调试器是在Linux环境下的,是纯命令行模式,所以会有很多的指令,做好心里准备😢
注:()括号里面是该指令的全称
l(list) 行号/函数名
—— 显示对应的code,每次10行
r(run)
—— F5【无断点直接运行、有断点从第一个断点处开始运行】
b(breakpoint) + 行号
—— 在那一行打断点
b 源文件:函数名
—— 在该函数的第一行打上断点
b 源文件:行号
—— 在该源文件中的这行加上一个断点吧
info b
—— 查看断点的信息breakpoint already hit 1 time【此断点被命中一次】
d(delete) + 当前要删除断点的编号
—— 删除一个断点【不可以d + 行号】
- 若当前没有跳出过gdb,则断点的编号会持续累加
d + breakpoints
—— 删除所有的断点
disable b(breakpoints)
—— 使所有断点无效【默认缺省】
enable b(breakpoints)
—— 使所有断点有效【默认缺省】
disable b(breakpoint) + 编号
—— 使一个断点无效【禁用断点】
enable b(breakpoint) + 编号
—— 使一个断点有效【开启断点】
- 相当于VS中的空断点
enable breakpount
—— 使一个断点有效【开启断电】
n(next)
—— 逐过程【相当于F10,为了查找是哪个函数出错了】
s(step)
—— 逐语句【相当于F11,】
bt
—— 看到底层函数调用的过程【函数压栈】
set var
—— 修改变量的值
p(print) 变量名
—— 打印变量值
display
—— 跟踪查看一个变量,每次停下来都显示它的值【变量/结构体…】
undisplay + 变量名编号
—— 取消对先前设置的那些变量的跟踪排查问题三剑客🗡
until + 行号
—— 进行指定位置跳转,执行完区间代码finish
—— 在一个函数内部,执行到当前函数返回,然后停下来等待命令c(continue)
—— 从一个断点处,直接运行至下一个断点处【VS下不断按F5】
2、命令演示
看了上面的这些命令后,相信你一定回到了刚开始学习Linux指令的时候那种恐惧感,不过没关系,我会一一地演示这些指令,让你在看完本文后有一个基本的调试能力💪
- 首先我们进入到gdb,然后它会等待我们输入指令
⌨ 行号显示
l(list) 行号/函数名
—— 显示对应的code,每次10行
- 首先若是直接【L】的话便会随机显示出该源文件中的随机10行内容,这不是我们想要的
- 若是【L 0】或者是【L 1】的话那就是从第一行开始往下列10行的内容
- 注意这里的L是小写,而且与数字之间要有一个空格
- 接下去若是想要看到我们所写的全部代码,只需要多
Enter
几次就可以了,gdb会自动记忆你上次敲入的指令
⌨ 断点设置
b + 行号
—— 在那一行打断点
b 源文件:函数名
—— 在该函数的第一行打上断点
b 源文件:行号
—— 在该源文件中的这行加上一个断点
⌨ 查看断点信息
info b
—— 查看断点的信息
- 若是直接执行【info】的话,出来的就是所有的调试信息
- 但若是我们只想查看一下所打的断点的信息,那就在后面加个
b/breakpoint
来简要介绍一下断点的一些字段信息
- Num —— 编号
- Type —— 类型
- Disp —— 状态
- Enb —— 是否可用
- Address —— 地址
- What —— 在此文件的哪个函数的第几行
- 最后的话就是每个断点信息的下面这块
breakpoint already hit 1 time
即此断点被命中1次
⌨ 删除断点
d + 当前要删除断点的编号
—— 删除一个断点【不可以d + 行号】
d + breakpoints
—— 删除所有的断点
- 此时若继续将这个20行的断点打上时,就可以发现其编号为【4】,而并不是从1开始,这是因为我们没有退出过gdb,所以会持续上一次的编号继续往下
⌨ 开启 / 禁用断点
disable b(breakpoints)
—— 使所有断点无效【默认缺省】
enable b(breakpoints)
—— 使所有断点有效【默认缺省】
disable b(breakpoint) + 编号
—— 使一个断点无效【禁用断点】
enable b(breakpoint) + 编号
—— 使一个断点有效【开启断点】
其实对于禁用断点和启用断点,VS中也是有的,它叫做【空断点】。我们一起来看看
⌨ 运行 / 调试
r(run)
—— F5【无断点直接运行、有断点从第一个断点处开始运行】
- 首先若是将断点删除掉,使用【r】指令运行的话就会直接运行到程序结束
- 再加上断点去运行的话就会在打的断点处停下来
⌨ 逐过程和逐语句
n(next)
—— 逐过程【相当于F10,为了查找是哪个函数出错了】
- 可以看到,我从第一个断点处也就是20行的位置开始执行,按下【n】之后因为在其后即22行有一个断点,此时就会直接运行到断点处
s(step)
—— 逐语句【相当于F11,一次走一条代码,可进入函数,同样的库函数也会进入】
- 此时我们按下【s】,也就相当于是【step】,让程序一步一步地走,继而进入了
AddToTop
这个函数,若是你在printf()语句要执行时按下【s】的话gdb就会进入printf()库函数内部去执行,这里就不展示了
- 接下去我们可以就继续【n】,然后进行逐过程调试,来到for循环中,那么逐过程也就是变量i的累加和计数器count的累加,所以会反复执行(通过图中最左侧可以看出是第8行和第10行在反复执行)
- 可以看到后面我没有再按【n】了,但是依旧会执行上面的步骤,这点上面也有提到过,因为gdb会自动化记忆你上一次执行过的命令,所以若是不想再敲了,直接
Enter
就可以了
⌨ 打印 / 追踪变量
p(print) 变量名
—— 打印变量值
- 都执行了那么多次了,不知道【i】和【count】发生了怎样的变化,将它们打印出来看看吧💻
- 通过继续执行【n】,然后再去打印就可以发现
i
的值和count
的值发生了变化
但是你不觉得这样每次去打印会显得很繁琐吗,那一定会的,所以我们有更好的办法💡
display
—— 跟踪查看一个变量,每次停下来都显示它的值【变量/结构体…】
- 我们也可以去追踪一下这两个变量的地址,不过可以看到对于地址来说是不会发生改变的
undisplay + 变量名编号
—— 取消对先前设置的那些变量的跟踪
- 但是呢,每次都追踪打印这么多内容又太多了,我想把它们取消了可以吗?答:当然是可以的
- 既然有
display
,那就有undisplay
⌨ 查看函数调用
bt
—— 看到底层函数调用的过程【函数压栈】
- 通过仔细观察刚才追踪的4个变量最左侧的编号,就可以看到它们的排列的顺序是倒着的。因为变量i和变量count是我们先追踪的,它们的地址是我们后追踪的,所以可以看出这很像是一个压栈的过程
- 其实不仅是对于它们,
AddToTop
函数和main
函数也呈现这样的关系。此时我们就可以通过【bt】这个指令来查看函数压栈的过程,此时便可以看到因为
⌨ 修改变量的值
set var
—— 修改变量的值
- 对于这个修改变量的值,很像是在VS里调试之前设置的那种条件断点,可以使调试开始后直接运行到此断点处。不过对于【set var】而言是在调试过程中进行设置
我们也可以到VS中来看一下条件断点是如何设置的
在右击选择【条件】后,就可以输入你本次执行代码想要从哪个条件开始,然后按下Enter,启动调试之后就会从你设置的条件处开始执行
排查问题三剑客🗡
掌握了上面的这些,你就可以在Linux下调一些简单的代码了,不过想做到高效地进行调试,就需要学习一下【三剑客】
⌨ 指定行号跳转
until + 行号
—— 进行指定位置跳转,执行完区间代码
- 可以看到,当前在for循环内容执行累加的逻辑,但若是我们一直这么执行下去,就没有时间排错了,除了上面的哪一种【set var】之外,还有一种方法其实起到直接结束当前循环的作用,那就是进行指定行号跳转
- 通过观察下图可以看到,当我们运行了
until 13
之后,程序直接就给出了我们最终的结果count,而且即将要执行最后的打印语句,说明我们跳转成功了
- 不过呢,在我使用这个【until】的时候,遇到了一个bug,此时我在循环内部,也就是第10行的位置打了一个断点,用过VS调试器的都知道一直按F5就可以立即执行下一次循环。
- 我在上面是没有打上断点的,所以使用【until】的时候也没有出现问题,但是在下面我在循环内部设置了一个断点,此时再去使用【until 13】进行跳转的时候就出现问题了,并没有马上跳转到指定的行号,而是接着执行循环中的内容
⌨ 强制执行函数
finish
—— 在一个函数内部,执行到当前函数返回,然后停下来等待命令
- 有时候我们会有这样的需求,在初步排查的时候推断可能是某个函数内部的逻辑出了问题,但是呢又不想一步步地进到函数内部进行调试,在VS中其实很简单,只需要在函数下方设个断点,然后F5直接运行到断点处即可
- 但是在Linux下的gdb中,我们可以使用【finsh】指令来直接使一个函数执行完毕。从下图我们可以看到,首先【s】进到函数内部,接下去我直接使用
finish
,可以看到它直接回到了调用函数的位置,returned了一个返回值
- 然后可以看到,在获取到返回值后,也就直接进行了printf打印
⌨ 跳转到下一断点
c(continue)
—— 从一个断点处,直接运行至下一个断点处【VS下不断按F5】
- 这点也是我刚才在上面有提到过的,在VS中,我们要直接跳转到下一个断点处只修要按下F5即可,那在gdb中该如何操作呢,你需要敲个【c】就可以了
- 从下图我们可以看出,对于这个指令的用处可谓是非常大,当我处于第一个断点也就是20行的时候,直接敲下【c】,就可以运行到第二个断点处也就是第10行。之后若反复敲【c】,因为这是一个单语句的循环,所以循环的下一次还是会执行到此处。上面的这两个功能就和我们在VS中用的F5是一个道理
四、实战演练:Swap Two Numbers
📖纸上得来终觉浅,绝知此事要躬行🔨
————————————
以下是本次调试所要使用到的代码,相信你一定非常熟悉了,也就是使用指针交换两数,如果想看细节讲解可以看看我的函数章节
1 #include <stdio.h>2 3 void swap(int* x, int* y)4 {5 int t = *x;6 *x = *y;7 *y = t; 8 }9 int main(void)10 {11 int a = 10;12 int b = 20;13 14 printf("a = %d, b = %d\n", a, b);15 16 swap(&a, &b);17 18 printf("a = %d, b = %d\n", a, b);19 return 0;20 }
- 首先我们在程序第16行设置上一个断点,然后【r】从第15行开始运行
- 然后我们使用【s】进入到swap函数中,因为我首先不想调试,想先立马看看运行结果,但是此时又已经进入调试了,那么我们就可以使用到【finish】来立马执行完这个函数,然后观察一下结果
- 可以看到,最后打印出结果的时候a和b的值确实发生了交换
- 既然清楚了二者会进行一个交换,接下去我们就逐语句【n】进行一个单步追踪吧
- 因为提前看了执行结果,所以我们要重新开始调试,按下【r】即可,它会询问你是否需要重新开始调试,选择y之后就可以重新从16行开始进行调试
- 首先通过【display】记录一下两个变量的值和地址
- 接着按【s】进入到swap函数里,追踪一下指针x和指针y的内容,也就是它们所存放的地址,就可以看到,函数内部已经接受到了这两个变量的地址
- 但是我们后面不想再查看它们了,只需要看值是否被修改,此时就可以取消对它们的跟踪【undisplay】
- 然后对我们要观察的值变化继续做一个追踪
- 并且在执行完第一个语句
t = *x
时,临时变量t中已经存放了变量a的值,也就是指针所指向的那块空间中的值
- 接下去执行
*x = *y
,此时*x中的值就发生了变化,因为指针x可以直接找到变量a的地址,所以可以对其中的内容做修改,就变为了20
- 接下去执行
*y = t
,同理,指针y可以直接找到变量b的地址,所以可以对其中的内容做修改,将原本保存在临时变量t中的10赋值给到*y,也就修改了其中的内容
- 再按【n】的话这个swap函数就执行结束了,回到了main函数,就可以清楚地看到函数内部的修改带动了函数外部值的变化,真正地通过【传址调用】交换了两个数
敲黑板:重点来了
- 但是我不仅想让你看到这些,更加重要的一点不知你有没有发现,刚才我们在main函数中追踪的变量a和变量b的值与地址,到了swap()中就不见了,这是为什么呢?
- 其实就是因为两个函数的函数栈帧不同,变量a和变量b位于main函数的函数栈帧中;而指针变量x和指针变量y位于swap函数的函数栈帧中,是互不相干的,临时变量除了函数栈帧就会被销毁【如果听不懂这些请看反汇编深挖【函数栈帧】的创建和销毁】
- 但是因为在swap()内部接受到了外部两个变量的地址,所以在函数内部就可以通过这两个指针找到这两个变量的地址,从而对这两个空间中的内容做一个修改的操作
通过以上的实验演练,你是否对调试器GDB的使用更上一层楼🐧
五、总结与提炼
最后来总结一下本文所学习的内容📖
- 在本文的开始,我们在初见GDB的时候发现了【
no debugging symbols
】的报错信息,于是便谈到了一个可执行程序的【DeBug】版本和【Release】版本,知道了对于前者来说是我们程序员使用的环境,是存在调试信息的;对于后者来说是测试人员所处的环境,是不存在调试信息的。而对于gcc/g++而言默认生成的可执行程序就是【Release】版本的,因此我们要加上一个-g
命令选项使其在make之后生成一个【DeBug】版本的可执行程序,这样就可以进行调试了 - 接着我们正式进入到了调试器GDB的使用,介绍了很多相关的指令,这些都是我整理出来的常见指令,其实对于GDB来说还有很多指令,但是真正常用的也就这些,只要你认真地看下来,将它们都使用熟练了,相信你的调试功底会大幅度提升,对你在VS中的调试也是有所帮助的
- 最后,在学习了GDB的许多调试指令后,我们便进行了【Swap Two Numbers】的实战演练,不仅巩固了C语言中有关传址调用以及函数栈帧的相关指知识,而且使我们对于调试器GDB的使用更上一层楼↑
以上就是本文要介绍的所有内容,感谢您的阅读❤️
相关文章:
Linux | 调试器GDB的详细教程【纯命令行调试】
文章目录一、前言二、调试版本与发布版本1、见见gdb2、程序员与测试人员3、为什么Release不能调试但DeBug可以调试❓三、使用gdb调试代码1、指令集汇总2、命令演示⌨ 行号显示⌨ 断点设置⌨ 查看断点信息⌨ 删除断点⌨ 开启 / 禁用断点⌨ 运行 / 调试⌨ 逐过程和逐语句⌨ 打印 …...
wifi芯片大市场和个人小生活
3.3 是日也,天朗气清,惠风和畅。仰观宇宙之大,俯察论文论坛,所以游目骋怀,足以极视听之娱,信可乐也。 夫人之相与,俯仰一世,或取诸怀抱,悟言一室之内;或因寄所…...
全国计算机技术与软件专业技术资格(水平)考试 上半年2023年3月13日开始,下半年2023年8月14日开始
根据2023年计算机技术与软件专业技术资格(水平)考试工作计划,可以得知,2023年软考报名时间——上半年2023年3月13日开始,下半年2023年8月14日开始。 点击查看:人力资源社会保障部办公厅关于2023年度专业技术人员职业资格考试工作计划及有关事项的通知 点击查看:2023年度…...
大数据框架之Hadoop:MapReduce(六)Hadoop企业优化
一、MapReduce 跑的慢的原因 MapReduce程序效率的瓶颈在于两点: 1、计算机性能 CPU、内存、磁盘、网络 2、IO操作优化 数据倾斜Map和Reduce数设置不合理Map运行时间太长,导致Reduce等待过久小文件过多大量的不可分块的超大文件Spill次数过多Merge次…...
Spring File Storage的详细文档
快速入门配置pom.xml引入依赖<dependencies><!-- spring-file-storage 必须要引入 --><dependency><groupId>cn.xuyanwu</groupId><artifactId>spring-file-storage</artifactId><version>0.7.0</version></dependen…...
Java软件开发好学吗?学完好找工作吗?
互联网高速发展的当下,Java语言无处不在:手机APP、Java游戏、电脑应用,都有它的身影。作为最热门的开发语言之一,Java在编程圈的地位不可撼动。可是,听名字就很专业的样子。Java语言到底好学吗?刚入坑编程圈…...
【独家C】华为OD机试提供C语言题解 - 优秀学员统计
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 最近更新的博客使用说明优秀…...
数据仓库、数据中台、数据湖都是什么?
相信很多人都在最近的招聘市场上看到过招聘要求里提到了数据仓库、数据中台,甚至还有数据湖,这些层出不穷的概念让人困扰。今天我就来跟大家讲一讲数据仓库、数据中台以及数据湖的概念及区别。 数据库 在了解数据仓库、数据中台以及数据湖之前ÿ…...
0099 MySQL02
1.简单查询 查询一个字段 select 字段名 from 表名; 查询多个字段,使用“,”隔开 select 字段名,字段名 from 表名; 查询所有字段 1.把每个字段都写上 select 字段名,字段名,字段名.. from 表名; 2.使用*(效率低,可读性差) select *…...
应急响应-ubuntu系统cpu飙高
这里写目录标题一、排查过程二、处置过程三、溯源总结一、排查过程 1、查看CPU使用情况 top -c2、查看异常进程的具体参数 ps -aux3、通过微步查询域名信息 4、查看异常进程的监听端口 netstat -anlpt5、查找服务器内的异常文件 ls cat run.sh cat mservice.sh6、查看脚本…...
MDK软件使用技巧
本文主要汇总MDK软件使用技巧 一、字体大小及颜色修改 第一步点击工具栏的这个小扳手图标 进去后显示如下,先设置 Encoding 为:Chinese GB2312(Simplified),然后设置 Tab size 为:4 以更好的支持简体中文,否则&…...
3 333333
全部 答对 答错 单选题 1. 一个项目来取代目前公司的文件存储系统已经获批。外部供应商提供硬件,内部团队开发软件。这个团队是自组织的,由一般的专家组成。团队建议迭代地与供应商合作,但供应商表示拒绝。因此,只有软件将被迭代…...
1528. 重新排列字符串
1528. 重新排列字符串https://leetcode.cn/problems/shuffle-string/ 难度简单52收藏分享切换为英文接收动态反馈 给你一个字符串 s 和一个 长度相同 的整数数组 indices 。 请你重新排列字符串 s ,其中第 i 个字符需要移动到 indices[i] 指示的位置。 返回重新…...
【8】【用户操作日志】操作日志SpringBootStarter
操作日志 此版本操作日志主要就是通过AOP拦截器实现的,整体主要分为AOP拦截器、自定义函数、日志上下文、扩展接口;组件提供了6个扩展点,自定义函数、日志上下文、用户信息获取,日志保存,自定义异常获取,入…...
【游戏逆向】寻路函数隐藏检测点分析
案例: 某游戏出现调用寻路函数失败异常崩溃。 基本情况分析: 在刚登陆游戏的时候直接调用寻路函数崩溃。 手动寻路以后再调用寻路不崩溃。(排除了函数编写错误的可能) 猜测可能检测方法: 有某一个标志位(全局类型)在游戏刚登陆的时候没…...
【Zabbix实战之运维篇】Zabbix监控Docker容器配置方法
【Zabbix实战之运维篇】Zabbix监控Docker容器配置方法 一、检查Zabbix监控平台状态1.检查Zabbix各组件容器状态2.奸诈Zabbix-server状态二、下载监控模板1.进入Zabbix官网下载页面2.查看下载模板三、创建一个测试容器1.创建一个测试容器2.检查测试容器状态3.访问测试web服务四、…...
这款 Python 工具进行数据分析及数据可视化真的很棒啊
前言 大家好,今天我们以全国各地区衣食住行消费数据为例,来分析2022年中国统计年鉴数据,统计全国各地人民的消费地图,看看: 哪个省份的人最能花钱 哪个省份的人最舍得花钱 哪个省份的人最抠门 全国各地区人民在吃、穿…...
visual Studio Code常用快捷键
1、向上/向下移动代码行 alt 下箭头/上箭头 2、向上/向下复制一行代码 shiftalt 下箭头/上箭头 3、选定多个相同的单词 ctrl d 4、全局替换 ctrl h 5、快速定位到某一行 ctrl g 6、放大缩小整个编辑器界面 ctrl / - 7、添加多个光标 Ctrl Alt 上箭头/下箭头…...
基础(一)十六进制转八进制
试题 基础练习 十六进制转八进制 资源限制 内存限制:512.0MB C/C时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s 问题描述 给定n个十六进制正整数,输出它们对应的八进制数。输入格式 输入的第…...
梯度提升算法决策过程的逐步可视化
梯度提升算法是最常用的集成机器学习技术之一,该模型使用弱决策树序列来构建强学习器。这也是XGBoost和LightGBM模型的理论基础,所以在这篇文章中,我们将从头开始构建一个梯度增强模型并将其可视化。 梯度提升算法介绍 梯度提升算法&#x…...
Linux系统调用之文件属性操作函数
前言 如果,想要深入的学习Linux系统调用中access,chmod,chown,truncate这些有关于文件属性的操作函数,还是需要去自己阅读Linux系统中的帮助文档。 具体输入命令: man 2 access/chmod/chown/truncate 即可…...
VMware 安装 银河麒麟高级服务器操作系统 V10 + QT 开发环境搭建
下载并安装vmware 下载银河麒麟操作烯烃服务器版v10的镜像文件从官网下载,因为是x86的电脑芯片,选择AMD64版,即vmare 安装麒麟操作系统注意事项:安装位置选择自动分区网络和主机名设置打开网络,ip4就不用再设置了创建一…...
2023年疫情开放,国内程序员薪资涨了还是跌了?大数据告诉你答案
自从疫情开放,国内各个行业都开始有复苏的迹象,尤其是旅游行业更是空前暴涨,那么互联网行业如何? 有人说今年好找工作多了,有人说依然是内卷得一塌糊涂,那么今年开春以来,各个岗位的程序员工资…...
太赫兹频段耦合器设计相关经验总结
1拿到耦合器的频段后,确定中心频率和波导的宽度和高度 此处贴一张不同频段对应的波导尺寸图 需要注意的是1英寸 2.54厘米,需注意换算 具体网址:矩形波导尺寸 | 扩维 (qualwave.com) 仅列举我比较常用的太赫兹频段部分 2.以220~320GHz频段&a…...
反弹shell数据不回显带外查询pikaqiu靶场搭建
P1 文件上传下载(解决无图形化和解决数据传输) 解决无图形化: 当我们想下载一个文件时,通常是通过浏览器的一个链接直接访问网站点击下载的,但是在实际的安全测试中,我们获取的权限只是一个执行命令的窗口…...
按键修改阈值功能、报警功能、空气质量功能实现
按键修改阈值功能 要使用按键,首先要定义按键。通过查阅资料,可知按键的引脚如图所示:按键1(S1)通过KEY0与PA0连接,按键2(S2)通过KEY1与PE2连接,按键3(S3&…...
spring重点整理篇--springMVC(嘿嘿,开心哟)
Spring MVC是的基于JavaWeb的MVC框架,是Spring框架中的一个组成部分(WEB模块) MVC设计模式: Controller(控制器) Model(模型) View(视图) 重点来了😄 SpringMVC的工作机制…...
图像融合评估指标Python版
图像融合评估指标Python版 这篇博客利用Python把大部分图像融合指标基于图像融合评估指标复现了,从而方便大家更好的使用Python进行指标计算,以及一些I/O 操作。除了几个特征互信息的指标没有成功复现之外,其他指标均可以通过这篇博客提到的P…...
20230303----重返学习-函数概念-函数组成-函数调用-形参及匿名函数及自调用函数
day-019-nineteen-20230303-函数概念-函数组成-函数调用-形参及匿名函数及自调用函数 变量 变量声明 变量 声明定义(赋值) var num;num 100; 声明与赋值分开var num 100; 声明时就赋值 赋值只能声明一次,可以赋值无数次 变量声明关键词 varconstletclassfunctio…...
Java面试题总结
文章目录前言1、JDK1.8 的新特性有哪些?2、JDK 和 JRE 有什么区别?3、String,StringBuilder,StringBuffer 三者的区别?4、为什么 String 拼接的效率低?5、ArrayList 和 LinkedList 有哪些区别?6…...
莱州环球网站建设/seo博客网站
由于安全限制,生产环境中的服务器(以下称为Intranet服务器)可能无法连接到外部网络. 在这种情况下,您将无法使用pip命令直接安装python软件包. 这是两种解决方案: Intranet服务器通常通过跳板计算机连接. 如果跳板计算机可以正常访…...
网站建设自学需要多久/苏州seo关键词排名
存储数据在Android开发中是一项非常重要的功能,下面介绍的这种技术是利用XML文件存储键值对。 SharedPreferences的使用主要有四步: 1)获得SharedPreferences对象 2)获得SharedPrefercences.Editor对象 3)使用putXXX方…...
企业网站优化的弊端/合肥seo网站排名
因为总是记不住,还要百度或者试验一下,不如干脆记下来 tran属性:position、rotation、scale transform.SetParent(Transform parent, bool worldPositionStays) worldPositionStays:是否保持原本的tran属性 worldPositionStay…...
网站编程语言排行榜/推广产品的方法和步骤
假期通常是一年中的某个时候,我们拿贺卡来使我们心爱的人开心。礼物通常附有圣诞节或新年贺卡,人们通过这些卡片表达他们的诚挚愿望,并希望使礼物更加个性化和特别。但是,贺卡可以全年发送:用于生日,婚礼&a…...
wordpress菜单对齐修改/线上推广方式有哪些
mysql---随机获取10%的数据最近在研究从某张表中随机获取百分之十的数据,查询各大网站,基本上都使用的是rand()函数,完全看不懂,感觉也不太符合我的需求,所以决定自己研究一下,怎么实现此需求。 查询原始数…...
怎样做网络推广信任豪升网络好/宁波外贸网站推广优化
服务器端Bonjour服务发布成功之后,客户端可以通过NSNetService解析服务,解析成功后,可以获得通讯的数据细节,如:IP地址、端口等信息。 首先需要实例化NSNetService对象代码如下: -(id)init {_service [[NS…...