绪论
你热爱生命吗?那幺别浪费时间,由于时间是组成生命的材料。——富兰克林 。 本篇文章写了主要写了Linux下编译器,以及编译器是如何实现编译的过程。
话不多说安全带系好,发车啦(建议电脑观看)。
附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或者其余颜色为次重点;黑色为描述需要
思维导图:
要XMind思维导图的话可以私信哈
目录
1.解决sudo问题
1.1那如何将一个用户加到信任白名单中
2.Linux编译器gcc、g++
2.1编译器是如何将一个源文件变成可执行的程序的呢?
2.1.1预处理:
2.1.2 编译
2.1.3 汇编(将汇编 转换成计算机语言 二进制)
2.1.4 链接
2.1.5 库
2.1.6目标文件(.o)和库是如何链接的?
2.1.7动静态链接的优缺点:
3.debug、release
1.解决sudo问题
知识点:
首先解决一下在之前我们已经讲过sudo的问题:虽然用了sudo但是并不能进行提权,这很好理解,因为不可能给所有的普通用户都和超级用户一样直接就能使用root一样的权限(他还要root干吗?),所以只有当用户进入到信任白名单中才能进行sudo提权(sudo file)
1.1那如何将一个用户加到信任白名单中
在root用户下:用vim打开文件:/etc/suoders
找到如下位置,将root行拷贝下来,让后再将root的名字改成所要信任的用户名
如下:
加入后就能去提取了:
2.Linux编译器gcc、g++
知识点:
功能:gcc 是用来编译C语言的 而g++一般是用来编译c++的(因为c++兼容C语言所以也能用来编译C语言)
用法:
- 先用vim写好
输入指令:gcc code (gcc + 目标文件)生成a.out可执行程序
运行方法:./ a.out (./ + 可执行程序)
g++的用法一样:用vim写好后再用g++编译器去编译源文件:g++ code.cpp
./a.out : 去执行可执行程序
2.1编译器是如何将一个源文件变成可执行的程序的呢?
他经历了下面的四个步骤:
- 预处理
- 编译
- 汇编
- 连接
下面我将逐一进行细致的分析其过程(其实我们在学习C语言时已经分析过了):
2.1.1预处理:
- 头文件的展开(将写的头文件中的内容放进文件中)
- 宏替换(把写的宏替换掉,因为在编译的前面所以无法做语法词法检查所以不关心类型)
- 去注释(将注释全部除去,他对程序编译是没用的)
- 条件编译
查看预处理后的源文件方法:
- 形成预处理后的文件指令: gcc -E 文件名
- 附指令:指定新形成的文件的文件名: -o + 新文件名
- 所以此处可写成: gcc -E code.c -o code.i (如果不带-o会默认把输出结果打印到屏幕上,对于预处理产生的文件一般习惯于以.i结尾)
- 其中-E 的意思是程序到预处理结束后就不往后走了
附:
我们为什么可以在Windows/Linux上可以开发、编程?
:是因为我们安装了vs、gcc、g++这类的平台(IDE、编译器)
看到头文件其实是从外部获取放到所写的代码中的使用说在这类平台上肯定是提前就将头文件、库文件写好了放到了某个位置,所以说对于c、c++他们不仅仅只是有个vs、gcc、g++就能写的,而更重要的其中的头文件、库文件!
像在Linux中开发的头文件放在了 : /usr/include/
所以说像在安装vs时不仅仅是安装了开发环境,还选择了对应的开发包(同步也下载了c/c++的头文件、库文件,所以说在Windows下肯定有我们下载了的头文件、库文件)
所以说当我们写#Include<...>时在程序预处理阶段就会自己去指定的位置处将头文件合并到程序中。
附:
如何在预处理阶段加上一个宏的方法:
2.1.2 编译
- 把C语言代码翻译成汇编代码
- 进行语法、词法、语义分析(查看是否有语法错误,因为预处理在编译前面所以宏是不会进行语法分析是否有错误的)
- 符号汇总:汇总一些全局变量和函数的符号
查看编译后的源文件(一般以.s结尾)方法:
- 指令:gcc -S code.c -o code.s
- -S : 意思是程序执行到形成汇编语言后停止下来
- 此时把C语言改成汇编语言
2.1.3 汇编
- 把汇编指令转换成二进制指令
- 形成符号表
查看汇编后的源文件(一般以.o结尾)方法:
- 指令:gcc -c code.c -o code.o
- -c : 意思是程序执行到汇编转换成二进制后停止下来
- 此时将汇编语言转换成了机器语言(二进制组成)
- 此时生成的code.o就是一个目标文件(又称可重定位二进制目标文件)window下是以.obj结尾的文件,此时还不能执行,还需要经过链接后才能执行该程序(没有可执行能力)
附:
可以用二进制查看工具 od 打开查看二进制文本(当用vim文本编辑器打开的时候是乱码)
2.1.4 链接
- 合并段表
- 符号表的合并和重定位
- 此处就不用再加上任何附加指令了其实直接就和正常的使用gcc一样(能写成gcc code.c)
- 指令:gcc code.o -o code(一般是自动生成a.out可执行程序)
- 当把目标文件和库连接后就能生成可执行程序了
附:
对于上面形成预处理、编译、汇编的附加指令我们可以快速记忆:
在我们键盘的左上角的 ESC 就分别对应着了预处理、编译、汇编。
后缀名可:i就正常记忆,s与附加指令一样,o用obj对应来记忆
2.1.5 库
在链接中就是将目标文件和库进行链接,所以在学习链接的过程就必须要了解一下库
库:
库是一个为我们提供库函数的实现的地方(在C语言中就是C语言标准库 , 本质还是一个文件,文件内有库函数的实现方法,所以对应着就有路径,Linux下是:/usr/lib64/libc.so )
库分为静态库、动态库:
在Linux下:.so(动态库) 、 .a(静态库)
在windows下: .dll(动态库)、.lib(静态库)
在Linux下库有自己的命名规则:lib name .so .xxx(其中name是库的名字,而xxx是不同版本。如:libc.so 此时去掉lib 和 .so 那么就能看出来他是一个c的库 )
综上所述:
库中是函数的实现,而头文件中就是函数的声明,所以我们需要先定义头文件,然后光写了头文件还不行还需要链接上库,才能让库函数正常的使用:即:头文件+库文件+自己写的源文件 才能生成 ==》可执行程序 (而链接的目的就是于此gcc将目标文件和c的库链接起来)
2.1.6目标文件(.o)和库是如何链接的?
- 动态链接
- 可执行程序通过编译器,找到动态库(共享库)进行链接
- 而动态库是不能缺失当,一旦缺失就会影响所用可执行程序
- 静态链接
- 静态库在链接的时候会将自己的方法拷贝到目标程序中,也就是即使后面静态库缺失也并不会影响到已经链接过的程序了
附: ldd指令 是一个可以查看可执行程序依赖的动态库:
用法如下:
此时就能看到code是依赖于c动态库的(libc.so.6)
静态库一般来说是没有安装的,所以需要先安装一下:
- sudo yum install -y glibc-static(c的静态库)
- sudo yum install -y libstdc++-static(c++的静态库)
用静态库链接的方法(附加指令 -static):gcc code.c -o code-static -static
如下:
此时比较动态库和静态库链接的区别:很明显用静态库的其大小会比用动态库的大许多(因为会将静态库中的内容拷贝到文件中)
附:
对于gcc来说,他的链接其实是分先后的,
- 他会优先链接动态库,当没有动态库时才会去链接静态库
- -static是改变gcc的优先级让gcc优先链接静态库(对于gcc来说他的链接可能是混合由多种动静态库组成,但注意当加上-static后就会让gcc只适配静态库)
附:file指令 能查看程序是动态链接还是静态链接的
2.1.7动静态链接的优缺点:
链接动态库:
- 优点:
- 节省空间(磁盘空间、内存空间、网络空间)
- 缺点:
- 一旦动态库有点缺失,就会导致多文件无法正常使用
链接静态库:
- 优点:
- 不依赖于库,程序能独立运行
- 缺点
- 体积大,消耗资源多
3.debug、release
- 在vs环境下就已经大概讲过,debug下的文件中添加了debug信息是可调试的、而release是发布版本进行了将调试信息优化了无法进行调试,对此debug文件就会比release文件大。
- 在Linux下默认生成的可执行程序是一个release版本的
- 若想生成debug调试版本的,可以在使用 gcc时加上 附加指令 -g
附加指令:readelf -S 可执行程序 查看可执行程序的空间布局情况以及一些数据的信息情况
对此上面的所有的 gcc的使用方法已讲完 他们都能直接替换成 g++ 用法是完全一样的就不过多赘述了
本章完。预知后事如何,暂听下回分解。
如果有任何问题欢迎讨论哈!
如果觉得这篇文章对你有所帮助的话点点赞吧!
持续更新大量Linux细致内容,早关注不迷路。