🥁作者: 华丞臧.
📕专栏:【LINUX】
各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注
)。如果有错误的地方,欢迎在评论区指出。
推荐一款刷题网站 👉 LeetCode刷题网站
文章目录
- 前言
- gcc
- gcc如何完成编译
- 预处理(进行宏替换)
- 编译(生成汇编)
- 汇编(生成机器可识别代码)
- 链接(生成可执行文件)
- 静态链接和动态链接
- 静态链接
- 动态链接
- 动静态链接测试
- gcc选项
前言
程序的编译过程:
- 预处理(进行宏替换)
- 编译(生成汇编)
- 汇编(生成机器可识别代码)
- 链接(生成可执行文件或库文件)
gcc
具体的编译过程请看👉编译和预处理详解
gcc如何完成编译
gcc的使用格式如下:
gcc [选项] [要编译的文件] [选项] [生成的目标文件名]
//gcc格式不只有一种
gcc [选项] [选项] [要编译的文件] [生成的目标文件名]
预处理(进行宏替换)
- 预处理功能主要包括宏定义、文件包含、条件编译、去注释等;
- 预处理指令是以#号开头的代码行;
- 选项“-E”,该选项的作用是让gcc在预处理结束后停止编译过程;
- 选项“-o”,指明生成的目标文件,“-i”文件为已经经过预处理的C原始程序。
gcc进行预处理的指令如下:
gcc -E hello.c -o hello.i
//从现在开始,进行程序的翻译,当将预处理做完,就停下来
测试代码如下(后面的编译过程使用的都是这个代码):
#include <stdio.h>
#define N 100
int main()
{
//注释测试
printf("hello world1!\n");
// printf("hello world2!\n");
// printf("hello world3!\n");
// printf("hello world4!\n");
// printf("hello world5!\n");
printf("hello world6!\n");
//条件编译
#ifdef S
printf("S!\n");
#else
printf("hello!\n");
//宏测试
printf("N:%d\n", N);
return 0;
}
注意:Linux当中文件的后缀相对于Linux系统没有意义,但是对于用户是有意义的。
使用vim打开test.i文件,定位到文件结尾;在文件当中我们可以看到头文件会被拷贝到test.i文件中。
头文件的意义:
- 写代码;
- 支持代码自动补齐。
我们还可以使用编译器进行传参,使用“-D”选项即可:
编译(生成汇编)
- 在这个阶段中,gcc首先要检查代码的规范性,是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言;
- 用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
gcc指令如下:
gcc -S test.i -o test.s
//从现在开始,进行程序的翻译,做完编译工作,变成汇编之后,就停下来
打开test.s文件可以看到,已经变成了汇编代码(汇编不能被执行):
汇编(生成机器可识别代码)
- 汇编阶段是把编译阶段生成的“.s”文件转成目标文件;
- 使用“-c”选项可以看到汇编代码转化为“.o”的二进制目标代码了。
gcc指令如下:
gcc -c test.s -o test.o
//从现在开始,进行程序的翻译,做完汇编工作,变成可重定向目标二进制,就停下来
使用vim打开test.o文件,可以看到里面已经不是我们能看懂的了,test.o文件已经把汇编代码转化成二进制代码了(ttest.o文件也不能被执行)。
链接(生成可执行文件)
- 在成功编译之后,就进入了链接阶段;
- 这个阶段会把你写的代码和C标准库中的代码结合起来(链接过程)。
gcc指令如下:
gcc test.o -o test
//也可以如下
gcc test.o //默认生成a.out
静态链接和动态链接
静态链接
静态链接是指在编译链接时,把库文件的代码全部加入到可执行文件中;因此生成的文件比较大,但在运行时也就不再需要库文件了一定程度上提高了执行速度。静态链接链接静态库,其后缀一般为.a
。
优势:形成的可执行程序体积小,很好地节省资源(网络、磁盘、内存);
劣势:受库升级或者被删除的影响。
动态链接
动态链接与静态相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库文件。动态链接链接动态库,其后缀一般为.so
。
优势:不受库升级或者被删除的影响;
劣势:形成的可执行程序体积太大,浪费资源(可能在网络、磁盘、内存上占用了很大的资源)。
动静态链接测试
测试代码如下:
#include <stdio.h>
int main()
{
printf("Hello world!\n");
return 0;
}
Linux当中默认的是动态链接:
指令如下:
//编译指令
gcc test1.c -o test
//查看可执行程序是否为动态链接
//可以使用file指令查看
file test1
//查看动态链接的动态库
ldd test1
在Linux下库的命名:
Linux下静态链接需要使用静态库,而操作系统中可能没有对应的静态库,如果没有需要下载。
//使用如下指令下载C标准静态库
sudo yum install -y glibc-static
//使用如下指令下载C++标准静态库
sudo yum install -y libstdc++-static
//使用如下指令下载g++
sudo yum install -y gcc-c++
//gcc静态链接编译需要在后面加上 -static
gcc test1.c -o teststatic -static
查看可执行程序是否为动态链接
file teststatic
可以看到同一个程序通过动态链接和静态链接形成的可执行文件体积大小差别很大,甚至从上图看到达到了100倍。
系统本身,为了支持我们编程,给我们提供了什么?
系统给我们提供标准库的.h
(告诉我们怎么用),标准的动静态库.so/.a
。(告诉我们,方法实现库里面有,有需要去找对应的库)
gcc选项
- -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面;
- -S 编译到汇编语言不进行汇编和链接;
- -c 编译到目标代码;
- -o 文件输出到 文件;
- -static 此选项对生成的文件采用静态链接;
- -g 生成调试信息。GNU 调试器可利用该信息;
- -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库;
- -O0
- -O1
- -O2
- -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高;
- -w 不生成任何警告信息;
- -Wall 生成所有警告信息。