一.用gcc生成静态库和动态库
我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库和动态库两种。静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。
本文主要通过举例来说明在Linux 中如何创建静态库和动态库,以及使用它们。
1.生成例子程序hello.h,hello.c,main.c
先创建一个作业目录,保存本次练习的文件。

用vim创建和编辑

hello.c是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出"Hello XXX!"。hello.h为该函数库的头文件。main.c为测试库文件的主程序,在主程序中调用了公用函数hello。
代码如下
hello.h
#ifndef HELLO_H
#define HELLO_H
Void hello(const char *name);
#endif //HELLO_H
hello.c
#include <stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n", name);
}
main.c
#include "hello.h"
int main()
{
hello("everyone");
return 0;
}
2.将 hello.c 编译成.o 文件
无论静态库,还是动态库,都是由.o 文件创建的。因此,我们必须将源程序hello.c 通过gcc 先编译成.o 文件。在系统提示符下键入以下命令得到hello.o 文件。
$ gcc -c hello.c
用ls查看当前目录下发现以及生成hello.o文件

3.由.o 文件创建静态库
静态库文件名的命名规范是以lib 为前缀,紧接着跟静态库名,扩展名为.a。
例如:我们将创建的静态库名myhello,则静态库文件名就是libmyhello.a。
创建静态库用ar 命令。在系统提示符下键入以下命令将创建静态库文件。
$ ar -crv libmyhello.a hello.o
该命令的意思是创建静态库文件libmyhello.a。

4.在程序中使用静态库
在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明静态库名,gcc 将会从静态库中将公用函数连接到目标文件中。
attention:gcc会在静态库名前加上前缀lib,然后追加拓展名.a得到的静态库文件名来查找静态库文件
因为在main.c中,包含了静态库的头文件hello.h,然后在主程序main中就可以直接调用公用函数hello,
方法一:
输入以下代码
$ gcc -o hello main.c -L. –lmyhello
自定义的库时,main.c 还可放在-L.和–lmyhello 之间,但是不能放在它俩之后,否则会提示myhello 没定义, 但是是系统的库时,如g++ -o main(-L/usr/lib) -lpthread main.cpp就不出错。
然后运行Hello文件,文件被顺利执行

方法二:
输入以下代码
$ gcc main.c libmyhello.a -o hello
运行之后,也成功得到了正确的结果
方法三:
首先生成main.o:
$ gcc -c main.c
再生成可执行文件
$ gcc -o hello main.o libmyhello.a
接着运行程序

也成功得到了结果!
动态链接时也可以这样做
我们删除静态库文件试试公用函数 hello 是否真的连接到目标文件 hello 中了。
$ rm libmyhello.a
$ ./hello
如图所示,删除掉静态库文件后也能连接到目标文件hello中,静态库中的公用函数已经连接到目标文件中了。
5.由.o文件创建动态库文件
动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如:我们将创建的动态库名为myhello,则动态库文件名就是libmyhello.so。用gcc 来创建动态库。在系统提示符下键入以下命令得到动态库文件libmyhello.so。
$ gcc -shared -fPIC -o libmyhello.so hello.o
其中 -o 不可少
使用ls命令发现动态文件库已经生成

6.在程序中使用动态库
在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明动态库名进行编译。我们先运行 gcc 命令生成目标文件,再运行它看看结果。
$ gcc -o hello main.c -L. -lmyhello
$ ./hello
而现在控制台却会报如下的错误
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
这是因为我们少做了一个步骤,程序无法找到动态文件库。我们需要把文件 libmyhello.so 复制到目录/usr/lib中程序才能运行
输入如下命令
$ sudo mv libmyhello.so /usr/lib
至此,再次输入./hello就能成功运行了

二.静态库.a与.so库文件的生成与使用
1.创建目录和任务所需的相应的文件
和上一个例子的方法一样,新建test2文件夹后用vim创建A1.c,A2.c,A.h,test.c

各文件的代码如下:
A1.C
#include<stdio.h>
void print1(int arg)
{
printf("A1 print arg:%d\n",arg);
}
A2.C
#include<stdio.h>
void print2(char *arg)
{
printf("A1 print arg:%s\n",arg);
}
A.h
#ifdef A_H
#define A_H
void print1(int);
void print2(char *);
#endif
test.c
#include<stdlib.h>
#include"A.h"
int main()
{
print1(1);
print2("test");
exit(0);
}
2.静态库.a 文件的生成与使用
首先,我们需要生成目标文件.o文件
$ gcc -c A1.c A2.c

接下来再生成静态库.a文件
使用.a 库文件,创建可执行程序
$ gcc -o test test.c libafile.a
$ ./test

