Linux编译器-gcc的使用
每博一文案
听过这样一句话,世间所有的内向都是因为聊错了对象。
人与人之间相处,最怕的就是你不相信你看到的,我却相信别人口中的我,
其实你不需要从别人口中来了解我。因为我对每个人都不太一样。
若你能走近一点,亲眼凝视我的眼睛,亲耳听听我的声音,放下芥蒂与我相处,
以你心的,换我心,你会发现,我跟别人口中所说的真的比太一样。
都说时间识人,落难之心,道听途说来的,只是别人的故事,
自己用心去了解到的,才是最真的我们
—————— 一禅心灵庙语
我们知道一个C语言程序变成一个可执行程序是要经过如下过程的:
预处理 ——> 编译 ——> 汇编 ——> 链接 这四个基本过程才可以生成一个可执行的 .exe文件
gcc的直接编译
我们编写一个C语言程序:如下
1 #include<stdio.h>
2
3 #define N 123
4
5 int main()
6 {
7
8 printf("hello,%d\n",N);
9 printf("hello,%d\n",N);
10 printf("hello,%d\n",N);
11 printf("hello,%d\n",N);
12 // printf("hello,%d\n",N);
13 // printf("hello,%d\n",N);
14 // printf("hello,%d\n",N);
15 // printf("hello,%d\n",N);
16
17 return 0;
18 }
~
我使用的是 vim 编译的,对于 vim 大家想要了解的,可以移步到 🔜🔜🔜 你一定可以看懂的:Linux编辑器-vim的使用_ChinaRainbowSea的博客-CSDN博客 如下:
编写好C语言程序后,我们可以使用 gcc 编译C语言程序,变成可执行程序
[linux@localhost dir2]$ gcc test.c
对名为 “test.c"的C语言文件,使用 gcc 进行编译,没有重向的话,默认生成的可执行程序,是 a.out
生成了 a.out 可执行程序,我们可以使用 ./ + 可执行文件名 ,运行看看,如下:
[linux@localhost dir2]$ ./a.out
运行一下名为 a.out 的程序
我们可以加上 -o(重定向) ,把可执行程序,重定向到我们指定的文件名中,没有该文件名的文件会自动创建如下:
[linux@localhost dir2]$ gcc test.c -o mybin
将 gcc test.c 编译生成的可执行程序,重定向(复制,拷贝)到 mybin 中
该重定向的文件也是可以使用 ./+文件名 运行的,结果是一样的 如下:
[linux@localhost dir2]$ ./mybin
运行一下,名为mybin的程序
预处理
我们来看看C语言程序,预处理后的内容
在预处理阶段主要进行了头文件的展开、去注释、宏替换、条件编译
头文件的展开: 使用的头文件的所在路径,的绝对路径的展开,使用到头文件 中的有关函数,变量拷贝到该原始程序中,所以经过 预处理后 的文件大小会变得有些 大
去注释: 注释的代码不使用,去了
宏替换: 将使用宏了的变量,变为数值
我们可以使用 gcc -E xxx(需要预处理的C语言文件) -o xxx.i (重定向为后缀为.i 的C原始程序),表示:对 C文件只进行 预处理 操作后就停下来,后面的编译,汇编等等都不执行
选项 ”-E" ,大写的 e 该选项的作用是让 gcc 在预处理 结束后就停止,不再进行后面的操作
选项 “-o: ,是指将内容重定向到目标文件中去,”.i" 文件为已经,经过预处理 过的 C原始程序
我们这里以 刚刚的 test.c C语言文件为例
[linux@localhost dir2]$ gcc -E test.c
未使用 -o 重定向,内容默认是打印显示在活动桌面上
我们带上重定向 -o 到文件名为 mybin.i 文件中,操作如下:
[linux@localhost dir2]$ gcc -E test.c -o mybin.i
将内容重定向到 文件名为 mybin.i 中,没有自动创建出来
打开预处理后的mybin.i 文件,和没有预处理过的 test.c文件存在什么样的不同点
我们这里都使用 vim 打开,如下:
mybin.i 存在对应有关头文件的路径的展开,以及头文件中使用到的变量,方法的拷贝,行数上的增加 854,宏的替换,注释的去除
注意: 预处理过的内容 mybin.i 文件,还是C语言 没有改变,因为其中还有我们熟悉的 main,printf 的函数
编译(生成汇编代码)
在这个阶段中,gcc首先要检查代码中的规范性,是否存在语法上的错误等等,以确定代码实际需要做的工作,在检查无误后,gcc 把代码翻译成 汇编语言 ,可以使用 “-S” 大写的 S 选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
我们可以使用 gcc -S xxx(需要编译的文件) -o xxx.i (重定向为后缀为.s的C原始程序) 表示:对文件进行编译(生成汇编代码),就停止了,而不进行后续的 汇编、链接操作,
这里同样以我们的 test .c 文件为例
[linux@localhost dir2]$ gcc -S mybin.i -o mybin.s
对 mybin.i(预处理后)的文件,进行编译,生成汇编代码
当然,这里也是可以是 test.c 未经过预处理的源文件,如下:
[linux@localhost dir2]$ gcc -S test.c -o mybin.s
不过它会重新进行一个预处理后,——> 在进行编译,生成汇编代码。因为这里我们已经有了经过预处理过后的文件的,所有我们可以直接对它进行编译,生成汇编代码
汇编代码(mybin.i) 与 test.c(原文件)的比较
汇编(生成机器可识别代码)
汇编 过程把 经过编译处理生成的汇m j编代码,生成二进制目标代码
汇编阶段是把编译阶段生成的 “.s” 文件转成 二进制目标文件**“.o”** ,可以使用 选项 ”-c"(小写的 c) 就可以看到汇编代码已转为 “.o" 的二进制目标代码了
我们可以使用 gcc -c xxx(需要编译的文件) -o xxx.i (重定向为后缀为.o的C原始程序) 表示:对文件进行汇编**(生成二进制目标代码)** ,就停止了,而不进行后续的链接操作
同样以 test 文件为例
[linux@localhost dir2]$ gcc -c mybin.s -o mybin.o
对 mybin.s 汇编代码,进行汇编生成二进制目标文件 mybin.o
同样我们也是可以直接对 test.c源文件,进行汇编操作的,如下:
[linux@localhost dir2]$ gcc -c test.c -o mybin.o
不过,不过它会重新进行一个预处理后,——> 在进行编译,生成汇编代码,——> 再对汇编代码,进行一个汇编操作,生成二进制目标文件
test.c (源文件) 与 mybin.s (二进制目标文件)
注意该生成的二进制目标文件是无法,运行的(不可执行),因为这仅仅是把我们所写的代码汇编生成了二进制的目标文件,并没有把我们导入头文件中的库函数中,方法 ,变量汇编成二进制目标文件,所以是无法运行的,我们可以试试看
没有权限我们把,可执行权限加上,对于文件的权限的管理,大家可以移步到 🔜🔜🔜 Linux 的权限命令_ChinaRainbowSea的博客-CSDN博客
linux@localhost dir2]$ chmod u+x mybin.o
再运行看看,结果还是不行,
[linux@localhost dir2]$ ./mybin.o
链接
经过了 预处理 ——> 编译 (生成汇编代码)——> 汇编(生成二进制代码)——> 链接
链接: 本质上就是引入我们在代码中使用的第三方库(c库),拷贝到我们的代码中,默认链接的是 C库 (由编译器和文件共同决定),所以不需要带 选项,直接使用 gcc 加上 重定向 -o ,操作如下:
[linux@localhost dir2]$ gcc mybin.o -o mybin
将 mybin.o 二进制目标文件 链接成 mybin 可执行程序
我们也可以使用和开头中的 gcc的直接编译一样,对源文件(test.c),进行编译链接
[linux@localhost dir2]$ gcc test.c -o mybin
不过需要经过一下 :预处理——> 编译——> 汇编——> 链接 可执行程序
运行看看, 链接后的二进制文件,可以运行
[linux@localhost dir2]$ ./mybin
test.c(源文件) 与 mybin(链接后的可执行文件)
我们可以使用ldd 命令,查看 对应链接的库的所在路径
[linux@localhost dir2]$ ldd mybin
libc.so.6 :动态库,库真正的名字是: 去掉前缀 lib, 去掉 .xxxx 点后面的,剩下的就是库的名字,这里是 c 库
lib.a : 静态库,库真正的名字是: 去掉前缀 lib, 去掉 .xxxx 点后面的,剩下的就是库的名字,这里是 c 库
函数库
我们的 C程序 中共,并没有定义 ”printf" 的函数实现,且在 预编译 中包含的 stdio.h 中也只有该函数的声明,而没有定义该函数的实现,那么是在哪里实现 printf 函数的呢?
答案: 是系统把这些函数实现都被 ,做到名为 libc.so,6 的库文件中去了,在没有特别指定时候,gcc 会系统默认的搜索路径,“/usr/lib" 下进行查找与系统变量类似 ,也就是链接到 lib.c.so.6 库函数中去,这样就能实现函数 ”printf" 了,而这也就是链接的作用
函数库一般分为静态库和动态库两种
静态库 是指 编译链接时 ,把库文件的代码全部加入到 可执行文件中 因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为 “.a" ,使用 -static 命令,导入静态库
再使用 -static 命令之前我们需要先安装一下,具体原因:大家可以移步至:/usr/bin/ld: cannot find -lc错误原因及解决方法
linux@localhost]$ sudo yum install glibc-static
安装好后,我们导入静态库 -static
[linux@localhost dir2]$ gcc test.c -o mybin_s -static
如下图我们可以看到,其最明显的结果是:导入静态的库,生成的可执行程序,在存储大小上明显比较大许多
动态库与之相反,在编译链接时并没有把库文件的代码到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为 ”.so" ,如前面所述的 lib.so.6 就是动态库。gcc 在编译时默认使用动态库,所以不用带选项,完成了链接之后,gcc 就可以生成可执行文件,如下所示:
[linux@localhost dir2]$ gcc test.c -o mybin
gcc 默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证
记忆方式
对于上述gcc 命令的记忆技巧,如下图所示:
最后:
限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵多多益善,谢谢大家,后会有期,江湖再见!