前言:

大家周末愉快呀~今天来整理一下linux中用到的一些开发工具,不过我们会在这里说的主要是vim,gcc,makefile这三个。

正文:

vim编辑器:

模式切换:

自己画的图,虽然丑了些,不过我们还是可以发现:

①:无论在什么模式,按ESC就可以切换到命令模式

②:无需额外记忆成本,切换时先回到命令模式,然后再切换到想要的模式

命令模式:

指令:

//vim所有模式不互通指令,命令模式是主要,绝大多数快捷键,移动光标,进入其他模式的快捷键全都属于命令模式

移动光标:

①.可以通过h(←)  ,   j(↓)   ,   k(↑)   ,   l(→)来移动

②.ctrl+b/f(u/d): 屏幕向后/前滚动半(一)页

③.shift+$:移动到该行末尾

④.shift+^:移动到该行开头(空格不算)

⑤.w  e  b:w为跳转到下个单词的开头,e为跳转到下个单词的末尾,b为跳转到上个单词的开头

⑥.n(数字)+l: 向该行后面跳转n个字符

⑦. shift+g:移动到全文末尾

⑧. gg:移动到全文开头

删除文字:

①.x:删除光标所在位置的第一个字符

②.n+x:删除从光标起数的n个字符

③.X:删除光标所在位置前的一个字符(例如int,光标在t上,删除n)

④.n+X:删除光标所在位置前的n个字符

⑤.dd:删除光标所在行

⑥.n+dd:删除从光标数n行

复制:

①.yw:复制光标开始到该单词末尾的字符

②.n+yw:复制n个单词

③yy:复制光标所在行

④.n+yy:复制从光标所在行开始往下的n行

⑤.p:将缓冲区内的字符贴到光标所在位置

替换:

①.r:替换光标所在位置的字符

.shift+r:进入替换模式,替换光标所到之处的字符(替换模式内容少,在这里就一笔带过了)

撤销:

①.u:撤销上一次命令,多次按u可撤销多次

②.ctrl+r:恢复撤销,只针对u,不能撤销别的命令

跳转:

①. n+G:跳转到第n行开头

底行模式:

指令:
跳转:

①. n:只需输入一个数字,按回车就可以跳转到第n行

查找:

①./c,从光标所在行跳转一行开始向下寻找c,并且同一行遍历顺序从左往右查找,按n寻找下一个c.

②.?c,从光标所在行向上跳转一行开始向上寻找c,同一行遍历顺序从右往左,按n寻找下一个c

这里有一个很诡异的现象:经过我自己在的演示,我发现如果用?来查找一个字符时候,例如这个例子,我把光标放在25时,开始搜索r,奇怪的是,r居然停在了21行的第一个r上(最左边的),而不是最后那个r上。而当我按回车向下一行,到了22行,我再按n,居然就到了21行的最后一个r(最右边的r上)

保存:

①.  w:输入w就可以将文件保存起来

退出:

①.  q:直接退出vim

②.  wq:保存并退出,一般推荐用这个

如果无法完成,可以在后面加上!强制退出/保存并退出

插入模式:

//本质就和我们写代码差不多,也没什么指令,所以一笔带过了

视图模式:

指令:

①.d:  删除选中的内容

②.y:复制选中区域

③.p:   在光标位置粘贴

④.c:   删除所选内容,自动进入插入模式

例子:

加入注释:

①.进入视图模式:

②. 选中想添加的行

③:按shift+i切换到插入模式,输入//

④:按ESC回到命令模式

gcc编译器:

编译的理解:

我们知道,编译分为四步,预处理,编译,汇编,链接。

那么这四步,分别干了什么?我们一个一个来讲:

预处理:

例子:gcc –E hello.c –o hello.i

①. 宏替换

②. 删注释

③. 展开头文件

④.  条件编译

别的都好理解,条件编译是什么?

举个例子:

保护头文件:

#ifndef __HEAD_H
#define __HEAD_H

void func();

#endif

//由于宏里面没有{},所以依靠endif划定范围,处在ifdef与endif之间的代码,才受宏的控制

编译:

例子:gcc –S hello.i –o hello.s

gcc ⾸先要检查代码的规范性、是否有语法错误等,若都没问题,gcc 把代码翻译成汇编语⾔

汇编:

例子:: gcc –c hello.s –o hello.o

汇编阶段是把编译阶段⽣成的“.s”⽂件转成⽬标⽂件,()二进制机械码)

小demo:

这里有一个问题,为什么要转化成汇编语言?为什么要转化成二进制机械码?我们在这里讲讲这个问题:

