编译器背后的故事
一、gcc生成.so动态库和.a静态库
我们通常把一些公用函数制作成函数库供其他程序使用。函数库又分为静态库和动态库。
静态库:在程序编译是会被连接到目标代码中,程序运行是将不再需要该静态库。步骤如下:
-
无论是静态库还是动态库都是由.o文件创建的,因此我们必须将源程序.c文件通过gcc先编译成.o文件
gcc -c main1.c sub1.c sub2.c
-
由.o文件创建静态库。静态库文件名的命名规范是以lib为前缀,紧跟着静态库名,扩展名为.a。创建静态库用ar命令。
ar -crv libsub2.a sub1.o sub2.o
-
在程序中使用静态库。
gcc -o test3 main1.o libsub.a
动态库:在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入,因此在程序运行时还需要动态库存在。步骤如下:
-
先生成.o文件
-
由.o文件创建动态库文件,命名规范和静态库类似,扩展名为.so
gcc -shared -fpic -o libsub.so sub1.o sub2.o
-
在程序中使用动态库
gcc main1.c libsub.so -o test4
总结注意:上述源程序代码main1.c,sub1.c,sub2.c为下
//main1.c
#include"sub1.c"
#include"sub2.c"
#include<stdio.h>
int main(){
int a,b,d,s;
a = 8;
b = 24;
d = x2x(a,b);
s = x2y(b,a);
printf("The result one is =%d\n",d);
printf("The result two is =%d\n",s);
return 0;
}
//sub1.c
extern int x2x(int a,int b){
int r;
r = a+b;
return r;
}
//sub2.c
extern int x2y(int a,int b){
int r;
r = a*b;
return r;
}
(1)动态库和静态库同时存在时,优先使用动态库
(2)当静态库和动态库同名时,gcc命令会优先使用动态库,默认去连/usr/lib和/lib等目录下的动态库,这时需要将.so文件复制到目录/usr/lib下即可 sudo cp ***.so /usr/lib
二、gcc背后的故事
gcc编译过程分为四个阶段进行:预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)
1)预处理
预处理主要包括以下过程(1)将所有的#define删除,并且展开所有的宏定义,并且处理所有的条件预编译指令,比如#if #idfef #elif #else #endif等(2)处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。(3)删除所有注释“//” “/* */ ”。(4)添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。(5)保留所有的#pragma编译器指令,后续编译过程需要使用它们。
预处理命令如下(本文章均用test.c):
gcc -E test.c -o test.i 或 gcc -E test.c
gcc的 -E选项,可以让摆一起在预处理后停止,并输出预处理结果。在本例中,预处理结果就是将stdio.h文件中的内容插入到test.c中
2)编译
编译过程就是对预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生成相应的汇编代码。
编译命令如下:
gcc -S test.i -o test.s
gcc的 -S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件
3)汇编
汇编过程调用对汇编代码进行处理,生成处理器能识别的指令,保存在后缀为.o的目标文件中。注:当程序有多个源代码文件构成时,每个文件都要先完成汇编工作生成.o文件才能进行下一步链接工作
gcc -c test.s -o test.o
将生成的汇编代码文件编译为目标文件
4)链接
链接分为静态链接和动态链接。区别如下
(1)静态链接是指在编译阶段直接把静态库加入到可执行文件中去,其中可执行文件会比较大。
(2)动态链接是指链接阶段仅仅只加入一些描述信息,而程序执行时再从系统中把相应动态库加载到内存中去,相对来说可执行文件会小很多
gcc test.o -o test
过程
生成的各种文件
静态库和动态库生成的文件大小比较
as汇编编译器针对的是AT&T汇编代码风格,Intel风格的汇编代码则可以用nasm汇编编译器编译生成执行程序。请在ubuntu中下载安装nasm,对示例代码“hello.asm”编译生成可执行程序,并与“hello world”C代码的编译生成的程序大小进行对比。
三、第三方库的了解
1)了解Linux 系统中终端程序最常用的光标库(curses)的主要函数功能,写出几个基本函数名称及功能;
initscr():初始化curses库和ttty。(在开始curses编程之前,必须使用initscr()这个函数来开启curses模式)
endwin():关闭curses并重置tty。(结束curses编程时,最后调用的一个函数)
cbreak():开启cbreak模式,除了 DELETE 或 CTRL 等仍被视为特殊控制字元外一切输入的字元将立刻被一一读取
crmode():使得终端进入到cbreak模式。
move(y,x): 将游标移动至 x,y 的位置.
getyx(win,y,x): 得到目前游标的位置. (请注意! 是 y,x 而不是&y,&x )
clear() and erase(): 将整个萤幕清除. (请注意配合refresh() 使用)
2)在 win10 系统中,“控制面板”-->"程序"--->"启用或关闭Windows功能",启用 "telnet client" 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net,以游客身份体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)。
3)在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库,请说明 头文件(比如curses.h)和库文件都被安装到哪些目录中?
头文件(比如curses.h)和库文件被分别安装在/usr/include/和/usr/lib/下
4)Linux 环境下C语言编译实现贪吃蛇游戏