gcc
是C语言的编译器,在Linux下我们也用这个编译C语言
安装gcc
sudo apt install build-essential
查看gcc版本信息
gcc --version
有时候会出现代码编译不过去的问题,通常可能是gcc的编译标准太低,不支持某些写法
比如在很多旧的编译标准下C语言for循环内不能定义变量
在Linux下输入这个就可以给目标文件改变编译标准
gcc test.c -std=c99
gcc的相关命令的格式是:gcc [options] [filenames]
输入
gcc 文件名 -o 新文件名
可以命名新生成的可执行文件,源码存的文件必须后缀名是.c,不然就会这样
这是正常编译捏
我们也可以通过指令看编译的四个过程:预处理,编译,汇编,链接
【ᐖ】编译器和解释器-CSDN博客
-E | 仅执行预处理,不进行编译、汇编和链接(生成后缀为 .i 的预编译文件) |
-S | 执行编译后停止,不进行汇编和链接(生成后缀为 .s 的预编译文件) |
-c | 编译程序,但不链接成为可执行文件(生成后缀为 .o 的文件) |
预处理
生成后缀为i的预编译文件
gcc -E TAT.c -o TAT.s
打开看就长这样,这些多出来的东西都是把头文件从库函数引入;头文件不见了,多出来的东西都来自于<stdio.h>
这是打开的头文件库
为了对比预编译的文件和源码的区别,我们进入vum的分屏模式
vs test.i //底行模式
红圈圈住的部分不全,总之红圈部分就是引入的头文件库,你会发现左屏的头文件不见了(惊讶
看看我们的编译文件
编译过程
看看我们的汇编文件
gcc -c TAT.c -o TAT.o
这个.o文件,后缀全称是.obj,是二进制指令文件,不是可执行文件
链接
链接形成可执行程序(程序和库结合的过程)
gcc test.o -o my.exe
链接就是程序里的内容对库的调用。一个语言在发明的时候要有自己的语法和库,库里有很多现成的东西,可以被调用。
查看你的文件链接的库
ldd my.exe
系统下的一个二进制库承装了很多方法:
ls /lib64/libc.so.6 -l
这个库的真实名字是掐头(去掉lib)去尾(去掉.so.6)就是它的真名:C
库有动态库和静态库之分
Linux下的动态库后缀名是.so,静态库是.a
Windows下的动态库是.dll,静态库是.lib
在链接的时候出错可能是库被误删了
目标文件要连接库,编译器告诉目标文件库的地址,目标文件在库里面找自己需要的方法,叫跳转到相应库执行;使用里面的方法再生成出可执行代码。整体的过程从编译器告诉地址开始,连接在编译的时候已经开始了,整体是动态运行的。库会跑来跑去,找库的时候就叫动态链接共享动态库,但是一旦动态库缺失,所有的动态链接这个库的程序都无法执行了(动态链接很依赖共享动态库)
于是另一种链接方式:静态链接,就是在库里找到方法后,直接把方法带回去。在编译的时候,将库中的方法拷贝到自己的可执行程序
动态库优缺点
1.不能丢失
2.节省资源(静态链接重复率很高)
静态库优缺点
1.一旦形成,和库无关
2.浪费资源
在Linux上,一般静态库是默认没有安装的
安装静态库:
yum install -y glibc-static libstdc++-static
静态链接应用场景:把需要的库拷贝到可执行程序(不依赖库),具有跨平台性
总结:选项:-ESC ,后缀名iso
计算机语言发展
在计算机语言发展过程,人们一直都在用二进制编程。就像纸带八音盒一样,在纸带上打孔,有孔就是1,没孔就是0
后来有了汇编语言, 面向机器的语言设计,他只是将机器语言做了简单编译,所以并没有根本上解决机器语言的特定性
后来发明了C语言,更适合两脚兽使用的语言,但是机器又看不懂,于是就需要编译,于是就有了编译器,把C语言翻译为汇编语言然后翻译成机器语言
编译器也是一款软件,比如我们的gcc在使用的时候需要先安装它。一般一开始我们的编译器是由二进制编写的汇编编译器。但是二进制维护性低下,所以就用汇编语言重新写新编译器,汇编语言写的编译器来编译汇编语言,就形成了新的编译器,才算比较完备了。就好像golang与语言一开始只是对C语言做了小改动,直到go语言自己也可以用go来写才算成熟
维护代码
在公司里大家合作做一个项目时,需要共同维护代码。那么我们是每人一份代码维护还是共同维护同一份代码?
如果大家各自维护,我们就需要将bug解决维持实时同步,每份代码都要测试一次,成本耗时都高。所以只需要维护一份代码就好。
我们的代码
#include<stdio.h>
int main()
{
#ifdef v1
printf("功能1\n");
#elif v2
printf("功能1\n");
printf("功能2\n");
#else
printf("功能1\n");
printf("功能2\n");
printf("功能3\n");
printf("功能4\n");
printf("功能5\n");
#endif
return 0;
}
我们的代码,更改了用户宏定义;define v2 1,默认会走第二个分支
#include<stdio.h>
#define v2 1
int main()
{
#ifdef v1
printf("功能1\n");
#elif v2
printf("功能1\n");
printf("功能2\n");
#else
printf("功能1\n");
printf("功能2\n");
printf("功能3\n");
printf("功能4\n");
printf("功能5\n");
#endif
return 0;
}
所以结果不同,这样就实现了动态裁剪,采用条件编译的方式将代码共同的部分保留,不同的部分进行动态裁减。