一.gcc介绍
gcc是一个C语言编译器,和我们平时用的VS2022不是一个概念,VS2022不仅可以编辑代码还可以编译执行代码。他是整合了很多功能的集成开发环境,简称IDE。gcc只负责编译C语言代码。
gcc 使用:
gcc [选项] 要编译的文件 [选项] [目标文件]
二.gcc翻译过程
(1)预处理(进行宏替换)
指令:
gcc -E test.c -o test.i
说明:
- 预处理功能主要包括宏定义,文件包含,条件编译,去注释等
- -E选项:让代码进行预处理,预处理之后就停下来。
- -o选项:后面紧跟着的是生成的目标文件名称,上面的命令也可以写成:gcc -o test.i -E test.c
举例:
1 #include<stdio.h> 2 #define M 100 3 #define MAX 10000 4 int main() 5 { 6 printf("hello gcc%d\n",M); 7 printf("hello gcc%d\n",M); 8 printf("hello gcc%d\n",M); 9 printf("hello gcc%d\n",MAX); 10 printf("hello gcc%d\n",MAX); 11 printf("hello gcc%d\n",MAX); 12 13 return 0; 14 } 15
我们发现,原本只有是几行的代码现在有了大还几百行。这就是因为在预处理阶段进行了头文件的展开,并且定义的宏也被替换了。
(2) 编译(生成汇编)
指令:
gcc -S test.i -o test.s
说明:
- 在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。
- “-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
举例:
查看test.s文件:
(3)汇编(生成机器可识别代码)
指令:
gcc -c test.s -o test.o
说明:
- 汇编阶段是把编译阶段生成的“.s”文件转成目标文件
- 选项“-c”:代码翻译到汇编结束就停下来,就可看到汇编代码已转化为“.o”的二进制目标代码了
二进制文件test.o:
这既是二进制文件,我们当然是什么都看不懂了,这就是给计算机识别的文件。
那既然是二进制文件,可不可以执行呢?
告诉我们权限不够。
我们给他给予上执行权限:
告诉我们,test.o是无法执行的二进制文件,也就是说汇编生成的二进制文件是无法执行的。
(4)连接(生成可执行文件或库文件)
指令:
gcc test.o -o test
说明:
- 命令没有选项。
- 直接生成可执行的文件。
执行:
(5)总结gcc
我们在使用gcc 不必 预处理,编译,汇编 ,链接,一步一步的执行,我们可以直接使用
gcc C语言源文件 -o 目标文件
gcc选项
-E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
-S 编译到汇编语言不进行汇编和链接
-c 编译到目标代码
-o 文件输出到 文件
-static 此选项对生成的文件采用静态链接
-g 生成调试信息。GNU 调试器可利用该信息。
-shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
-O0
-O1
-O2
-O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
-w 不生成任何警告信息。
-Wall 生成所有警告信息
三.动静态链接库
(1)函数库
我们在上卖面写的代码里面使用了printf函数,但是我们并没有定义printf函数,而且stdio.h头文件里面也只有printf函数的声明而已。那是哪里实现的printf函数呢?
系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到
系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函
数“printf”了,而这也就是链接的作用。
也就是说没有库, gcc 就无法对代码进行翻译。
其实我们从人生中写的第一个代码输出"hello word",就一直在使用库,也就是语言库。
例如上面的写的C语言代码,翻译形成的可执行文件 test ,它也依赖了库。
我们可以使用命令 :ldd,来查看文件依赖那些库。
ldd 【文件】
我们甚至可以看一下命令依赖哪些库:
这里不能发现 ls 命令也是依赖库的, 甚至依赖着C语言库,因为我们的命令有相当一部分就是C语言写的,其实命令也就是程序。
(2) 动态库与静态库
函数库一般分为静态库和动态库两种:
- 1.静态库:格式为
lib***.a
。 - 2.动态库:格式为
lib***.so
。
libc.so.6就是一个动态库,lib是前缀,so是后缀,c就是库名字,6是版本号,libc.so.就是C语言的库。
静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”
动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件 ,gcc test.o -o test ,test就是生成的可执行文件。
gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证。
(3)动态链接与静态链接
库的连接方式分为:动态链接和静态链接。
Linux下gcc默认是动态链接:
我们通过,file命令来查看可执行文件的连接方式。
file 【文件名】
我们将 file test 的内容输出重定向到 file.txt 中
那为什么linux下gcc 不去使用静态链接呢?我们先了解一下动态链接和静态链接。
静态链接:连接的时候静态链接找到静态库,把库文件的代码全部加入到可执行文件中。
优点:静态链接成功,程序将不依赖任何库,自己就可以独立运行。
缺点:因为自身的拷贝的原因,比较房费空间。
动态链接:链接时动态链接找到动态库,拷贝动态库中代码的地址到可执行程序中的相关位置。
优点:因为可以做到被大家共享方法,所以真正的实现永远都是在库中,程序内部只有地址,比较节省空间。
缺点:我们的程序还是依赖动态库,一旦动态库失效,程序就无法运行。
而Linux下使用动态库的原因就是,一般对应的动态库并不会出问题,还可以减小空间的消耗,综合考虑动态库更优。
(4)静态链接的使用
我们使用gcc 就可以实现默认动态链接,如果我们就是想使用静态链接呢?静态链接需要特定的方式来实现,而且linux默认只安装了动态库,要想实现静态链接还得先安装静态库。
安装静态库:
sudo yum install -y glibc-static
安装以后使用命令:
gcc test.c -o test_static -static
我们将file test.static 内容输出重定向到 file.txt 中。
test_static 就是静态链接生成的可执行文件,并且我们发现,静态链接生成的可执行程序的大小,远大于动态链接生成的可执行程序的大小。