3.共享库.so 文件的生成与使用
(1)生成目标文件(xxx.o) (此处生成.o 文件必须添加"-fpic"(小模式,代码少),否则在生成.so文件时会出错)
$ gcc -c -fpic A1.c A2.c

(2)生成共享库.so文件
$ gcc -shared *.o -o libsofile.so
(3)使用.so库文件,创建可执行程序
$ gcc -o test test.c libsofile.so
$ ./test

直接这样使用出现了错误,我们可以运行ldd test,来查看链接情况

发现图中并没有对应的.so文件。
查阅资料后发现,这是由于Linux自身系统设定的相应的设置的原因,即只在/lib 和 /usr/lib下搜索对应的.so文件,所以我们需要用cp命令复制.so文件到指定的文件夹
$ sudo cp libsofile .so /usr/lib
这样就能成功运行,解决掉了这个问题了!
还有一种方法
直接使用
$ gcc -o test test.c -L. -lname
来使用相应的库文件
其中,
-L.:表示在当前目录下,可自行定义路径path,即使用-Lpath 即可。
-lname:name:即对应库文件的名字(除开lib),即若使用libafile.a,则name 为afile;若要使用libsofile.so,则name 为sofile。
4.练习
a.静态库的练习并记录大小
题目:沿用上次的一个x2x函数(输出两个数的和),新建一个x2y函数(功能自定),main函数代码将调用x2x和x2y;
将这3个函数分别写成单独的3个 .c文件。
(1)创建一个头文件head.h
代码如下:
#ifndef HEAD_H
#define HEAD_H
float x2x(int a,int b);
float x2y(int a,int b);
#endif
(2)创建一个sub2.c文件包含x2y函数,作用是输出两个数的差值
代码如下
#include<stdio.h>
float x2y(int a,int b)
{
float c=0;
c=a/b;
return c;
}
(3)创建main.c文件
#include<stdio.h>
#include"head.h"
void main()
{
int a=2,b=1;
printf("%d\n",x2x(a,b));
printf("%d\n",x2y(a,b));
}
(4)用gcc分别编译为.o文件
(5)将sub1、sub2用ar工具生成一个.a的静态库文件
(6)用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序并记录文件的大小

(7)查看静态库生成的文件大小

b.动态库练习并记录大小
(1)生成动态库

(2)使用.so库文件,创建可执行程序
(3)使用同样的方法查看动态库生成文件的大小