首先,在古早时期,老手子是直接通过二进制进行写代码的,但这样太麻烦了,而且二进制,本身就不太适合人类读;所以为了阅读性,有人就发明了汇编语言,那发明了汇编语言后,可惜的是计算机只能识别二进制,不能识别汇编语言,所以在运行的时候,就要将汇编语言翻译为二进制语言,需要用到编译器,那么编译器从哪来?

这里有一个悖论,如果我用汇编写一个编译器,那这个编译器,它底层是汇编,二进制不认识,所以跑不起来,但是,我又需要一个编译器来把汇编语言转化为二进制语言,怎么办?

先用二进制写一个编译器,然后这个编译器就可以认识汇编语言,然后通过这个编译器,我再用汇编语言写一个编译器,这就实现了汇编语言写的编译器,这个过程,我们称之为编译器自举。

尽管如此,汇编语言还是太过生涩,所以后来又出现了C语言这些语言,我们就以C语言为例。那么,我们是要把C语言翻译成汇编语言,再翻译成二进制语言;还是直接把C语言翻译成二进制语言?显而易见,前者更加简单,工作量小,且安全。所以我们又进行了编译器自举,从而出现了从C语言到汇编语言的编译器

最后,我们要说的是什么呢?

结论:C语言-->汇编语言-->二进制语言,二进制语言才是计算机可以真正识别的!!!

链接:

有个问题:我既然  .o  文件,都叫可执行文件了,也翻译成了二进制,那么是不是可以直接运行了?

[tsx@VM-0-10-centos ~]$ touch haha.c
[tsx@VM-0-10-centos ~]$ vim haha.c
[tsx@VM-0-10-centos ~]$ cat haha.c
#include<stdio.h>
int main()
{
  printf("好想玩原神");
  return 0;
}
[tsx@VM-0-10-centos ~]$ gcc -c haha.c
[tsx@VM-0-10-centos ~]$ tree
.
|-- dira
|   |-- 2026_7_18
|   |   |-- hehe.c
|   |   `-- hello.txt
|   |-- dir1
|   |   |-- 7-23
|   |   |-- dir1
|   |   |   `-- dir2
|   |   |       |-- dir3
|   |   |       |-- hello.txt
|   |   |       `-- xixi
|   |   |-- dirb
|   |   |-- f267fea1643a85f1d6140c288823b506.jpg
|   |   |-- hello.txt
|   |   `-- minchao.c
|   `-- yuanshen.txt
|-- haha.c
|-- haha.o
`-- install.sh

8 directories, 11 files
[tsx@VM-0-10-centos ~]$ ./haha.o
-bash: ./haha.o: Permission denied
[tsx@VM-0-10-centos ~]$ chmod u+x haha.o
[tsx@VM-0-10-centos ~]$ ./haha.o
-bash: ./haha.o: cannot execute binary file

当然不是!!!!!!

为什么?因为printf这些函数,是你自己写的吗???当然不是

这时候就有个问题:我包含头文件了啊?为什么不可以

头文件中只包含函数声明,具体的函数实现在库中

库:

库分为两种,静态库和动态库

有什么区别呢?

静态链接:我直接把需要的函数,从静态库(.a)中拷贝过来,这样链接完后我就独立了,无需库就能完成调用

动态链接:我得到一个库的地址,我在使用需要的函数时,我去动态(.so)里找,即使链接完后,依然依赖库

优缺点对比:

静态链接:消耗的空间大,启动时略微快些

动态链接:消耗的空间小,启动时略微慢些

那我们推荐哪个?动态链接!

因为相比之下,静态链接消耗的空间是动态链接的几十上百倍

[tsx@VM-0-10-centos 2026_7_18]$ gcc haha.c -o haha_static -static
[tsx@VM-0-10-centos 2026_7_18]$ gcc haha.c -o haha
[tsx@VM-0-10-centos 2026_7_18]$ tree
.
|-- haha
|-- haha.c
`-- haha_static

0 directories, 3 files
[tsx@VM-0-10-centos 2026_7_18]$ ll
total 860
-rwxrwxr-x 1 tsx tsx   8360 Jul 25 15:09 haha
-rw-rw-r-- 1 tsx tsx     74 Jul 25 14:50 haha.c
-rwxrwxr-x 1 tsx tsx 861216 Jul 25 15:09 haha_static

如果没安装静态库,可以输入这个:

sudo yum install glibc-static

总而言之:链接就是要把所需函数对应的实现找到,并且与我的文件产生关联

make/makefile

简要介绍:

make 是工具;Makefile 是我写的配置脚本,用来指导 make 怎么编译代码

使用方法:

我们要用makefile,就要先知道两个概念,依赖关系和依赖方法:

举例理解:比如说你有个老师叫武斌,你想请假,就和武斌说:武斌啊,我是你学生!

武斌感到很奇怪,把电话挂断了......

这时,你告诉武斌说我是你的学生,这是我们之间的关系,这就是依赖关系。而我要依赖他,我要干嘛?

于是,我又给武斌打电话,我说:

武斌啊,我是你的学生!

        给我请个假呗;

这时,武斌就知道了,哦~原来他想找我请假,于是他就给我开了假条,我就达成了我的目的

这里的“给我请个假”就是我的依赖方法。

说完依赖关系和依赖方法,我们接下来说说make 和 makefile,

makefile是要我自己写的,make是指令

①. 在终端输入 make,make 工具会自动在当前目录寻找 Makefile /makefile

②. 如果找不到这个文件,直接报错:make: *** No targets specified and no makefile found. Stop.

我们接下来用这个例子来讲解:

[tsx@VM-0-10-centos 2026_7_18]$ tree
.
`-- haha.c

