gcc/g++编译器介绍
编译器的功能就是把代码经过一系列处理变成可执行文件,然后就可以执行文件实现代码的功能。
gcc编译器编译C语言文件,g++编译C++文件,g++也可以编译C语言,但gcc不能编译C++文件。
gcc使用方法:gcc [选项] 要编译的文件 [选项] [目标文件]
g++和gcc使用方法相同。
常见的C++文件后缀有.cpp|.cc|.cxx
注意以下代码使用gcc编译时可能会报错,代码本身没有语法问题。
1 #include<stdio.h>
2
3 int main()
4 {
5 for(int i = 0; i < 10; i++)
6 {
7 printf("123\n");
8 }
9 return 0;
10 }
报错显示第五行在for循环里定义变量是c99才有的,所以我们在编译时还要手动设置c99标准来编译该代码
加了标准之前:
gcc code1.c
加了标准之后:
gcc code1.c -std=c99
程序翻译过程:
- 预处理
- 编译
- 汇编
- 链接
- 生成可执行程序
在介绍程序翻译过程之前我们先介绍一下编译器的发展史,更有利于理解后面的程序翻译。
编译器的发展史
计算机语言最开始是二进制语言,所有指令都由0101组成,因为二进制语言比较麻烦所以后面就发明了汇编语言。计算机只能读懂二进制,所以以前的人们还得开发一个软件将汇编语言翻译成二进制语言才能让计算机读懂,所以人们就开发出了这样的软件,这个软件只能用二进制语言来写,这样才能让计算机运行这个软件把汇编语言翻译成二进制语言。这个软件就叫做编译器,因为是二进制语言写的所以就叫二进制编译器。
随着时间的流逝,人们发现汇编语言也不是很方便,于是就发明了高级语言,比如C语言。这时候又需要一个软件来把高级语言翻译成汇编语言,然后再由二进制编译器把汇编语言翻译成计算机看得懂的二进制语言。这个软件就叫做汇编编译器,这样我们就能比较方便的进行编程了。
既然我们有了汇编编译器和二进制编译器,那么我们就可以重写编译器,比如用汇编语言重写二进制编译器,用高级语言重写汇编编译器,于是就变成了下面这样:
因为我们还有汇编编译器(汇编语言写的)和二进制编译器(二进制语言写的),所以就能翻译高级语言写的汇编编译器和汇编语言写的二进制编译器。
有人说为什么不直接把高级语言翻译成二进制语言呢?可以但没必要,因为已经有人造好了轮子,在人们发明了高级语言时只需要写一个编译器翻译成汇编语言即可,不需要再大费周章的写一个编译器把高级语言翻译成二进制语言。
程序翻译的过程
假设我们有一个代码名为code.c
-
预处理
gcc -E code.c -o code.i
这句代码的意思是对程序进行翻译,做完预处理就停止,生成的文件重命名为code.i
- 头文件展开(编译前十几行的代码在头文件展开后可能有一千行)
- 去注释
- 条件编译(通过条件编译可以实现对代码的动态裁减)
- 宏替换
-
编译
gcc -S code.i -o code.s
这句代码的意思就是从现在开始对程序进行翻译,做完编译就停止,生成的文件重命名为code.s
- 检查代码的规范性,是否具有语法错误
- 将C语言翻译成汇编语言
-
汇编
gcc -c code.s -o code.o
这句代码的意思就是从现在开始对程序进行翻译,做完汇编就停止,生成的文件重命名为code.o
- 把编译阶段生成的.s文件转成目标文件
- 虽然
code.o
已经是二进制文件了,但此时无法执行code.o
文件,不过可以编译形成可执行程序
-
链接
*.o
+系统库=可执行程序系统库就是函数库,下面我们将会详细介绍。
函数库
用C语言举例,我们的熟知的printf
,scanf
函数,我们自己没有定义又为什么能用呢?实际上这些函数的实现都放在库文件里去了,这些库都在特定的路径下,一般没有指定时,编译器会去系统默认搜索路径查找。而在Linux中,这个路径为usr/lib
,我所用的CentOS7系统的printf
函数存放在libc.so.6
的库文件中。函数库分为静态库和动态库。
ldd [文件名]
可以查看该文件所依赖的动态库文件
静态库和动态库
Linux下动态库是
.so
,静态库是.a
,Windows下静态库的后缀是.dll
,静态库是.lib
动态库:是C/C++或者其他第三方提供的所有方法的集合,被所有程序以链接的方式关联起来,这种称为动态链接。库中所有的函数,都有入口地址,所谓的动态链接,其实就是把要连接的库中的函数地址拷贝到我们可执行程序的特定位置,也就是链接的合并符号表。
静态库:是C/C++或者其他第三方提供的所有方法的集合,被所有程序以拷贝的方式,将需要的代码,拷贝到自己的可执行程序中,这种叫做静态链接。
动态库的优缺点:
优点:形成的可执行程序体积比较小,比较节省资源。(磁盘和内存)
缺点:稍慢一点,因为要通过函数地址找到函数的实现。强依赖动态库,动态库没了,所有依赖这个库的程序都无法运行了
静态库的优缺点:
优点:无视库,可以独立运行
缺点:体积太大,浪费资源
动态库居多,静态库比较少,下面是使用动态链接和静态链接文件的对比:
gcc code.c -o code-s -static
后面加上-static
就是静态链接的方式编译
code-s
是静态链接编译的,code.exe
是动态链接编译的,很明显可以看到静态链接的可执行文件大小比动态链接的大的多。
当然这里有些人使用静态链接的方式编译时可能会报错,因为有些云服务器是没有安装静态库的,需要手动安装,输入以下代码:
C:
sudo yum install glibc-static
C++:
sudo yum install -y libstdc++-static
从上面可以看出系统一般自带动态库,而不是静态库,编译器默认链接的方式也是动态链接,原因是:
系统中有各种各样的软件,如果每个程序都是用静态链接的方式,那么每个程序执行时都要拷贝一份静态库,最后在内存里就会有很多相同的静态库,就会极大的浪费内存资源。
这也就是为什么动态库比静态库多的原因了
所以,我们的开发环境,默认都要为我们做什么呢?
- 下载开发环境
- 设置合理的查找路径
- 规定好我们形成可执行文件的链接方式