龙空技术网

Linux系列_4:gdb调试器和Make/Makefile

deeplearning爱好者 1168

前言:

现在你们对“fflushstdout怎么用”可能比较注意,咱们都需要分析一些“fflushstdout怎么用”的相关文章。那么小编在网上收集了一些对于“fflushstdout怎么用””的相关资讯,希望大家能喜欢,姐妹们快快来了解一下吧!

前言

如需获取工程文件,xshell,虚拟机和镜像等,请关注公众号【0与1】,并在后台回复【Linux】

本文接前文:Linux系列_3:编辑器vim与编辑器gcc/g++的使用 。编写代码出现问题时,需要快速找出问题的话就需要用到调试了。Linux下调试时使用的gdb调试器,不像在IDE当中,在Linux调试时使用的依旧是终端,所以这也是使用Linux调试的弊端,不过不用担心,VScode可以帮助解决这个问题,让你在图形界面下轻松调试,详细见VS Code+Vim打造C/C++极致开发环境 。实际开发过程中,我们的工程文件不可能只有零星几个,而在前面的讲述中,可以得知一个程序的编译有很多操作,其中最后一个操作是链接,那么这么多文件应该如何链接,或者说有没有顺序要求,这一切的一切都需要make/makefile(项目自动化构建)的帮助。接着本文还用一个小程序——进度条来结束这两篇关于Linux开发入门的文章,最后本文还提及了如何在Linux下提交自己的代码到GitHub下。

一:Linux调试器-gdb(1)debug与release

程序的发布方式有debugrelease两种模式,release没有调试信息,不能进行调试,体积较小,debug携带调试信息,可以进行调试,但是文件较大

Linux中,使用gcc/g++编译的程序,默认使用的是release模式,所以就不能直接使用gdb进行调试。如果想要调试,必须在进行gcc/g++编译时,携带-g选项

如下,同一个文件分别使用debug(带-d的)和release,其中debug的文件大小明显大于release

使用readelf -S命令可以查看debug文件

(2)一起来调试一个程序

如下是一个计算1-100的和的C语言程序,进行编译(不要忘记-g

1:开启调试

输入gdb [文件名],进入调试状态

2:显示源代码

输入list(简写为l),显示源代码便于观察,每次显示10行,按Enter向下翻输入list n(简写为l n),显示第n-5行第n+5行

3:断点

输入break n(简写为b n),在第n行打上一个断点

输入info b,可以查看断点信息输入d [断点编号],删除对应编号的断点输入disable breakpoint [断点编号],禁用对应编号的断点;输入disable breakpoints,禁用所有断点输入enable breakpoint [断点编号],启用对应编号的断点;输入enable breakpoints,启用所有断点

4:运行程序

①:开启运行

输入run(简写为 r),程序开始运行,如果没有断点将直接输入程序结果,如果有断点,程序将停止到第一个断点处。类似于在VS中,在某行上按下了F5打上了断点,然后再按F5,程序将跳转到此断点处,但还没有执行

②:逐语句,逐过程执行

如上,此时来到一个断点处,类似于VS,你会有两种选择:要么逐语句F10,不进入此函数,直接执行此函数跳到下一行语句,要么逐过程F11进入此函数执行

输入next(简写为n),程序会到下一句,若输入step(简写为s),程序会进入这个函数

③:查看变量

上述程序在运行过程中,若需要查看变量的值,则输入p [变量名]

若需要动态查看变量名,则输入display [变量名],接着再执行程序,程序在执行过程中除显示当前执行的语句外,还会显示执行过程中的变量在动态查看中若不想查看某个变量了,取消的方式和设置的方式稍微有点区别。可以发现设置display后,gdb为每个要展示的变量都设置了相应的编号,所以取消时输入undisplay [变量名所对应的编号];如果要全部取消则直接输入display

④:执行中跳转

上述,进入sum函数,就在不断执行循环,如果临时决定想要跳出这个循环,或者直接到某一行,那么就输入until n,其中n表示行号如果想要从一个断点,跳转到下一个断点,则输入c如果进入了某个函数,想要立即结束掉这个函数直接返回到调用处,则输入finish

(3)总结-gdb选项

选项

描述

list [行号]

显示源代码

list [函数名]

显示某函数的源代码

run

运行程序

next

逐语句执行

step

进入函数

break [行号]

在某行出设置断点

break [函数名]

在某函数开头设置断点

info break

查看断点信息

finish

结束当前函数,返回调用处

p []变量]

打印变量的值

c

跳转到下一个断点

d [断点编号]

删除对应编号的断点

delete breakpoints

删除所有断点

disable/enable breakpoint [断点编号]

禁用/启用对应编号断点

display [变量名]

跟踪变量,执行时每次都显示

undisplay

取消跟踪

until [行号]

跳转到对应行

quit

退出

二:Linux项目自动化构建工具-Make/Makefile(1)前言

