C 程式設計/庫
在 C 語言中,一個庫是多個頭檔案的集合,這些標頭檔案被暴露出來供其他程式使用。因此,庫由一個用.h檔案(稱為“標頭檔案”)表示的介面和一個用.c檔案表示的實現組成。這個.c檔案可能是預編譯的或不可訪問的,也可能是可供程式設計師訪問的。(注意:庫可能呼叫其他庫(如標準 C 庫或數學庫)中的函式來完成各種任務。)
庫的格式會因使用的作業系統和編譯器而異。例如,在 Unix 和 Linux 作業系統中,庫由一個或多個目標檔案組成,這些目標檔案包含通常是編譯器(如果源語言是 C 或類似語言)或彙編器(如果源語言是組合語言)的輸出的目的碼。然後,這些目標檔案透過ar 歸檔器(一個將檔案儲存到更大的檔案中的程式,不考慮壓縮)被轉換成一個庫形式的歸檔檔案。庫的檔名通常以“lib”開頭,以“.a”結尾;例如,libc.a 檔案包含標準 C 庫,而“libm.a”檔案包含數學例程,連結器會將它們連結進來。其他作業系統,如 Microsoft Windows,使用“.lib”擴充套件名錶示庫,使用“.obj”擴充套件名錶示目標檔案。Unix 環境中的某些程式,如 lex 和 yacc,會生成 C 程式碼,這些程式碼可以與 libl 和 liby 庫連結以建立可執行檔案。
我們以一個包含一個函式的庫為例:一個用於從命令列解析引數的函式。命令列上的引數可以是獨立的
-i
具有可選引數,該引數與字母連線起來
-ioptarg
或者在單獨的 argv 元素中具有引數
-i optarg
該庫除了函式之外,還匯出四個宣告:三個整數和一個指向可選引數的指標。如果引數沒有可選引數,則指向可選引數的指標將為 null。
為了解析所有這些型別的引數,我們編寫了以下“getopt.c”檔案
#include <stdio.h> /* for fprintf() and EOF */
#include <string.h> /* for strchr() */
#include "getopt.h" /* consistency check */
/* variables */
int opterr = 1; /* getopt prints errors if this is on */
int optind = 1; /* token pointer */
int optopt; /* option character passed back to user */
char *optarg; /* flag argument (or value) */
/* function */
/* return option character, EOF if no more or ? if problem.
The arguments to the function:
argc, argv - the arguments to the main() function. An argument of "--"
stops the processing.
opts - a string containing the valid option characters.
an option character followed by a colon (:) indicates that
the option has a required argument.
*/
int
getopt (int argc, char **argv, char *opts)
{
static int sp = 1; /* character index into current token */
register char *cp; /* pointer into current token */
if (sp == 1)
{
/* check for more flag-like tokens */
if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
return EOF;
else if (strcmp (argv[optind], "--") == 0)
{
optind++;
return 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 no characters left in this token, move to next token */
if (argv[optind][++sp] == '\0')
{
optind++;
sp = 1;
}
return '?';
}
if (*++cp == ':')
{
/* if a value is expected, get it */
if (argv[optind][sp + 1] != '\0')
/* flag value is rest of current token */
optarg = argv[optind++] + (sp + 1);
else if (++optind >= argc)
{
if (opterr)
fprintf (stderr, "%s: option requires an argument -- '%c'\n",
argv[0], optopt);
sp = 1;
return '?';
}
else
/* flag value is next token */
optarg = argv[optind++];
sp = 1;
}
else
{
/* set up to look at next char in token, next time */
if (argv[optind][++sp] == '\0')
{
/* no more in current token, so setup next token */
sp = 1;
optind++;
}
optarg = 0;
}
return optopt;
}
/* END OF FILE */
介面將是以下“getopt.h”檔案
#ifndef GETOPT_H
#define GETOPT_H
/* exported variables */
extern int opterr, optind, optopt;
extern char *optarg;
/* exported function */
int getopt(int, char **, char *);
#endif
/* END OF FILE */
在最少的情況下,程式設計師擁有介面檔案以瞭解如何使用庫,儘管一般來說,庫程式設計師也編寫了有關如何使用庫的文件。在上述情況下,文件應該說明提供的引數**argv 和*opts 都不能是 null 指標(否則你為什麼要使用getopt 函式呢?)。具體而言,它通常說明每個引數的用途以及在哪些條件下可以預期哪些返回值。使用庫的程式設計師通常不關心庫的實現,除非實現存在錯誤,在這種情況下,他們會希望以某種方式提出投訴。
getopts 庫的實現和使用庫的程式都應該宣告#include "getopt.h",以便引用相應的介面。現在庫與包含 main() 函式的程式“連結”在一起。程式可能引用數十個介面。
在某些情況下,僅放置#include "getopt.h" 似乎是正確的,但仍然無法正確連結。這表明庫沒有正確安裝,或者可能需要一些額外的配置。您將需要檢查編譯器文件或庫文件以瞭解如何解決此問題。
標頭檔案中放什麼
[edit | edit source]一般來說,標頭檔案應該包含程式中其他模組“可見”的任何宣告和宏定義(預處理器#define)。
可能的宣告
- 結構體、聯合體和列舉宣告
- typedef 宣告
- 外部函式宣告
- 全域性變數宣告
在上面的getopt.h 示例檔案中,聲明瞭一個函式(getopt),還聲明瞭四個全域性變數(optind、optopt、optarg 和opterr)。這些變數在標頭檔案中使用儲存類說明符extern 宣告,因為該關鍵字指定“真實”變數儲存在其他位置(即getopt.c 檔案)而不是標頭檔案內。
#ifndef GETOPT_H/#define GETOPT_H 技巧俗稱包含保護。這樣做是為了防止getopt.h 檔案在翻譯單元中被包含多次時,該單元只看到一次內容。或者,在標頭檔案中使用#pragma once 也可以在某些編譯器中實現相同的效果(#pragma 是一個不可移植的萬能符)。
將庫連結到可執行檔案
[edit | edit source]將庫連結到可執行檔案的方式因作業系統和使用的編譯器/連結器而異。在 Unix 中,可以使用-L選項將連結的目標檔案目錄指定給 cc 命令,使用-l(小寫 l)選項指定單個庫。例如,-lm選項指定應將 libm 數學庫連結進來。