命为志存。 —— 朱熹
Linux中C/C 翻译过程
- 1、样例介绍
- 1、1、gcc版本过低不能编译成功
- 1、2、编写 .cxx或.cc或.cpp代码(都是C )
- 2、程序的翻译过程
- 2、1、条件编译(补充)
- 2、2、语言历史
- 3、深入理解链接
- 3、1、静态链接的使用场景
1、样例介绍
1、1、gcc版本过低不能编译成功
代码语言:javascript复制int main()
{
for(int i=0;i<10;i )
{
printf("%dn",i);
}
return 0;
}
可能对于没有配置过的centos来说,会无法编译成功。为什么呢?那是由于gcc的版本在默认的情况之下是取了一个适中的版本,不算太新,正好又不能编译for循环,所以导致这样不能进行编译。 如果想要编译的话,可以根据提示信息,进行改写一下gcc命令
代码语言:javascript复制gcc test.c -std=c99
这样的话就能够编写成功了。
gcc test.c -o my.exe -std=c99:确定版本的同时,指定生成可执行文件的名称为my.exe(能够自行定义)。
1、2、编写 .cxx或.cc或.cpp代码(都是C )
当我们写文件的时候不能够把文件名的后缀弄错这个原因之前讲过如果忘记的话,可以回头看看。 同时编写C 代码的时候不能够写gcc test.c,这样的话会报错,大概率都是链接错误,gcc不认识g 中的流。但是g 能够编译 .c 代码
代码语言:javascript复制g test.c:默认版本同时默认生成文件名称
g test.c -o my.exe -std=c 11:选择编译版本同时命名可执行文件名称。
2、程序的翻译过程
程序翻译过程简单概括: 预处理,编译,汇编,链接
翻译过程 | gcc命令 | 说明 |
---|---|---|
预处理 | gcc -E test.c -o test.i | 宏替换,去注释,头文件展开,条件编译 |
编译 | gcc -S test.i -o test.s | 将C变成汇编语言 |
汇编 | gcc -c test.s -o test.o | 汇编语言编译成为二进制目标文件 |
链接 | gcc test.o -o my.exe | 形成可执行程序 |
2、1、条件编译(补充)
其中-E表示从现在开始执行程序的翻译,预处理完成就停下。并且我们在写的时候需要尽量的把它保存在一个文件之中,如果不保存的话,会直接显示在显示器上,不方便查看修改等操作。包含头文件展开操作,直接在系统中找到对应的头文件然后进行展开,此时展开了之后头文件就失效了。包括宏替换,去注释。 条件编译: 为了方便不同版本的维护,简化调试成本和修复时间,即使是不同版本的,例如社区版或者是正式版,都是相同的一份代码,可是这么造成每个人使用的软件有区别呢?区别就在于条件编译。 条件编译能够实现对于代码的动态裁剪。
代码语言:javascript复制#ifdef X
#elif XX
#else
#endif
可以在软件维护上在条件编译的条件下,完成对于代码的裁剪,从而实现一份代码完成不同的功能。
代码语言:javascript复制gcc test.c -D(内容V1=1) test.c :命令行式的宏,更方便的实现代码的裁剪,实现功能的不同保留
!!!!预处理过程的时候头文件展开时候里面的注释没了,源文件中的注释也没有了,那说明什么?说明去注释也可能和条件编译有关!!!
2、2、语言历史
在时代的最开始的时候使用的是打孔纸袋,进行二进制的编写,编写完之后,有光的地方是1,没透光的地方是0。但是由于这样的操作太过于繁琐并且难以检查。所以人们发明了汇编语言,方便我们进行检查和编写。汇编中的符号叫做助记符(方便人们记忆)。但是呢有了汇编语言还是不行,后来就有了VB,VC语言,随着不断的发展又有新的语言,同时还在不断的发展特性,注释啊,宏啊等等一系列操作。 所以在这个过程之中编译器不是一开始就有的,二进制代码是不需要编译器的,从汇编开始以后才开始有编译器。所以随后发展的C语言来说,会站在前人的肩膀上,把C语言翻译成为汇编语言,再通过转化为二进制编程。这样能够减少很多的操作,减少没必要的麻烦。 那么在最开始的时候是先有汇编的编译器还是有汇编的呢? 其实是先有语言,但是语言编译好了也要等能够处理这个语言的汇编器出现。汇编的编译器是用二进制来编写汇编的编译器,然后编写的汇编语言,让汇编经过汇编器转化为二进制。 编译器是软件吗? 是软件。 那么之前用二进制写的汇编编译器就可以在此基础之上用汇编语言来写,利用二进制的汇编编译器,实现汇编语言写的编译器也能够正常运行并且还能简单化。这样的话从此以后也就只需要维护汇编语言写的编译器就能够直接实现运行了。—编译器的自举过程 链接?作用是什么? 链接就是把我们的程序和库结合的过程。通过和库的结合,能够简化编程过程,增加稳定性安全性。 所以在安装开发环境的时候,还会相对应的安装它的标准库。 Linux中C/C 的库位libc-2.17.so
3、深入理解链接
Linux中的库分为两种,动态库以及静态库。 动态库文件后缀:.so 静态库文件后缀:.a
Windows中也有动态库和静态库。 动态库文件后缀:.dll 静态库文件后缀:.lib
链接时,两种连接方式:动态链接,静态链接。
讲个例子来帮助理解。 你是一个高中生,学校是封闭式管理,不允许带手机电脑等一系列电子设备,但是你又特别喜欢上网玩。你很苦恼,为了能够有解决办法,你在正式上学前,找了个认识的高三学长,询问他上网的方法。学长说,虽然学校没有,但是你只要出了校门会有很多很多的网吧,去那里上网就行了,告诉你怎么去网吧的同时还告诉了你网吧的8好位置又靠窗又有空调。随着时间的推移,你越来越熟悉操作流程,不需要过多询问,你就能够处理好时间的安排并且开心的上着网吧。 所以可以总结一下,你去网吧的这个整个事件就能够简单叙述一下链接的过程。 学长就是编译器,学校就是内存,网吧就是目标库的地址。在你没有上学校之前,编译器就已经告诉了你目标库的地址在哪。编译器告诉目标库的地址称为动态链接。网吧就是动态库。编译器告诉你的8号位置就是在动态库中的方法。那你在学校管理时间安排计划表为了上网的过程,就是程序在执行过程中,执行代码(计划表),当执行过程中跳转到库(网吧)并且执行库中的方法(8号位置)之后再返回需要执行的代码。这个过程就是执行一次动态运行的过程。 所以你这么执行的时候,你的同学也会学你,这么去网吧爽一爽。可是林子大了什么都有,就会有一些人要告诉老师,顺便还要举报这个网吧让高中生上网。同时学校附近还有一个派出所,正好直接出动,到网吧之后,让网吧老板出来,问他有营业执照吗?网吧老板说:“啊?”,随后警察说,我接到匿名举报,说你们这有黑网吧,我们现在要依法处置。警察说完之后,老板说:“我保证配合你们调查”。随后网吧就被查封了。后来网吧老板打算“东山再起”,在开一个网吧,但是在开一个就不能开在原本的位置了,要是还在原本的位置,说不定好会有不少的麻烦事。所以倒霉的还有学校内的同学们,想像以前一样上网也就没有机会了。 这说明动态链接非常依赖这个网吧,网吧一旦被查封,所有的同学都将运行不了。这样的行为叫做,共享动态库,但是一旦动态库确实,所有的动态链接这个库的程序都无法执行了。如果此后还有学弟学妹问学长,学长会说上什么网,网吧都没有了。这样换而言之,以后的情况编译都有可能是一个问题。 但是不能上网的条件让你痛不欲生。虽然痛不欲生,你还是努力学习考到了年级第一。你在第一个寒假回家的时候,你的父亲询问了你的成绩。很震惊,你居然考的那么好,你的父亲决定实现你的一个愿望。正好学校网吧被封,你就和父亲说,我就想上网(主要是想查学习资料)。恰好你爸和校长又有关系,你爸就和校长打了个电话,校长说行啊,我给宿管打个招呼,让你在宿舍装一个。有趣的是,你的父亲找到了当时网吧的店长,找到店长之后询问了儿子最喜欢的位置上的机器,你爸当场就买了。之后开学的时候,你爸就把这个机器装到你的宿舍,虽然可能不是新的,但是有总比没有好。 把电脑放在学校宿舍里面的这个过程就是把库拷贝到自己的可执行程序中,这个过程也叫做静态链接。网吧老板也成为了买电脑的。从此往后去网吧的人越来越少。 在编译的时候,把库中的方法拷贝到自己的可执行程序中叫做静态链接。卖电脑的网吧老板也成为了静态库。 gcc默认生成的可执行文件利用的是动态库。
动态库及动态链接的优缺点:
- 不能够丢失
- 在内存中加载的时候只需要加载一份—节省资源
静态库及静态链接的优缺点:
- 一旦形成和库无关
- 浪费资源
怎么验证
代码语言:javascript复制gcc -o test-static test.c -static:链接静态链接
ldd test-static:查询链接属性
可是如果没有操作过的操作系统的话通常来说可能没有静态库,找不到静态库的存在。这样看来的话,系统是建议使用动态库。
/usr/bin/ld :链接加载器不能够找到 -lc。 那么现在就是安装静态库的操作
代码语言:javascript复制sudo yum install -y glibc-static libstdc -static:安装C/C 静态库
3、1、静态链接的使用场景
如果想要自己的程序想要拥有非常强的平台适用的强大性。就必须要让程序自己安装静态库,这样的话,无论在什么地方,无论别人的系统中有没有你需要的库,你都能够通过静态链接实现自己程序的运行。