函数库是为代码复用建立的,将同一类型,需要在不同的程序里使用的函数放置在一起,就组成了一个函数库。如 C 语言的标准库,它集合了开发者常用的函数。开发者自行编写的函数也可以组成函数库,通常称之为自定义函数库。C 语言的函数库以源代码的形式放在头文件里,较大的函数库可能会用到多个文件,因此涉及到编译的问题。本节将讨论用文件组成函数库的问题。
5.6.1 程序文件的大小
C 语言编译器允许分别编译,为每个文件生成以“.o”为后缀的目标文件,最后执行链接过程生成可执行文件。将一个程序放在一个大文件中,或者分拆成多个较小的文件,所得到的结果其实是相同的。编译器编译一个大文件比编译由这个大文件拆分而成的一系列小文件所花费的时间要长,对成员员来说,小文件更容易阅读和理解,所以应该把大文件拆分为一系列小文件编写。而且如果修改程序时只修改了其中一个文件,那么编译时也只需要重新编译修改过的文件,再执行连接过程生成可执行文件,这样能缩短程序编译的总时间。
5.6.2 分类组织文件
在开发大型程序时,很容易迷失某个具体功能点在源代码中的位置。如果在程序设计的早期预先估计程序的规模,将同一类型的函数放在同一个文件中,以有意义的名字为函数和文件命名,编写详细的注释,则可以最大程度避免该问题。如下例所示的多个文件。
(1)主程序文件 exercise.c:
#include <stdio.h> #include "rate_compute.h" // 包含用户自定义的函数库 rate_compute.h int main() { ... simple_interest(); // 调用函数库 rate_compute.h 中的函数 compound_interest(); // 调用函数库 rate_compute.h 中的函数 ... }
(2)包含所有利率计算函数原型的头文件 rate_compute.h:
#ifndef RATE_COMPUTE_H_ // 判断是否未定义标识符 RATE_COMPUTE_H_ #define RATE_COMPUTE_H_ // 定义标识符 RATE_COMPUTE_H_ ... float simple_interest(float rate, int day); // 定义函数原型 float compound_interest(float rate, int day); ... #endif
(3)包含所有利率计算函数定义的头文件 rate_compute.c:
#include "rate_compute.h" // 包含用户自定义的函数库 rate_compute.h ... float simple_interest(float rate, int day) // 定义函数 { ... // 定义函数体 } float compound_interest(float rate, int day) { ... } ...
这种结构是 C 语言实际开发中经常使用的。主程序文件控制程序主要的流程,而具体功能实现放在其他文件中。与利率计算有关的函数说明放在头文件 rate_compute.h 内,该文件利用编译预处理命令判断自己是否已经被包含,如果没有则进行函数说明。这样可避免因多个文件相互包含时,函数被重复说明而出错。函数定义部分放在头文件配对的 rate_compute.c 文件内,两个文件名前部分相同。rate_compute.c 文件需要使用 #include 语句包含头文件 rate_compute.h,这样,当主程序文件也包含头文件 rate_compute.h,就可以使用这些函数。编译器会处理文件之间的连接问题。
5.6.3 函数库
函数库由一系列函数说明文件和函数定义文件所组成,使用时,将该文件包含在自己的程序文件内。例如需要使用输入输出函数时,可使用标准库内的 stdio.h 文件,需要使用数学函数时,可使用标准库内的 math.h 文件。
Linux 系统上有大量的函数库资源,根据 GPL 协议,这些函数库都能免费使用,并且可按照自己的需求修改。很多Linux 系统上的程序发布时,同时也提供了相应的函数库,以便开发者扩展该程序,或者借助该程序实现程序中某一功能。例如 Pidgin 是 Linux 系统上常用的多协议即时通讯软件,用户可使用 Pidgin 提供的libpurple 开发新的即时通信软件,或者让自己的程序具备即时通信功能。Linux 系统常用的 C 语言函数库如表 9.1 所示。
表 9.1 Linux 系统常用的 C 语言函数库
libdbus | D-Bus 进程间通信机制函数库,用于两个应用程序相互联系和交互消息 |
libpthread | POSIX 线程库,用于多线程通信 |
libthread | Solaris 线程库,用于多线程通信 |
libnet | 网络开发库,用于实现和封装了数据包的构造和发送过程 |
libcurl | 客户端 url 传输库,用于数据下载和上传 |
libpq | PostgreSQL 数据库接口,用于操作 PostgreSQL 数据库 |
GLib | GTK+ 和 GNOME 的基础,提供了具备可移植性的数据结构 |
GTK+ | 图形化用户界面构件库,用于开发图形界面 |
GStreamer | GStreamer 多媒体框架函数库,用于开发多媒体播放程序 |
libgrade | Grade 开发库,用于所见即所得方式的图形界面开发 |
Linux 系统一般将函数放置在 “/lib”和“/usr/lib”两个路径中,GCC 编译器在编译时会自动搜索这两个路径。在位于默认搜索路径之外的函数库,可在环境变量 LD_LIBRARY_PATH 中指明库的搜索路径,或者在“/etc/ld.so.conf”文件中添加库的搜索路径。另外还可以直接将库文件全部复制到程序开发目录中,或者在编译时指定函数库路径。
pkgconfig 程序为配置函数库的路径提供了方便,它用于在编译时指定函数库的路径。
首先需要安装该软件,安装命令如下:
yum install pkgconfig
pkgconfig 可查看已安装的函数库和函数库的版本信息,命令为:
pkg-config --list-all
例如在开发使用 GLib 库的程序时,在编译命令加入参数 “`pkg-config --cflags --libsglib-2.0`”:
gcc -g `pkg-config --cflags --libs glib-2.0` glib_test.c -o glib_test.o
在 Eclipse 中,先用鼠标单击 Project Explorer 中对应的项目名称,选择 Project|Properties 命令打开该项目的属性对话框。然后在对话框左侧的列表框中选择 C/C++Build|Settings 命令打开设置选项卡,在设置选项卡内Tool Settings 列表中选择 GCC C Compiler|Miscellaneous 列表项,将编译参数“`pkg-config --cflags --libs glib-2.0`”加入在 Other flags 文本框原有的内容后。再在同一个列表框中,选择 GCC C Linker|Miscellaneous列表项,将上面的编译参数输入到 Linker flags 文本框原有的内容后。单击 OK 按钮保存。
注意:pkg-config 指令是由一对特殊的符号“`”包围,该符号对应的键盘按键通常位于 Esc键 下方。