• Linux:今天学vim编辑器,gdb调试器,makefile项目自动化构建工具


    vim编辑器的三种模式

    用vim编辑器打开一个文本,默认是命令模式,想对文本输入内容

    1.敲一个i进入插入模式
    2.想回到命令模式,Esc返回
    3.从命令模式进入底行模式,输入一个冒号,回到命令模式同理
    底行模式和插入模式不能直接转换
    在这里插入图片描述

    vim命令模式的一些常用操作

    yy,复制当前文本行,nyy从光标处开始向下复制n行文本
    p,粘贴到光标的下一行,np重复粘贴n次
    dd:剪切,ndd从光标处开始向下剪切n行文本
    shift + 4,将光标移动到当前文本行的末尾
    shift + 6,将光标移动到当前文本行的开始
    shift + g,移动到文本的末尾
    gg,移动到文本的开头
    u,撤销操作
    ctrl+ r,撤销上一次u的操作
    n + shift + g移动到文本的第n行
    r,替换当前字符
    shift + r,类似insert,批量化替换
    shift + ~,大小写转换
    w,b,当前行的后移,前移,以单词为单位的移动
    h,j,k,l,左下上右

    编译过程的感性认知

    一段文本要转换成机器能看懂的语言要经过翻译和链接两步,其中翻译又细分为三个部分:预处理,编译,汇编,这些过程都做了什么?

    1.预处理:宏替换,头文件展开,去注释,条件编译(将代码变得干净)
    2.编译:将干净的代码转换成汇编代码
    3.汇编:将汇编代码转换成机器能看懂的可重定位的二进制代码
    4.链接:链接代码中调用的库

    用代码解释编译过程

    在这里插入图片描述
    用gcc直接编译这个test.c文件会直接生成一个可执行程序
    在这里插入图片描述
    -o指定生成文件的名字
    在这里插入图片描述
    -E选项,只执行编译的第一步:预处理,预处理后的文件通常用i作为后缀。
    在这里插入图片描述在这里插入图片描述
    在底行输入vs test.c,可以同时看两个文件,使用ctrl+w+w,光标在两个分屏窗口切换

    可以看到头文件被展开,宏替换,去注释,条件编译后生成的预处理文件
    在这里插入图片描述
    -S选项,对i文件进行编译生成汇编代码,文件通常以s作为后缀。
    在这里插入图片描述
    s文件中都是汇编代码
    在这里插入图片描述
    -c选项,对s文件进行汇编,生成机器能看懂的二进制文件在这里插入图片描述
    od -x xxx,xxx是文件名,这是查看二进制文件的指令。编译后的一部分代码就如上图所示。在这里插入图片描述
    在这里插入图片描述
    此时的test.o还不能执行(连执行属性都没有),强行加上后又说不能执行该二进制文件。原因是生成可执行程序还差最后一步:链接,代码中包含了头文件。头文件只是声明了有这个方法可以使用,但这个方法的具体实现在库中(可以将库理解为特殊处理过的源文件),链接就是将库与代码产生联系(链接起来)在这里插入图片描述

    使用gcc直接对文件进行操作,gcc指令中默认会将文件与库进行链接,此时的out文件就是可执行文件在这里插入图片描述

    动态库与静态库概念

    动态库后缀 Linux(.so) windows(.dll)
    静态库后缀 Linux(.a) windows(.lib)

    由动态库与静态库引出的两个概念:动态链接和静态链接,动态库作为单独文件独立于你的代码文件存在,如果你的代码需要调用动态库,程序会找到动态库的存储位置然后调用里面的函数,一般情况下动态链接较为常见。

    而静态链接是将静态库拷贝到你的代码文件中,如果调用的静态库较多,拷贝的代价会很大,你的文件大小也随之增加
    在这里插入图片描述
    (安装c语言静态库:sudo yum install -y glibc-static,安装c++静态库:sudo yum install -y libstdc++ -static)

    静态链接产生的文件大小大概是动态的100倍

    gdb的使用

    用gcc默认编译的文件版本是release的,release版本不能进行调试,在gcc命令后加上-g选项,编译的版本就是debug,能进行调试的版本。
    在这里插入图片描述
    test_g.out比test.out多出了一千多字节,这些多出的信息就是文件的调试信息。

    调试的相关指令

    进入gdb,gdb 文件名(test_g.out),文件必须是-g选项生成的可执行文件
    在这里插入图片描述

    • b(break) 行号,打断点
    • info b,查看所有断点信息
    • d 断点编号,删除断点
    • l 行号,显示该行的代码
    • s(step),逐语句,(可以进入函数)
    • n(next),逐过程,(跳过函数)
    • display && undisplay,常显示一个变量的值和取消常显示
    • until 行号,跳转到指定行
    • r,运行程序
    • c(continue),从一个断点运行到另一个断点
    • finish,执行完一个函数就停止
    • quit,退出

    (l 0很好用,连续敲回车可以依次查看代码)

    makefile/make

    (make是一个命令,makefile是一个文件)

    vs作为一个集成开发环境,可以不用对每个源文件进行编译,vs会自动编译工程中所有的源文件(ctrl + B自动编译),但linux中的工程,如果源文件的数量较多,在该工程中应该先编译哪个源文件?整个工程的结构应该是怎样的?使用Linux的编译器就会面临这样的问题,Linux提供了一个工具makefile,该工具能解决整个工程的编译问题。

    makefile的目的是编译文件,要达到这个目的需要告知源文件和可执行文件之间的依赖关系,以及源文件生成可执行文件的依赖方法。

    所以makefile中要写依赖关系和依赖方法,比如test.out依赖test.c生成,要生成这个可执行文件的依赖方法是gcc test.c -o test.out,

    在这里插入图片描述
    第一行的依赖关系,test.out依赖test.c生成,

    • 格式为目标文件:依赖文件

    第一行就是test.out:test.c,第二行要先敲一个Tab,接着是编译该可执行文件的指令,比如gcc test.c -o test.out。
    在这里插入图片描述
    PHONY是一个关键字,.PHONY:后跟一个伪目标,也就是说使用make命令后,系统不会生成这个目标文件,因为它是伪目标,声明完伪目标后,由于clean **“文件”**没有依赖文件,所以clean的冒号后为空,定义完目标文件clean后,回车Tab写上删除文件的指令。(rm -f删除文件,如果文件不存在不输出提示信息)

    要删除生成的可执行文件就敲make clean命令
    在这里插入图片描述
    被.PHONY修饰的伪目标,表示该依赖关系总是被执行,何为总是被执行?无论目标文件是否新旧,直接执行依赖关系
    在这里插入图片描述
    clean是.PHONY修饰的伪目标,用make clean执行依赖关系:rm -f test.out时,由于系统不会伪目标,所以系统执行make clean时,检测到makefile的同级目录下没有该目标文件,所以总是指向伪目标的依赖方法。而make生成的目标文件test.out没有被.PHONY修饰,make生成了目标文件后,再次make,系统检测到test.out是新生成的文件,并且其依赖文件的没有更新,所以不会执行gcc test.c -o test.out这个依赖关系。
    在这里插入图片描述
    还有一点,make只会生成第一个目标文件,执行该依赖关系的依赖方法。如果clean写在test可执行文件前面,make就只会clean在这里插入图片描述
    最后,可以用$@, $^简化书写

    • $@表示目标文件, $^表示依赖文件
    • 如果依赖文件不止一个,那么$^表示所有的依赖文件
    test.out:test.c
    	gcc $@ -o $^
    
    • 1
    • 2

    可以用这两个符号写makefile文件

    目标文件的新旧,系统是怎样分别的?

    通过时间来区别文件的新旧,Linux中的文件由内容和属性组成,使用stat命令显示文件的状态信息,可以看到文件有三个时间

    • Access:文件的访问时间,如果进入该文件达到一定的次数这个时间会更新
    • Modify:文件的内容被修改,时间更新
    • Change:文件的属性被修改,时间更新

    但修改了文件的内容,Modify时间一定改变,而修改内容可能改变文件的属性,所以Change时间也可能改变
    在这里插入图片描述比如test.c生成目标文件test.out,那么test.c的修改时间一定先于test.out,但修改了test.c,test.c的修改时间就会改变。使用make命令时,如果test.c的修改时间后于目标文件test.out,那么make会执行依赖关系gcc test.c -o test.out,如果依赖的修改时间先于目标文件,那么make不会指向依赖关系

    makefile的多文件使用

    在这里插入图片描述
    在这里插入图片描述
    解析一下makefile的逻辑:现在有两个源文件hello.c和main.c,将这两个源文件编译成hello.o和main.o目标文件后,将这两个目标文件作为新的目标文件hello的依赖文件进行编译

    clean:清理目标文件,将所有以.o结尾的文件清理,以及hello这个可执行文件

    缓冲区理解

    在这里插入图片描述
    这段代码是先输出hello world再休眠还是先休眠在输出hello world?由于缓冲区的存在,程序的结果是先睡眠再输出。因为缓存区是一段内存,缓冲区采用的刷新策略是行刷新,当缓冲区没有满或者一行没有结束时,缓冲区的内容不会向显示器输出。如果在hello world后加上\n,就会先打印hello world,再睡眠。

    进度条的打印

    在这里插入图片描述
    进度条运行的效果
    在这里插入图片描述
    %-100s,打印字符串,预留100个字符的空间,-的意思是让打印内容从左向右填充。

    后面是一个旋转的特效,依次定义plan中的每个字符,为了保证不越界,cnt要%4。

    plan中要用\表示\,因为\是一个转义字符。

    但这段代码最重要的一部分是fflush(stdout),将缓存区的内存向标准输出流中输出,也就是输出到屏幕,每次进度条变化都要刷新缓冲区,否则这个效果就会一卡一卡的(但同样的代码在vs下,不加fflush却不会造成卡顿,这说明vs和gcc的缓冲区刷新机制不同,vs是直接输出,gcc则是达到缓冲区满了再输出)。

    git使用

    用git clone xxx将远程仓库克隆到本地,xxx是仓库的网址。用git上传代码的三板斧:git add xxx,xxx是文件名,add之后git commit -m “xxx”,xxx是这次提交的日志,,最后是git push,将提交的代码上传到远程仓库里。

    当仓库的代码需要更新时,使用git pull拉取最新的代码。
    在这里插入图片描述
    git status,查看这次提交的文件
    在这里插入图片描述
    .gitignodre文件里存储的是git提交可以忽略的文件后缀,即带有该后缀的文件不会被上传,是一个黑名单文件。

  • 相关阅读:
    智能垃圾分类策略
    c++中的宏#define用途
    正则表达式高级用法
    [Linux入门]---gdb调试
    LeetCode 2558. 从数量最多的堆取走礼物
    【牛客】SQL127 月总刷题数和日均刷题数
    C++初阶(命名空间+缺省参数+const总结+引用总结+内联函数+auto关键字)
    DGIOT国内首家轻量级物联网开源平台——真实ModbusRTU接入实战教程
    NEFU离散数学实验1-排列组合
    拉电流、灌电流和漏电流
  • 原文地址:https://blog.csdn.net/weixin_61432764/article/details/126588567