对于一个大型项目,可能会涉及到很多文件,例如头文件,源文件等等。在VS中,我们只需在其设定的目录下,新建对应文件然后进行编写即可,然后按下运行键,只要你的代码没有问题,那么就可以运行出结果。这一切一切的感觉看起来很轻松,但是实则不然,例如首先编译哪个文件,如何链接?这些都是需要考虑的,但是VS作为一个集成开发环境,自然而然帮你做好了这一切,但是在Linux中,这些都是需要自己做的。而正因为做这些很麻烦,且不好理解,所以make,makefile应用而生

make/makefilie:用于在Linux中维护项目文件之间的关系,其中make是一个目录,makefile是一个文件,通常该文件存放于当前工作目录

(2)依赖关系和依赖方法

通俗的来讲依赖关系是指:要生成A就必须先生成B,而依赖方法是指:怎样通过B生成A。

比如前述在gcc编译过程时,这几个文件就存在以下依赖关系和依赖方法(假设由hello.c编译生成hello.exe

hello.exe依赖hello.o,依赖方法是gcc hello.o -o hello.exehello.o依赖hello.s,依赖方法是gcc hello.s -o hello.ohello.s依赖hello.i,依赖方法是gcc hello.i -o hello.shello.i依赖hello.c,依赖方法是gcc hello.c -o hello.i(3)单文件Makefile

简单其工作过程就是,在同一个目录下,创建一个文件叫做Makefile,然后在该文件内,进行编写,编写的代码反映了上述的依赖关系和依赖方法,然后返回终端通过输入目命令,来执行这个过程。很像Windows中的批处理。

准确来说:make是执行依赖关系和依赖方法的命令,Makefile是维护该机制的文件,Makefile里面保存了项目的依赖结构

如下,创建一个hello.c的文件,并编写简单代码,然后再在相同目录下创建文件Makefile

接着在Makefile编写如下代码,其中各项作用解释于图中。

Makefile编写好之后,在命令行直接使用make,就可以调用这个文件执行操作。下面的两个操作相当于VS中的生成解决方案和清理解决方案。

(4)多文件Makefile

编写一个简单加法程序,两个源文件:main .ccompute .c,一个头文件compute. h,使用Makefile进行编译

首先,编写程序如下编写Makefile如下执行

(5)总结

综上所述,当我们编写好Makefile,然后在终端输入makemake就会在当前目录下寻找名字叫做Makefile的文件,如果找到,它会以该文件中的第一个文件(比如上面的exe文件)作为最终需要生成的文件,如果该文件不存在或者说它依赖的.o文件要比这个.exe文件新,那么它就会执行依赖方法,来生成这个.exe文件,同样的,如果.o文件不存在,那么make就会找这个文件的依赖性,再根据上面的规则走下去。但最终,.c文件和.h文件一定会存在的,于是一层套一层,最终直到生成目标文件。

终极makefile三:小程序:进度条(1)两个问题A:缓冲区问题

有如下代码

#include <stdio.h>#include <unistd.h>int main(){	printf("Hello linux\n");	sleep(3)	return 0;}12345678

执行效果如下,可以发现当输出带有\n时,能按照正常的逻辑输出,即先打印再等待

若将代码改为下面这样,去掉\n

#include <stdio.h>#include <unistd.h>int main(){	printf("Hello linux");	sleep(3)	printf("\n");	return 0;}123456789

可以发现似乎先执行了sleep,再执行printf

但是任何语言都是自上向下执行的,不会出现胡乱执行的问题,出现这个问题的原因在于缓冲区。

printf在执行时,并不是一下就把语句输出到屏幕上去,而是先输出到缓冲区里,然后等到合适的刷新出现后,就将内容发送到屏幕上去,第一次之所以正常输出,是因为有了\n,这个刷新标志,而第二次之所以没有正常输出,是因为printf没有遇到刷新标志,然后就接着执行sleep,最后遇到return才刷新,自然而然感觉是先刷新后打印了

为了解决这一个问题,可以在printf结束之后,让其强制刷新,即输入fflush(stdout),即强制刷新屏幕

#include <stdio.h>#include <unistd.h>int main(){	printf("Hello linux");	fflush(stdout);	sleep(3)	printf("\n");	return 0;}12345678910
(B)回车与换行

转义字符\r表示回车,回车的意思是回到本行的第一个字符处

转义字符\n表示换行,换行的意思是到下一行对应位置再输入

(2)进度条

效果

代码

#include "bar.h"  4 void bar()  5 {  6   char bar[NUM];  7   memset(bar,'\0',sizeof(bar));  8   9   const char* label="|/-\\"; 10   int i=0; 11   while(i<100) 12   { 13     fflush(stdout); 14     bar[i++]='#'; 15     usleep(150000); 16     printf("[%-100s][%3d%%][%c]\r",bar,i,label[i%4]);                                                                              17   } 18   printf("\n"); 19 }123456789101112131415161718
四:Git

如何在Linux上使用GitHub托管代码

按照正常步骤,在GitHub上创建一个代码仓库,复制https

使用git clone,在本地创建相应的一个仓库

需要的代码,拷贝到这个仓库里三板斧之第一板斧–git add。输入git add [文件名]三板斧之第二板斧-git commit。输入git commit三板斧之第三板斧-git push。输入git push,然后输入用户名和密码即可

标签: #fflushstdout怎么用