Linux环境基础开发工具->gcc/g++
引入:gcc/g++是什么?
在上篇博客我们知道,vim是一个编辑器,vim负责的是代码的编辑;而gcc/g++是一个编译器,负责的就是代码的编译!gcc负责C语言代码的编译,而g++负责c++代码的编译!
既然是编译,那就要先看这个链接一下编译器的工作分为哪几个步骤:编译链接的过程发生了什么?_编译链接 发生了什么-CSDN博客
注:本篇博客着重讲gcc,因为g++和gcc类似,不再赘述
一:快速使用gcc和g++
现在有一个.c文件,其内容如下:
我们对其进行gcc编译,生成了a.out:
对其进行执行:
以上就是使用gcc,非常的简单!
用起来非常简单,但其中的内容其实很多,只是我们gcc指令直接一步到位了,gcc是一个编译器,博主在此篇博客中介绍过编译的整个过程:编译链接的过程发生了什么?_编译链接 发生了什么-CSDN博客
总的来说在执行编译的时候一般有以下四个步骤:
1)预处理(头文件展开、去注释、宏替换、条件编译)。
2)编译(C代码翻译成汇编语言)。
3)汇编(汇编代码转为二进制目标代码)。
4)链接(将汇编过程产生的二进制代码进行链接)。
每步对应的工作如下:
而gcc直接从一个.c文件 到了可执行文件,上面的这四步其隐式的执行了,所以我们要结合gcc的选项才能够才不同的时期停下来
二:gcc的选项
须知:
-o:将处理结果输出到指定文件,该选项后需紧跟输出文件名;所以-o也是gcc的选项,一般放在两个文件之中!
1:gcc -E(预处理)
用法:
gcc -E code.c -o code.i
解释:从现在开始对code.c文件进行翻译,当对code.c文件做完预处理工作的时候,就停下来,此时生成的文件我把他命名为code.i 。
如图:
此时我们vim进入code.i :会发现其有800多行
解释:预处理进行了多个工作,主要包括头文件展开、去注释、宏替换、条件编译等,而其中的头文件展开就是导致突然有800多行的原因;至于去注释、宏替换,可以自己在.c中写一个宏替换和注释,会发现预处理之后,注释被去掉了、宏被替换了!
至于条件编译的话,博主在上文提到的博客中没有说到,现在进行补充:
Q:条件编译是什么?
A:举个例子
现在我们创建了一个新的file.c,其内容如下:
此时直接gcc是会报错的,因为x是未定义的变量,但是却被使用,但是我们执行以下指令的时候:
gcc file.c -D x=1
解释:gcc
编译file.c文件并同时定义宏 x=1 ,所以我们现在就会生成一个a.out文件,我们对其执行一下,会发现其成功运行了:
这就是一种简单的条件编译,目前我们需了解条件编译的例子有两个:
①:在我们使用软件的时候,一些软件有专业版和社区版 ,本质就是条件编译起到的作用,让社区版的功能和专业版有所区别;
②:我们的头文件在写出来的时候 其实前后都是有条件编译的,一个头文件只有在第一次写的时候,才会被定义,后面在一个源文件中的重复包含头文件,均会被条件编译省略
2:gcc -S(编译)
用法:
gcc -S code.i -o code.s
解释:从现在开始对code.i文件进行翻译,当对其的编译工作做完的时候,就停下来。此时生成的文件我把他命名为code.s
注意:可以对code.c进行操作,也可以对预处理之后的code.i进行操作,自由选择
如图:我们gcc -S后生成了code.s
此时我们vim进入code.s:
解释:编译这一步进行的工作是->首先检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,将代码翻译成汇编语言。所以我们vim进入看见的都是会汇编语言!
3:gcc -c(汇编)
用法:
gcc -c code.s -o code.o
解释: 从现在开始对code.s文件进行翻译,当对其的汇编工作做完的时候,就停下来。此时生成的文件我把他命名为code.o
如图:
此时我们vim进入code.o:
解释:汇编阶段是把编译阶段生成的 .s 文件转成目标 .o 文件,此时的.o文件中内容都是汇编代码转化为的二进制目标代码了。
但是此时的.o文件虽然是一个目标文件,但是叫作可重定位目标二进制文件,其是不可执行的
验证其不可执行:
对.o文件进行./执行:
Q:虽然演示出了不可执行,但是这.o文件对于wtt1来说没有x权限,有x权限应该能执行了?
A:好的,对wtt1增加x权限:
还变绿了,感觉真的行哦~
解释:依旧不行!报错 :无法执行二进制文件,所以这就证明了汇编生成的目标文件不能执行,因为我们少了链接这一步!需要链接到库,才能够形成可执行文件!
4:链接
链接无选项,用法:
gcc code.o
这样默认生成的就是a.out的可执行文件,我们也可以用-o选项命名的选择:
gcc code.o -o code.out
如图:
解释:
- 链接的主要任务就是将生成的各个 .o 文件进行链接,生成可执行文件。
- gcc/g++不带-E、-S、-c选项时,就默认生成预处理、编译、汇编、链接全过程后的文件。
- 若不用-o选项指定生成文件的文件名,则默认生成的可执行文件名为a.out。
此时我们vim进入code.out:
解释: 链接后生成的也是二进制文件。因为计算机只认识二进制
Q:你这链接之后,我感受不到变化啊,有什么什么办法让我感受一下链接体现在哪?
A:要查看一个文件是否链接了,有ldd指令和file指令两种方法
①:ldd
ldd指令即可,ldd指令用于打印可执行文件或共享库所依赖的动态链接库(.so
文件)
对比一下code.out和code.o 对于ldd指令的区别:
解释:信息说明了我们的code.out文件的确是链接到了库!(图中的/lib64/libc.so.6就是当前云服务器当中的C标准库)。
②:file
解释:dynamically linked 意味着动态链接
那库有类别吗?难道还有静态链表吗?
三:动态库和静态库
1:库的类别及优缺点
函数库一般分为静态库和动态库两种:
①:静态库是指编译链接时,把库文件的代码全部加入到可执行文件当中,因此生成的文件比较大,但在运行时也就不再需要库文件了,静态库一般以.a为后缀。
②:动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件当中,而是在程序运行时由链接文件加载库,这样可以节省系统的开销,动态库一般以.so为后缀。
动态链接:
优点:省空间(磁盘的空间,内存的空间),bin体积小,加载速度快。
缺点:依赖动态库,程序可移植性较差。静态链接:
优点:不依赖第三方库,程序的可移植性较高。
缺点:浪费空间。
2:动态链接
Q:那我们写的.c 和 .cpp文件进行连接的时候,是采取那哪一种?
A:file指令就能知道
解释:dynamically linked 意味着动态链接
3:静态链接
我们还可以强行对code.o文件进行静态链接到库,指令:gcc -static
注:当然也可以直接对code.c进行强行的静态链接,自由选择即可
gcc code.o -o code.out_static -static
我们静态链接生成的可执行文件取名为 code.out_static,方便区别于code.out
此时我们就有两个可执行文件了:
解释:由静态链接的缺点可知,静态链接是把库文件的代码全部加入到可执行文件当中,所以这就是为什么我们的code.out_static的大小是861288,远远大于code.out的原因!
我们可以通过ldd来查看code.out_static是否真的被静态链接了:
解释: 信息告诉我们:这个程序是静态链接的,没有依赖任何动态库(.so
文件),因此无法列出动态库依赖关系;所以这正好说明了的确进行了静态链接
再通过file来看一下code.out_static:
解释:statically linked 意味着静态链接
总结:Linux中的编译器gcc和g++默认都是动态链接的!
四:一些细节
1:巧记选项后后缀
Q:你上面的-E -S -c 选项;和 -E生成的.i ,-S生成的.s,以及-c生成的.o 文件,怎么记忆?
A:Esc就是我们键盘上的返回键,对应的就是预处理,编译,汇编,只需记住c小写即可;而iso三个字母对应的就是预处理,编译,汇编生成的文件后缀,我不喜欢ios系统,所以它叫iso
2:指定输出文件的重要性
注意:
-E一定会生成一个.i文件,所以我们一般指定写生成的.i文件的前缀,.s 文件和 .o 文件也是类似的道理!但其实其中的-E预处理比较特殊:
-
如果不指定输出文件,预处理后的代码会直接输出到 标准输出(屏幕),而 不会自动生成
.i
文件。
而其余的编译,汇编都会直接生成文件,而不是输出到标准输出(屏幕)!
3:gcc对后缀的要求
我们平时使用gcc,一般是对文件直接编译成可执行文件,但需要注意的是,我们编译的文件一定要是.c文件!
gcc hello.txt # 默认会失败,因为 GCC 根据后缀名调用处理工具