库中C是一组功能和声明,由其他程序暴露以供使用。因此,库包含在文件中表示的接口.h
(称为“标题”)和在文件中表示的实现.c
。此.c
文件可能已预编译或无法访问,或者程序员可能可以使用。(注意:库可以调用其他库中的函数,例如标准C或数学库来执行各种任务。)
库的格式因操作系统和正在使用的编译器而异。例如,在Unix和Linux操作系统中,库由一个或多个目标文件组成,这些目标文件由目标代码组成,目标代码通常是编译器的输出(如果源语言是C或类似的东西)或汇编程序(如果源语言是汇编语言)。然后,这些目标文件由ar archiver 以存档的形式转换为一个库(一个程序,它接收文件并将它们存储在一个更大的文件中而不考虑压缩)。库的文件名通常以“lib”开头,以“.a”结尾; 例如libc.afile包含标准C库和“libm.a”数学例程,链接器将链接到该数学例程。其他操作系统(如Microsoft Windows)使用库的“.lib”扩展名和对象的“.obj”扩展名文件。Unix环境中的某些程序(如lex和yacc)生成的C代码可以与libl和liby库链接以创建可执行文件。
我们将使用一个包含一个函数的库作为示例:一个从命令行解析参数的函数。命令行上的参数可以是它们自己:
-一世
有一个可选的参数连接到字母:
-ioptarg
或者将参数放在一个单独的argv-element中:
-i optarg
除了函数之外,该库还有四个声明导出:三个整数和一个指向可选参数的指针。如果参数没有可选参数,则指向可选参数的指针将为null。
为了解析所有这些类型的参数,我们编写了以下“getopt.c”文件:
#include <stdio.h> / *表示fprintf()和EOF * / #include <string.h> / * for strchr()* / #include “getopt.h”/ *一致性检查* / / *变量* / int opterr = 1 ; / * getopt打印错误,如果这是* / int optind = 1 ; / *令牌指针* / int optopt ; / *选项字符传回用户* / char * optarg ; / * flag参数(或值)* / / * function * / / *返回选项字符,EOF如果没有或者?如果有问题。 函数的参数: argc,argv - main()函数的参数。“ - ”的参数将 停止处理。 opts - 包含有效选项字符的字符串。 选项字符后跟冒号(:)表示 该选项具有必需参数。 * / int getopt (int argc , char ** argv , char * opts ) { static int sp = 1 ; / *字符索引到当前令牌* / 注册 char * cp ; / *指向当前令牌的指针* / if (sp == 1 ) { / *检查更多类似标志的标记* / if (optind > = argc || argv [ optind ] [ 0 ] != ' - ' || argv [ optind ] [ 1 ] == '\ 0' ) 返回 EOF ; else if (strcmp (argv [ optind ], “ - ” ) == 0 ) { optind ++ ; 返回 EOF ; } } optopt = argv [ optind ] [ sp ]; if (optopt == ':' || (cp = strchr (opts , optopt )) == NULL ) { if (opterr ) fprintf (stderr , “%s:invalid option - '%c' \ n ” , argv [ 0 ], optopt ); / *如果此标记中没有剩余字符,则移至下一个标记* / if (argv [ optind ] [ ++ sp ] == '\ 0' ) { optind ++ ; sp = 1 ; } 回来 '?' ; } if (* ++ cp == ':' ) { / *如果需要一个值,得到它* / if (argv [ optind ] [ sp + 1 ] != '\ 0' ) / *标志值是其余的当前令牌* / optarg = argv [ optind ++ ] + (sp + 1 ); else if (++ optind > = argc ) { if (opterr ) fprintf (stderr , “%s:选项需要参数 - '%c' \ n ” , argv [ 0 ], optopt ); sp = 1 ; 回来 '?' ; } else / *标志值是下一个标记* / optarg = argv [ optind ++ ]; sp = 1 ; } else { / *设置为查看令牌中的下一个char,下次* / if (argv [ optind ] [ ++ sp ] == '\ 0') { / *当前令牌中没有更多,所以设置下一个令牌* / sp = 1 ; optind ++ ; } optarg = 0 ; } 返回 optopt ; } / *文件结尾* /
接口将是以下“getopt.h”文件:
#ifndef GETOPT_H #define GETOPT_H / * export variables * / extern int opterr , optind , optopt ; extern char * optarg ; / * export function * / int getopt (int , char ** , char * ); #万一 / *文件结尾* /
程序员至少要有一个接口文件来弄清楚如何使用库,尽管一般来说,库程序员还编写了有关如何使用库的文档。在上面的例子中,文档应该说提供的参数**argv
和*opts
两者都不应该是空指针(或者为什么你还要使用该getopt
函数?)。具体而言,它通常说明每个参数的用途以及在哪些条件下可以预期的返回值。使用库的程序员通常对库的实现不感兴趣 – 除非实现有错误,在这种情况下他会想要以某种方式抱怨。
getopts库的实现和使用该库的程序都应该声明#include "getopt.h"
,以便引用相应的接口。现在,库与程序“链接” – 包含main()函数的程序。该程序可能涉及许多接口。
在某些情况下,只是放置#include "getopt.h"
可能看起来正确,但仍然无法正确链接。这表示库未正确安装,或者可能需要一些其他配置。您必须检查编译器的文档或库的文档,以了解如何解决此问题。
放在头文件中的内容
作为一般规则,标头应包含任何声明和宏定义(预处理器#define
),以便程序中的其他模块“看到”。
可能的声明:
- struct,union和enum声明
- typedef声明
- 外部函数声明
- 全局变量声明
在上面的getopt.h
示例文件,一个函数(getopt
)声明及四个全局变量(optind
,optopt
,optarg
和opterr
)也宣布。变量是使用extern
头文件中的存储类说明符声明的,因为该关键字指定“实际”变量存储在别处(即getopt.c
文件)而不是头文件中。
该#ifndef GETOPT_H/#define GETOPT_H
诀窍是俗称包括警卫。这样使用,如果getopt.h
文件在翻译单元中被多次包含,则该单元只能看到一次内容。或者,#pragma once
在头文件中也可以用来实现同样的事情。
猜你想读:《C编程.高级C》1.常见做法