跳轉到內容

C 程式設計/庫

來自華夏公益教科書
前一頁: 更多數學 C 程式設計 下一頁: UNIX 中的網路程式設計

在 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),還聲明瞭四個全域性變數(optindoptoptoptargopterr)。這些變數在標頭檔案中使用儲存類說明符extern 宣告,因為該關鍵字指定“真實”變數儲存在其他位置(即getopt.c 檔案)而不是標頭檔案內。

#ifndef GETOPT_H/#define GETOPT_H 技巧俗稱包含保護。這樣做是為了防止getopt.h 檔案在翻譯單元中被包含多次時,該單元只看到一次內容。或者,在標頭檔案中使用#pragma once 也可以在某些編譯器中實現相同的效果(#pragma 是一個不可移植的萬能符)。

將庫連結到可執行檔案

[edit | edit source]

將庫連結到可執行檔案的方式因作業系統和使用的編譯器/連結器而異。在 Unix 中,可以使用-L選項將連結的目標檔案目錄指定給 cc 命令,使用-l(小寫 l)選項指定單個庫。例如,-lm選項指定應將 libm 數學庫連結進來。

參考文獻

[edit | edit source]
前一頁: 更多數學 C 程式設計 下一頁: UNIX 中的網路程式設計
華夏公益教科書