编译器背后的故事

一、gcc生成.so动态库和.a静态库

我们通常把一些公用函数制作成函数库供其他程序使用。函数库又分为静态库动态库

静态库:在程序编译是会被连接到目标代码中,程序运行是将不再需要该静态库。步骤如下:

  1. 无论是静态库还是动态库都是由.o文件创建的,因此我们必须将源程序.c文件通过gcc先编译成.o文件

    gcc -c main1.c sub1.c sub2.c
    
  2. 由.o文件创建静态库。静态库文件名的命名规范是以lib为前缀,紧跟着静态库名,扩展名为.a。创建静态库用ar命令。

    ar -crv libsub2.a sub1.o sub2.o
    

  3. 在程序中使用静态库。

    gcc -o test3 main1.o libsub.a
    

动态库:在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入,因此在程序运行时还需要动态库存在。步骤如下:

  1. 先生成.o文件

  2. 由.o文件创建动态库文件,命名规范和静态库类似,扩展名为.so

    gcc -shared -fpic -o libsub.so sub1.o sub2.o
    

  1. 在程序中使用动态库

    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语言编译实现贪吃蛇游戏

posted @ 2020-10-17 16:34  一蓑烟雨任平生噶  阅读(94)  评论(0编辑  收藏  举报