目录
- 传统艺能😎
- 回顾🤔
- -E 指令🤔
- -S 指令🤔
- -c 指令
- 动态链接与静态链接🤔
- - static 选项🤔
- gdb 调试🤔
- coding 技巧🤔
- make/Makefile🤔
- 概念🎉
- 项目构建🎉
传统艺能😎
小编是双非本科大一菜鸟不赘述,欢迎大佬指点江山
🎉🎉🎉倾力打造转码社区微信公众号🎉🎉🎉
回顾🤔
说到 Linux 编译原理还得说说文件底层,为什么机器只认识二进制文件呢?其实并不是机器只认识二进制,而是机器的各种硬件比如磁盘,cpu他们只认识二进制,因为需要通电工作,而电在机器中分辨为高电平(1)和低电平(0),自然识别出来就是二进制的 0,1。
在之前就从 C 语言预处理出发谈及了程序执行原理,如雷贯耳的预处理,编译,汇编,链接在 Linux 环境下也是一样的,尤其是 gcc 环境,这四个大步骤只是一个粗略认知,细节是非常深奥复杂的涉及到编译原理这一大学科。==gcc 是 Linux 中仅支持 C 语言编译的环境,我们现在的环境是 g++,也就是 C++ 环境 ==。
gcc 条件我用的并不是太多,毕竟接触了 C++,gcc 有一个改名指令,因为输出目标文件默认为 a.out ,如果想自己命名可使用 -o 指令:
gcc test.c -o newname
-E 指令🤔
当然,我编写程序运行出来都是一步到位,要是想单独研究中间预处理这些个过程该怎么办呢?我们可以依靠 -E 指令完成:
gcc -E test.c -o test.i
-E 是指:强制程序进行翻译时只到预处理步骤结束就终止, -o 是因为预处理完了形成的大量汇编内容会输出到屏幕上,不方便查看,所以我们 -o 重定向到 .i 文件。根据之前的经验,为了更好的展现预处理的作用,我针对预处理的功能只在源文件中添加了宏定义,头文件,注释。
我们 vim 打开源文件,Esc 进行底行模式输入 vs test.i 就可以进入 .c 和 .i 文件的分屏模式:
这张图也展现了预处理中头文件展开这项工作,预处理出来的文本是能看懂的,所以结果本质上还是C语言,但是他是干净的C语言,很明显这是知道了系统下路径才可以进行头文件引用,C 语言也并不是随随便便包一句 #include <stdio.h> 就可以搞定了,他必须有C语言的库才行。
其实头文件并不是什么必需品,不需要创建了项目一来就敲个头文件上去,本质上我们不是需要头文件而是依赖某些头文件所声明的方法,比如 stdio 声明了输入输出接口,但是我不包含就不需要:
简单的一句代码不包含头文件一样能编译通过对吧。
-S 指令🤔
gcc -S test.i -o test.s
同样的,-S 是指:强制程序进行翻译时只到编译步骤结束就终止。所有的 .s 后缀文件都是汇编文件。
-c 指令
gcc -c test.s -o test.o
-c 是指:强制程序进行翻译时只到汇编结束就终止。.o 后缀文件就是 .obj 文件,它是每个程序的源文件在编译器 Debug 目录下生成的可重定位目标文件,说白了就是链接前的最后一个步骤文件。
(前三步其实很好记,记住你键盘左上角的 Esc 键,只需要 s 变大写即可)
以上这些文件其实都是在对源文件 test.c 进行操作,但是比如 printf 他是如何被运作出来的,我们都知道 printf 是C语言函数,但是我在我的文件里的 printf 它只是一句苍白的单词?这就抛出了一个问题:代码里面的 printf 是如何和C语言库函数 printf 进行关联的?
答案是链接,这一步并不需要我们指令去展现,他也不是执行它是在产生关系。因为前面三步走完系统会自动链接。如果说头文件是方法,那么库文件就是提供方法的实现,以供链接来实现可执行程序,他们就如同 .h 和 .c 的关系 。
动态链接与静态链接🤔
常见的动态库包括 Linux 的 .so 库,Windows 的 .dll 库;静态库包括 Linux 的 .a 库,Windows 的 .lib 库。对应的库又产生了两种不同的链接方式:动态链接和静态链接。
动态链接的本质就是我写我自己的代码,但是要实现需要借助外部库,我只需要在链接阶段将库的地址关联进来即可。我们使用者就好像是一群限制自由的高中生,但是外部库就想一个网吧我们回是不是需要他,当然需要的也不只是我一个人,所以这个网吧就相当于是被我们所有人共享的一个库。
而静态库就相当于时代进步了,自家有了高大上的电脑了,随时随地也能在家开黑,就不需要借助网吧这种外部库。虽然咱自家的电脑硬件上和网吧没关系,但是功能上并无差别,将原本库里面的代码拷贝到可执行程序,形成属于我自己的方法,这就是静态链接。
总的看下来就知道动态链接的优点就在于共享,任何身份都可以进行访问,也就是系统中只要存在一个 C 语言标准库就可以满足成百上千的命令,就可以节省资源 ;当然惠及越大必然代价越大,一旦库缺失将面临几乎所有指令崩溃的窘境。因此静态库就优点就显而易见了,他不依赖任何库程序可独立执行,缺点就是浪费资源。
- static 选项🤔
原本 Linux 默认生成文件类型都是动态链接方式,但是我们可以自己改变他的链接方式:
gcc test.c -o mytest2 -static
这个指令生成的 mytest2 就是一个静态链接文件,对比一下就看得出浪费空间这个缺点发挥的是淋漓尽致,将近 100 倍要是大项目现在可能已经寄了:
gdb 调试🤔
gdb 是 Linux 中一个重要主流的调试手段,只需要 gdb + 可执行程序名即可进入调试模式:
gdb exename
但是你会发现此时还无法调试甚至报了个错:未发现调试符号
为什么会报这个错呢?其实,默认形成的可执行程序没法调试!!不知道各位还记不记得C语言程序发布有两种形式:debug 和 release,我们在 vs 的环境中默认是 debug 所以支持随时随地的调试,但是 Linux 默认是 release,要调试就需要 -g 选项来修改一下:
gdb exename -o exename -g
这样就能转换出一个 debug 版本的文件:
coding 技巧🤔
- 进入调试模式是空的,所以我们首先需要的是展现代码,先bia一个 list 指令,可简写为 l 来展示全部代码段,l + 0 是从第 0 行显示, l + main 是从 main 开始显示。
- 断点:正式调试需要打断点,语法为:== breakpoint + 对应行数==,breakpoint 显得过于繁琐可简写为== b + 行数 ==即可,查看断点信息 info b ,这里随便打了 2 个断点为例:
- 删除断点用 delete + 断点编号,可简写为 d + 断点编号;
- 查看、打印数据:如果想查看此时某个数据,我们采用== print + 对象命令打印显示,可简写为 p + 对象== 。
- 逐语句执行: step 指令,可简写为 s,相当于 vs 里 F11 键的作用。
- 逐过程执行:区别于逐语句,逐过程把函数当成一个大步骤直接走完不进入,我们采用 next 指令,可简写为 n ,相当于 vs 里的 F10 键的作用。
- 监视值:display + 对象参数名,可以在往后调试的每一次步骤中常显示,类似于 vs 中的监视窗口,取消常显示 :undisplay。
- 跳转:如果在一个循环内,我调试几步发现没有毛病想跳出循环又不改动里面内容,我们直接 until + 行数 可跳转到对应行。
- 断点间执行: c ,即 continue ,运行到下一个断点处停止。
- 运行程序: r ,相当于 vs 里的 F5 键 。
make/Makefile🤔
概念🎉
make/Makefile 是 Linux 项目自动化构建工具,会不会写从侧面可以反映你是否具备完成大型工程的能力。
一个工程中的源文件不计其数,按照类型、功能、模块分别放在若干个目录中,makefile 定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,makefile 带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,极大的提高了效率。
make 是一个解释 makefile 中指令的命令工具,一般来说,大多数的 IDE 都有这个命令,比如:Visual C++的 nmake,Linux下 GNU 的 make 。所以 make 是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
项目构建🎉
项目的构建牵扯到项目的结构,一个项目里面包含了多个 .c ,.h 或者 .cpp ,到底先编译谁?需要链接哪些库?库和头文件去哪里找?这些问题我们压根儿就不关心,vs 下之所以一个 F5 就能运行是因为 vs 自动的给咱维护好了,但是 Linux 下就没有这么幸运了,我们面对多文件体系的办法就是使用 makefile ,在存放源文件,头文件的目录中 touch 一个 Makefile ,之所以能用 touch 就表明此时 Makefile 还是一个普通文件,他里面会包含两个东西:
- 依赖关系
- 依赖方法
通俗点讲我们要生成可执行文件的依赖关系就是源文件 test.c,依赖方法就是 gcc test.c -o test.c ,至此我们就可以打开 Makefile 进行添加:
注意第一行是依赖关系,下一行不能一来就写依赖方法,要以 Tab 键开头!之后在指令中键入 make 即可,make 指令相当于 vs 下的生成解决方案。那么相应的清理解决方案怎么办呢?我们照样打开 Makefile ,添加进语句:
这里 clean 是个孤儿没有依赖关系所以后面不需要跟,好了就可以出来 make claen 一下就可以了。这里的 .PHONY 是一个 makefile 中的关键字,他后面跟的叫做伪目标,这个写不完了,写一篇讲吧,
今天就到这里,润了家人们。