0 directories, 1 file
[tsx@VM-0-10-centos 2026_7_18]$ cat haha.c
#include<stdio.h>
int main()
{
  printf("好想玩原神");
  return 0;
}

紧接上图

我们往makefile文件里面写入

haha:haha.c
    gcc haha.c -o haha

注意:这里第二行开头是一个tab的距离

[tsx@VM-0-10-centos 2026_7_18]$ touch makefile
[tsx@VM-0-10-centos 2026_7_18]$ vim makefile
[tsx@VM-0-10-centos 2026_7_18]$ make
gcc haha.c -o haha
[tsx@VM-0-10-centos 2026_7_18]$ tree
.
|-- haha
|-- haha.c
`-- makefile

0 directories, 3 files
[tsx@VM-0-10-centos 2026_7_18]$ ll
total 20
-rwxrwxr-x 1 tsx tsx 8360 Jul 25 15:28 haha
-rw-rw-r-- 1 tsx tsx   74 Jul 25 14:50 haha.c
-rw-rw-r-- 1 tsx tsx   32 Jul 25 15:27 makefile
[tsx@VM-0-10-centos 2026_7_18]$ ./haha
好想玩原神[tsx@VM-0-10-centos 2026_7_18]$ 

我们就发现,成功打印出了“好想玩原神”

但我如果要清理呢?

我们往makefile中写入:

haha:haha.c
    gcc haha.c -o haha
.PHONY:clean
clean:
    rm -rf haha

如此:我们再输make clean 就可以清除了

[tsx@VM-0-10-centos 2026_7_18]$ tree
.
|-- haha
|-- haha.c
`-- makefile

0 directories, 3 files
[tsx@VM-0-10-centos 2026_7_18]$ make clean
rm -rf haha
[tsx@VM-0-10-centos 2026_7_18]$ tree
.
|-- haha.c
`-- makefile

0 directories, 2 files
[tsx@VM-0-10-centos 2026_7_18]$ 
.PHONY:

这是什么?

在讲这个之前,我们要先讲一个小东西:

三个时间:

命令:stat

[tsx@VM-0-10-centos 2026_7_18]$ stat haha.c
  File: ‘haha.c’
  Size: 74        	Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d	Inode: 656881      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1001/     tsx)   Gid: ( 1001/     tsx)
Access: 2026-07-25 15:08:43.889582547 +0800
Modify: 2026-07-25 14:50:54.341216280 +0800
Change: 2026-07-25 15:06:39.482960143 +0800
 Birth: -
[tsx@VM-0-10-centos 2026_7_18]$ tree
.
|-- haha.c
`-- makefile

0 directories, 2 files
[tsx@VM-0-10-centos 2026_7_18]$ make
gcc haha.c -o haha
[tsx@VM-0-10-centos 2026_7_18]$ stat haha
  File: ‘haha’
  Size: 8360      	Blocks: 24         IO Block: 4096   regular file
Device: fd01h/64769d	Inode: 795951      Links: 1
Access: (0775/-rwxrwxr-x)  Uid: ( 1001/     tsx)   Gid: ( 1001/     tsx)
Access: 2026-07-25 15:40:31.910107278 +0800
Modify: 2026-07-25 15:40:31.898107218 +0800
Change: 2026-07-25 15:40:31.898107218 +0800
 Birth: -

①access时间:这个时间指的是最后访问文件的时间,但是!!!!我们会发现,有时候我们访问文件之后,access时间没变,这是为什么?

它是一个文件属性,如果我频繁的看文件,那么我就要频繁的把这个文件属性写进磁盘,很浪费效率,所以呢,一般是规定访问几次以后,才进行修改一次

②.modify时间:这个时间指的是修改文件内容的时间,我们在说makefile时候就看这个

③. change时间:这个时间指的是修改文件属性的时间,但我们发现,很多情况下,我改一个文件内容,change时间也会改变,为什么呢?

因为,修改文件内容之后,文件大小会发生改变,这也是文件属性!!!!所以change时间也会变

细说PHONY