通过比较发现静态库要比动态库要小很多,生成的可执行文件大小也存在较小的差别。
三.Linux GCC常用命令
GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理C语言。GCC 很快地扩展,变得可处理 C++。后来又扩展为能够支持更多编程语言,如Fortran、Pascal、Objective-C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection)
gcc命令下各选项的含义
-E:仅作预处理,不进行编译、汇编和链接
-S:仅编译到汇编语言,不进行汇编和链接
-c:编译、汇编到目标代码(也就是计算机可识别的二进制)
-o:执行命令后文件的命名
-g:生成调试信息
-w:不生成任何警告
-Wall:生成所有的警告
gcc编译的四个步骤
预处理:gcc -E Test.c -o Test.i
编译: gcc -S Test.i -o Test.s
汇编: gcc -c Test.s -o Test.o
链接生成可执行文件: gcc Test.o -o Test
gcc常用的编译代码
这里只介绍部分
1、ar
用于创建静态链接库。为了便于初学者理解,在此介绍动态库与静态库 的概念:
(1)如果要将多个.o目标文件生成一个库文件,则存在两种类型的库,一种是静态库,另一种是动态库。
(2)在windows中静态库是以.lib为后缀的文件,共享库是以.dll为后缀的文件。在linux中静态库是以.a为后缀的文件,共享库是以.so为后缀的文件。
(3)静态库和动态库的不同点在于代码被载入的时刻不同。静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。在Linux系统中,可以用ldd命令查看一个可执行程序依赖的共享库。
(4)如果一个系统中存在多个需要同时运行的程序且这些程序之间存在共享库,那么采用动态库的形式将更节省内存。
2、ld
用于链接。
3、as
用于汇编。
4、ldd
可以用于查看一个可执行程序依赖的共享库。
5、size
查看执行文件中各部分的大小。
6、addr2line:用来将程序地址转换成其所对应的程序源文件及所对应的代码行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对应的源代码位置。
四.as汇编编译器
1.as汇编编译器简介
as汇编编译器针对的是AT&T汇编代码风格,Intel风格的汇编代码则可以用nasm汇编编译器编译生成执行程序
2.在ubuntu中下载安装nasm编译器
在控制台输入
$ sudo apt install nasm
(下载之前记得更换下载源,不然速度会很慢)
3.将代码编译生成可执行程序
先用vim创建hello.asm
代码如下:
section .data ; 数据段声明
msg db "Hello, world!", 0xA ; 要输出的字符串
len equ $ - msg ; 字串长度
section .text ; 代码段声明
global _start ; 指定入口函数
_start: ; 在屏幕上显示一个字符串
mov edx, len ; 参数三:字符串长度
mov ecx, msg ; 参数二:要显示的字符串
mov ebx, 1 ; 参数一:文件描述符(stdout)
mov eax, 4 ; 系统调用号(sys_write)
int 0x80 ; 调用内核功能
; 退出程序
mov ebx, 0 ; 参数一:退出代码
mov eax, 1 ; 系统调用号(sys_exit)
int 0x80 ; 调用内核功能
使用如下命令:
生成hello.o文件
$ nasm -f elf64 hello.asm
生成可执行文件hello
$ ld -s -o hello.o
执行./hello输出
图为nasm输出hello world生成程序的大小与gcc编译的大小的对比
对比可知nasm方式编译得到的可执行文件要比用gcc编译得到的文件要小很多。
五.Linux中的第三方库函数
1.Linux 系统中终端程序最常用的光标库—— curses
(1)光标库(curses)的主要函数功能
curses函数库能够优化光标的移动并最小化需要对屏幕进行的刷新,从而也减少了必须向字符终端发送的字符数目。
(2)一些基本函数名称及功能
从屏幕读取:
chtype inch(void); //返回光标位置字符
int instr(char *string); //读取字符到string所指向的字符串中
int innstr(char *string, int numbers);//读取numbers个字符到string所指向的字符串中
清除屏幕:
int erase(void);//在屏幕的每个位置写上空白字符
int clear(void);//使用一个终端命令来清除整个屏幕,相当于vi内的Ctrl+L
//内部调用了clearok来执行清屏操作,(在下次调用refresh时可以重现屏幕原文)
int clrtobot(void);//清除光标位置到屏幕结尾的内容
int clrtoeol(void);//清除光标位置到该行行尾的内容
移动光标:
int move(int new_y, int new_x); //移动stdcsr的光标位置
int leaveok(WINDOW *window_ptr,bool leave_flag);
//设置一个标志,用于控制在屏幕刷新后curses将物理光标放置的位置。
窗口移动和更新屏幕:
int mvwin(WINDOW *win, int new_y, int new_x); //移动窗口
int wrefresh(WINDOW *win);
int wclear(WINDOW *win);
int werase(WINDOW *win);
//类似于上面的refresh, clear, erase,但是此时针对特定窗口操作,而不是stdcur
int touchwin(WINDOW *win); //指定该窗口内容已改变、
//下次wrefresh时,需重绘窗口。利用该函数,安排要显示的窗口
int scrollok(WINDOW *win, bool flag); //指定是否允许窗口卷屏
int scroll(WINDOW *win); //把窗口内容上卷一行
窗口优化屏幕刷新:
int wnoutrefresh(WINDOW *window_ptr);
//The wnoutrefresh subroutine determines which parts of the terminal may need updating.
int doupdate(void);
//The doupdate subroutine sends to the terminal the commands to perform any required changes.
2.时代遗迹——BBS
BBS是一个用键盘光标控制的终端程序,为什么说他是时代遗迹呢,下图可见
百度都已经这样说了
现在国内还有唯一一个有人维护的BBS,let’s check it out
(1)在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net。

在命令行输入telnet bbs.newsmth.net即可体验

3.curses库的安装
在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库
查找curses.h等文件可用whereis + 文件名 的方法查找

4.Linux 环境下C语言编译实现贪吃蛇游戏
代码参考网站:
链接: http://www.linuxidc.com/Linux/2011-08/41375.htm.
用vim建立.c文件snake.c,复制代码到unbuntu
再运行命令:
$ gcc snake.c -lcurses -o snake
$ ./snake
这样就可以成功运行贪吃蛇游戏了!
六.总结
通过本次实验,了解到了c语言不同的编译方式带来的不同的结果,以及一些实用的第三方库,在编写中遇到困难解决问题也提升了自身的能力。
本文详细介绍了如何在Linux中创建和使用静态库与动态库,包括使用gcc编译.o文件,创建静态库(.a)和动态库(.so),以及在程序中链接和调用这些库。实验过程包括从创建源文件到生成可执行程序的完整步骤,并通过实例演示了静态库和动态库的生成与应用,强调了静态库在程序编译时会被连接,而动态库在运行时加载。最后,文章讨论了Linux GCC常用命令、as汇编编译器和第三方库函数,如curses库的应用。

2923

被折叠的 条评论
为什么被折叠?