如果我们已经编译了haha.c ,我再编一次,会怎么样?

[tsx@VM-0-10-centos 2026_7_18]$ tree
.
|-- haha
|-- haha.c
`-- makefile

0 directories, 3 files
[tsx@VM-0-10-centos 2026_7_18]$ make
make: `haha' is up to date.

up to date?这是什么意思

意思是,haha这个文件它的modify时间已经在haha.c之后,那么就不让它继续编了

那如果我改一下haha.c呢?

我改成这样

 #include<stdio.h>
  2 int main()
  3 {
  4   printf("好想玩原神");
  5   printf("好想玩原神");
  6   printf("好想玩原神");
  7   printf("好想玩原神");
  8   printf("好想玩原神");                                                                                                                                                                                                                                                   
  9   return 0;
 10 }
[tsx@VM-0-10-centos 2026_7_18]$ stat haha.c
  File: ‘haha.c’
  Size: 190       	Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d	Inode: 656881      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1001/     tsx)   Gid: ( 1001/     tsx)
Access: 2026-07-25 15:49:51.964960826 +0800
Modify: 2026-07-25 15:49:51.961960811 +0800
Change: 2026-07-25 15:49:51.961960811 +0800
 Birth: -
[tsx@VM-0-10-centos 2026_7_18]$ stat haha
  File: ‘haha’
  Size: 8360      	Blocks: 24         IO Block: 4096   regular file
Device: fd01h/64769d	Inode: 795951      Links: 1
Access: (0775/-rwxrwxr-x)  Uid: ( 1001/     tsx)   Gid: ( 1001/     tsx)
Access: 2026-07-25 15:40:31.910107278 +0800
Modify: 2026-07-25 15:40:31.898107218 +0800
Change: 2026-07-25 15:40:31.898107218 +0800
 Birth: -
[tsx@VM-0-10-centos 2026_7_18]$ make
gcc haha.c -o haha
[tsx@VM-0-10-centos 2026_7_18]$ tree
.
|-- haha
|-- haha.c
`-- makefile

0 directories, 3 files
[tsx@VM-0-10-centos 2026_7_18]$ ./haha
好想玩原神好想玩原神好想玩原神好想玩原神好想玩原神[tsx@VM-0-10-centos 2026_7_18]$ stat haha
  File: ‘haha’
  Size: 8360      	Blocks: 24         IO Block: 4096   regular file
Device: fd01h/64769d	Inode: 795951      Links: 1
Access: (0775/-rwxrwxr-x)  Uid: ( 1001/     tsx)   Gid: ( 1001/     tsx)
Access: 2026-07-25 15:50:19.910103033 +0800
Modify: 2026-07-25 15:50:18.545096088 +0800
Change: 2026-07-25 15:50:18.545096088 +0800
 Birth: -

由此见得:对于没有被.PHONY修饰的,必须满足modify时间的约束,才可以执行

所以我们称.PHONY修饰的称之为“总是被执⾏

只要我把makefile改成

.PHONY:haha                                                                                                                                                                                                                                                               haha:haha.c
     gcc haha.c -o haha
.PHONY:clean
clean:
     rm -rf haha

那么

[tsx@VM-0-10-centos 2026_7_18]$ make
gcc haha.c -o haha
[tsx@VM-0-10-centos 2026_7_18]$ make
gcc haha.c -o haha
[tsx@VM-0-10-centos 2026_7_18]$ make
gcc haha.c -o haha

这样就没有问题了

一些语法:
cat makefile
bin=haha
cc=gcc
src=$(wildcard *.c)
obj=$(src:.c=.o)
lianjie=-o
bianyi=-c
shan=rm -rf
$(bin):$(obj)
	$(cc) $(lianjie) $@ $^
$(obj):$(src)
	$(cc) $(bianyi) $<
.PHONY:clean
clean:
	$(shan) $(obj) $(bin)

①. 普通语法声明:例如bin=haha

②. wildcard文件搜索函数:自动匹配当前目录中所有指定文件

③. $:起到一个展开变量内容的作用

④.变量后缀替换:obj=$(src:.c=.o),把src中的所有.c文件替换成.c文件

即为$(变量:旧后缀=新后缀)

⑤.$@: 代表当前的目标文件

⑥.$^:  代表当前规则全部的依赖

⑦.$<: 把依赖一个一个的展开

推导过程:

makefile寻找依赖关系时,要求可以从依赖推到目标,如果当前的依赖关系暂时不可以连通,那么我就把当前依赖关系压栈,直到目标和依赖可以串起来,当然,如果最后串不起来,那我就不工作咯

尾:

写的累死我了......这一篇字数很多呢,图是自己画的,指令是自己敲的,字也是一个一个打的,累死了......总之希望能够帮到大家,嗯......吃什么呢?

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