C 程式設計/stdlib.h/函式參考
void abort(void);
此函式用於異常終止程式或程序。當程式執行中遇到錯誤條件時,程式需要退出,這時可以使用此函式。
它會在結束程式之前刪除緩衝區並關閉所有開啟的檔案。abort() 函式會終止當前程式。根據實現的不同,函式的返回值可能指示已取消(例如,使用 signal() 函式捕獲 SIGABRT)或終止失敗。程序在呼叫 cstdlib 中定義的 abort libc 函式時會向自身傳送 SIGABRT 訊號。可以捕獲 SIGABRT 訊號,但不能阻塞它;如果訊號處理程式返回,則所有開啟的流都會被關閉並重新整理,程式會終止(如果合適的話會轉儲核心),然後它會將控制權返回給主機環境。這意味著 abort 呼叫永遠不會返回。由於這種特性,它通常用於在支援庫中發出致命條件訊號,在這種情況下,當前操作無法完成,但主程式可以在退出之前執行清理工作。如果斷言失敗,它也會被使用。
它是標準 c 庫中的一個執行緒安全函式。也就是說,該函式可以由不同的執行緒呼叫,不會出現任何問題。
int abs (int i);
long labs (long i);
double fabs (double i);
float fabsf (float i);
long double fabsl (long double i);
許多程式語言都有函式可以計算數字的絕對值,要麼叫 abs 要麼叫 Abs。在 C 語言這樣的語言中,它有針對長整型和浮點型的變體,分別稱為 labs 和 fabs。所有函式都以一個有符號數作為引數,並以相同的資料型別返回該數字的絕對值。
int atexit(void (*function)(void));
此函式註冊給定的 函式,以便在正常程序終止時執行,無論是透過 exit 還是透過從程式的主函式返回。
atexit 函式以要註冊的回撥函式的引用作為引數。如此註冊的函式將按照其註冊順序的反序呼叫;不會傳遞任何引數。
atexit 函式由 POSIX 規範標準化。
如果該函式成功完成執行,它將返回零 (0)。非零返回值表示錯誤。
POSIX 要求 atexit 的實現允許註冊至少 ATEXIT_MAX (32) 個這樣的函式。
ISO/IEC 9899:1999 規範 (PDF). p. 315, § 7.20.4.2.
double atof (const char *str);
此函式將字串轉換為浮點數值表示形式。atof 代表 ASCII 到浮點。它包含在 C 標準庫標頭檔案 stdlib.h 中。它的原型如下
str 引數指向一個字串,該字串由一個字元陣列表示,包含浮點值的 字元 表示形式。如果字串不是 double 的有效文字表示形式,atof 會靜默失敗,在這種情況下會返回零 (0.0)。[1]
請注意,雖然 atoi 和 atol 返回與其名稱相對應的變數型別(“atoi” 返回整數,“atol” 返回長整數),但 atof 不會返回 float,它返回 double。
一個相關的函式是 sscanf。此函式從字串中提取值,其返回值是它成功提取的有效值的個數(因此,與 atof 不同,sscanf 可以用來測試字串是否以有效數字開頭)。
int atoi(const char *str);
此函式將字串轉換為整數數值表示形式。atoi 代表 ASCII 到整數。它包含在 C 標準庫標頭檔案 stdlib.h 中。它的原型如下
str 引數是一個字串,由一個字元陣列表示,包含一個有符號整數的字元。該字串必須以 null 結尾。當 atoi 遇到沒有數值序列的字串時,它會返回零 (0)。
atoi 函式有幾個變體,atol、atof 和 atoll,它們分別用於將字串轉換為 long、double 或 long long 型別。atoll 以前稱為 atoq,並已包含在 C99 中。
無法判斷字串是否包含表示數字 0 的有效數字序列或無效數字,因為該函式在這兩種情況下都會返回 0。較新的函式 strtol 沒有此缺陷。
atoi 在某些作業系統上既不是執行緒安全的,也不是非同步取消安全的。[2]
此外,atoi 只轉換十進位制 ascii 值(從不同的角度來看,這可能也是一個優點)。strtol 和其他函式支援十六進位制和八進位制等其他進位制。
然而,由於返回 0 的模糊性和在某些作業系統上缺乏執行緒安全和非同步取消安全,atoi 被認為已被 strtol 棄用。[2]
The Version 7 Unix Manual Pages © 1979 by Bell Telephone Laboratories, Incorporated.
The Version 1 Unix Manual page for atoi written by Ken Thompson (November 1971).
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
int (*compare)(const void *, const void *));
該函式用於使用 二分查詢演算法 在排序陣列中搜索物件。[3]
bsearch 是一個通用函式,它可以在包含任何型別物件或指向物件的指標的任何大小的排序陣列中進行搜尋,並使用任何型別的比較謂詞。然而,通用性是以型別安全為代價的,因為 bsearch 在 void 指標上操作;並且在函式呼叫次數上也很昂貴(因為每次比較都需要呼叫比較謂詞),這會產生很大的開銷。
bsearch() 函式返回指向陣列中匹配成員的指標,如果未找到匹配項,則返回 NULL。如果陣列具有多個匹配元素,則返回值將是指向其中一個元素的指標。哪個特定元素是未指定的。[4]
div 是 C 程式語言中的一個函式,它接受兩個整數作為引數並返回它們之間除法的結果。它在 ANSI-C 中指定,在使用時從 stdlib.h 標頭檔案中包含。
div 總是向 0 舍入,這與 C 中的普通整數除法不同,在 C 中,負數的舍入是實現相關的。
div 的原型如下
div_t div (int numerator, int denominator)
返回值 div_t 是一種特殊的資料型別,專門用於儲存該函式的結果。它的定義如下
typedef struct {
int quot;
int rem;
} div_t;
其中 quot 儲存商,rem 儲存餘數。
ldiv 和 lldiv 是類似的函式,它們分別對 long 和 long long 型別的整數進行除法;並分別返回型別為 ldiv_t 和 lldiv_t 的結構。
ldiv_t ldiv (long numerator, long denominator );
lldiv_t lldiv (long long numerator, long long denominator);
- stdlib.h
- stdio.h
在許多計算機作業系統上,計算機程序透過執行 exit 系統呼叫來終止其執行。更一般地說,在多執行緒環境中退出意味著執行執行緒已停止執行。作業系統回收程序使用的資源(記憶體、檔案等)。程序在終止後被稱為死程序。
在 Unix 和類 Unix 作業系統下,當父程序執行fork 系統呼叫時,程序啟動。父程序隨後可能等待子程序終止,或者可能繼續執行(可能分叉出其他子程序)。當子程序終止(“死亡”)時,無論是透過呼叫exit正常終止,還是由於致命錯誤或訊號(例如,SIGTERM、SIGINT、SIGKILL)異常終止,退出狀態都會返回給作業系統,並且會向父程序傳送 SIGCHLD 訊號。然後,父程序可以透過wait 系統呼叫檢索退出狀態。
大多數作業系統允許終止程序向系統提供特定的退出狀態,該狀態會提供給父程序。通常,這是一個小的整數值,儘管某些作業系統(例如,Plan 9)允許指定一個字元字串。
退出操作通常在將控制權返回給作業系統之前在程序空間內執行清理操作。一些系統和程式語言允許註冊使用者子例程,以便在程式真正終止之前呼叫它們。作為終止的最後一步,會呼叫一個原始系統退出呼叫,通知作業系統程序已終止,並允許其回收程序使用的資源。
有時可以繞過通常的清理;C99 提供了_exit() 函式,它終止當前程序,沒有任何額外的程式清理。例如,這可以在 fork-exec 例程中使用,當exec 呼叫無法替換子程序時;呼叫atexit 例程會錯誤地釋放屬於父程序的資源。
一些作業系統以特殊方式處理父程序已終止的子程序。這種孤兒程序成為特殊根程序的子程序,然後等待子程序終止。同樣,類似的策略用於處理殭屍程序,即已終止但其退出狀態被其父程序忽略的子程序。這種程序成為特殊父程序的子程序,該程序檢索子程序的退出狀態,並允許作業系統完成死程序的終止。處理這些特殊情況會使系統程序表保持一致狀態。
在計算中,malloc 是執行動態記憶體分配的子例程。malloc 是標準庫的一部分,在stdlib.h 標頭檔案中宣告。
malloc 的許多實現可用,每個實現根據計算硬體和程式的編寫方式而表現不同。效能在執行時間和所需記憶體方面各不相同。使用 malloc 分配的記憶體的指標最終必須傳遞給free 子例程以釋放記憶體,以避免記憶體洩漏。
malloc 函式是標準 C 中用於分配記憶體的函式之一。它的函式原型是
void *malloc(size_t size);
它分配size 位元組的記憶體。如果分配成功,則返回指向記憶體塊的指標。該指標保證適當地對齊到任何型別(包括結構體等),否則返回 NULL 指標。
透過malloc 分配的記憶體是持久存在的:它將繼續存在,即使程式離開呼叫分配的範圍,直到程式終止或程式設計師顯式地透過free 子例程釋放記憶體。這是透過使用free 子例程實現的。它的原型是
void free(void *pointer);
它釋放由pointer指向的記憶體塊。pointer必須是之前由malloc,calloc或realloc返回的,並且在將其傳遞給free之後,pointer不能再使用。特別是,透過new或new[]分配的記憶體不應該傳遞給free,並且沒有來自malloc(例如堆疊變數)或已經被釋放的指標不能傳送到free。呼叫NULL指標上的free是安全的,它沒有任何效果。
使用示例
[edit | edit source]建立10個int物件的陣列的標準方法
int array[10];
但是,如果希望動態地分配類似的陣列,可以使用以下程式碼
/* Allocate space for an array with ten elements of type
int. */
int *ptr = (int *) malloc(10 * sizeof (int));
if (ptr == NULL) {
/* Memory could not be allocated, the program should
handle the error here as appropriate. */
} else {
/* Allocation succeeded. Do something. */
free(ptr); /* We are done with the int objects, and
free the associated pointer. */
ptr = NULL; /* The pointed-to-data must not be used again,
unless re-assigned by using malloc
again. */
}
malloc返回一個空指標以指示沒有可用記憶體,或發生了其他錯誤,從而阻止了記憶體分配。
由於malloc呼叫可能由於記憶體不足而失敗,因此定義一個在malloc失敗時呼叫malloc並退出的宏通常很方便。一個可能的宏定義是
if(!(ptr=malloc(sizeof(int))))
{
fprintf(stderr,"Insufficient memory"); /* Prints the necessary error statement. */
exit(EXIT_FAILURE); /* Exits <code>malloc</code> as there is insufficient memory. */
}
此宏將是
#define MALLOC(p,s) /*Here p & s are 2 integer pointer & variable respectively. */ \
if(!((p)=malloc(s))) \
{ \
fprintf(stderr,"Insufficient memory"); /* Prints the necessary error statement. */ \
exit(EXIT_FAILURE); /* Exits <code>malloc</code> as there is insufficient memory. */ \
}
因此,上述基本程式碼可以用一行程式碼代替
MALLOC(ptr,sizeof(int));
此示例中顯示了使用malloc的有用習慣用法
int *ptr = malloc(10 * sizeof(*ptr));
也就是說,不要將硬編碼型別寫入malloc的引數,而是使用要分配的指標的內容對sizeof運算子。這確保了分配左側和右側的型別在程式碼修改時永遠不會不同步。
用於建立和返回大小為m*n的二維陣列的C函式
int **create(int m, n)
{
int **p, i;
p = (int **)malloc(m*sizeof(int*));/* this will store base order of all the row in p */
for(i = 0; i < m; i++)
p[i] = (int *)malloc(n*sizeof(int));/* this will create m row of n elements */
return p;
}
強制轉換和型別安全
[edit | edit source]malloc返回一個void指標(void *),它表示它是一個指向未知資料型別區域的指標。malloc返回的特定指標型別缺乏是一種型別不安全行為:malloc根據位元組數進行分配,而不是根據型別進行分配。
可以將此指標“強制轉換”(參見型別轉換)為特定型別
int *ptr;
ptr = malloc(10 * sizeof (*ptr)); // Without a cast
ptr = (int*)malloc(10 * sizeof (int)); // With a cast
執行此類強制轉換有優點和缺點。
強制轉換的優點
[edit | edit source]- 如果存在強制轉換,並且隨後更改了左側指標的型別,則將生成一個警告以幫助程式設計師糾正否則可能成為錯誤的行為。
- 強制轉換允許使用最初返回
char *的舊版malloc。[6]
強制轉換的缺點
[edit | edit source]- 根據ANSI C標準,強制轉換是多餘的。
- 新增強制轉換可能會掩蓋未包含標頭檔案
stdlib.h的錯誤,其中找到了malloc的原型。[6][7]在沒有malloc的原型的情況下,標準要求C編譯器假設malloc返回一個int。如果沒有強制轉換,在將此整數分配給指標時會發出警告;但是,使用強制轉換,則不會生成此警告,從而隱藏了一個錯誤。在某些體系結構和資料模型(例如64位系統上的LP64,其中long和指標為64位,而int為32位)中,此錯誤實際上會導致未定義的行為,因為隱式宣告的malloc返回一個32位值,而實際定義的函式返回一個64位值。根據呼叫約定和記憶體佈局,這可能會導致堆疊溢位。此問題在現代編譯器中不存在,因為它們統一生成使用未宣告函式的警告,因此仍然會出現警告。例如,gcc的預設行為是顯示一條警告,提示“內建函式的不相容隱式宣告”,無論是否存在強制轉換。
相關函式
[edit | edit source]calloc
[edit | edit source]malloc返回一個為程式設計師分配的記憶體塊,用於程式設計師使用,但未初始化。如果需要,記憶體通常會手動初始化,方法是透過memset函式,或者透過一個或多個對指標進行解引用賦值語句。另一種方法是使用calloc函式,它分配連續記憶體,然後將其初始化為零。它的原型是
void *calloc(size_t nelements, size_t elementSize);
realloc
[edit | edit source]能夠縮小或擴大記憶體塊通常很有用。這可以使用realloc來完成,它返回一個指向指定大小的記憶體區域的指標,該區域包含與pointer指向的舊區域相同的資料(截斷為舊大小和新大小的最小值)。如果realloc能夠就地調整記憶體區域的大小,它將分配新的儲存空間,複製所需的資料,並釋放舊指標。如果此分配失敗,realloc將保持原始指標不變,並返回空指標值。在擴充套件的情況下,複製到舊資料的外部新記憶體區域未初始化(內容不可預測)。函式原型是
void *realloc(void *pointer, size_t size);
常見錯誤
[edit | edit source]不正確使用malloc和相關函式通常會導致錯誤。
分配失敗
[edit | edit source]不能保證malloc會成功,如果記憶體不可用,或者程式已超過其允許引用的記憶體量,malloc將返回一個空指標,這應該在分配後始終檢查。許多編寫得很差的程式沒有檢查malloc是否失敗。這樣的程式將嘗試像使用指向已分配記憶體一樣使用malloc返回的空指標。該程式很可能崩潰;在某些環境中,特別是執行虛擬記憶體管理的較舊或較小的平臺,零是一個有效地址,因此問題將不會被發現。
記憶體洩漏
[edit | edit source]當malloc,calloc或realloc呼叫成功時,返回的指向已分配記憶體的指標最終應該傳遞給free函式。這將釋放已分配的記憶體,使其能夠重複使用以滿足其他記憶體分配請求。如果不這樣做,已分配的記憶體將不會在程序退出之前(在某些環境中,甚至在程序退出之後)釋放,換句話說,記憶體洩漏將發生。通常,記憶體洩漏是由於丟失指標造成的,例如沒有使用臨時指標來表示realloc的返回值,這可能會導致原始指標被空指標覆蓋。
釋放後使用
[edit | edit source]在將指標傳遞給free之後,它將變為懸空指標:它引用一個具有未定義內容的記憶體區域,該區域可能不可用。指標的值不可訪問。例如
int *ptr = malloc(sizeof (int));
free(ptr);
*ptr = 7; /* Undefined behavior */
類似的程式碼具有未定義的行為:其效果可能會有所不同。實際上,即使嘗試讀取已釋放指標的值也會導致未定義的行為(此處)。
釋放未分配的記憶體
[edit | edit source]另一個問題是,當free傳遞一個未由malloc,realloc或calloc分配的地址時。當將指向字面字串的指標或已宣告陣列的名稱傳遞給free時,可能會發生這種情況,例如
char *msg = "Default message";
int tbl[100];
將上述任一指標傳遞給free會導致未定義的行為。
分配大小限制
[edit | edit source]malloc 能分配的最大記憶體塊取決於主機系統,尤其是物理記憶體的大小和作業系統的實現。理論上,最大值應該是 size_t 型別可以容納的最大值,它是一個與實現相關的無符號整數,表示記憶體區域的大小。最大值為 2CHAR_BIT*sizeof(size_t) − 1,或 C99 標準中的常量 SIZE_MAX。
外部連結
[edit | edit source]- IEEE Std 1003.1 標準中 malloc 的定義
- glibc 分配器基礎的設計,作者:Doug Lea
- ptmalloc 主頁,作者:Wolfram Gloger
- Hoard 主頁,作者:Emery Berger
- nedmalloc 主頁,作者:Niall Douglas
- jemalloc 主頁,作者:Jason Evans
- TCMalloc 主頁,由 Google 開發的高效能 malloc
- 簡單的記憶體分配演算法,來自 OSDEV 社群
- Hoard:用於多執行緒應用程式的可擴充套件記憶體分配器,作者:Emery Berger
- 可擴充套件的無鎖動態記憶體分配,作者:Maged M. Michael
- 記憶體管理內部 - 動態分配的選擇、權衡和實現,作者:Jonathan Bartlett
- 記憶體減少(GNOME) wiki 頁面,包含大量關於修復 malloc 的資訊
- C99 標準草案,包括 TC1/TC2/TC3
- 一些關於 C 的有用參考
- ISO/IEC 9899 - 程式語言 - C
qsort
[edit | edit source]qsort 是一個用於對陣列中的元素進行排序的函式。它以快速排序演算法命名,儘管 C 標準不要求它使用任何特定的演算法來實現。[8]
qsort 是一個通用函式,它可以對任何大小的陣列進行排序,這些陣列包含任何型別的物件(儘管,如果物件大小不同,則必須使用指標),並使用任何型別的比較謂詞。但是,通用性是以犧牲型別安全性為代價的,因為 qsort 對 void 指標進行操作。
原型
[edit | edit source]void qsort(void *base, size_t nmemb, size_t size, int (*compare)(const void *, const void *));
行為
[edit | edit source]陣列的內容根據比較函式(由 compare 指向)進行排序。當專案比較相等時,它們在結果陣列中的順序是不確定的,這意味著 qsort 不必是穩定的排序。
strtod
[edit | edit source]strtod(string to double)將字元字串轉換為雙精度浮點數。[9] 它被定義為
double strtod ( const char * str, char ** endptr );
概述
[edit | edit source]strtod 函式解析 C 字串 str,將其內容解釋為浮點數,並將其值作為雙精度數返回。如果 endptr 不是空指標,函式還會將 endptr 指向的值設定為指向數字後面的第一個字元。
該函式首先丟棄儘可能多的空格字元,直到找到第一個非空格字元。然後,從這個字元開始,獲取儘可能多的字元,這些字元有效地遵循類似於浮點文字的語法,並將它們解釋為數值。指向數字後面最後一個有效字元之後的字串的指標儲存在 endptr 指向的物件中。
strtod 的有效浮點數由以下內容組成:
- 可選的加號或減號
- 數字序列,可選地包含小數點字元
- 可選的指數部分,它本身由一個 'e' 或 'E' 字元後跟一個可選符號和一個數字序列組成。
如果 str 中第一個非空格字元序列不構成上面定義的有效浮點數,或者如果由於 str 為空或僅包含空格字元而不存在這樣的序列,則不會執行轉換。
引數
[edit | edit source]str
- 以浮點數表示形式開頭的 C 字串。
endptr
- 對已分配的 char* 型別物件的引用,該函式將該值設定為指向 str 中數值後面的下一個字元。此引數也可以是空指標,在這種情況下它不會被使用。
返回值
[edit | edit source]如果成功,該函式將返回轉換後的浮點數作為雙精度值。如果無法執行有效的轉換,則返回零值 (0.0)。如果正確的值超出了可表示值的範圍,則返回正值或負值 HUGE_VAL,並將全域性變數 errno 設定為 ERANGE。如果正確的值會導致下溢,則返回零,並將 errno 設定為 ERANGE。
strtod 函式包含在 stdlib.h 中。
strtol
[edit | edit source]strtol 是 C 程式語言中一個將字串轉換為長整數的函式。strtol 代表string to long。它包含在 C 標準庫標頭檔案 stdlib.h 中。它的原型如下所示
long strtol(const char *restrict str, char **restrict end, int base);
str 引數指向一個字串,該字串由一個字元陣列表示,其中包含有符號整數值的字元表示形式。該字串必須以 null 結尾。base 引數指定要使用的數字基數,從 2 到 36。如果數字基數大於 10,則使用字母字元('A' 到 'Z')作為表示形式中的數字。轉換完成後,end 指向的值將設定為指向字串中最後一個有效數字字元後面的字元,並將返回轉換後的整數值。如果字串不包含有效的數字序列,則返回零 (0),並將全域性變數 errno 設定為 EINVAL。
這些函式還有類似的變體,分別名為 strtoul、strtoll 和 strtoull,它們分別解析並返回型別為 unsigned long、long long 和 unsigned long long 的整數。
標準一致性
[edit | edit source]strtol 函式是 ISO 標準 C 庫(C89,1989)的一部分,strtoll 函式作為 C99 庫(1999)的一部分新增。它作為對現有 atoi 函式的更良好行為的替換而新增到標準 C 庫中。
參考資料
[edit | edit source]- strtol at OpenGroup.org
- 正確且可移植地使用 strtol
在 C 標準庫中,system 是一個用於執行子程序和命令的函式。 它在 stdlib.h 標頭檔案中定義。 它不同於 exec/spawn 函式族,因為 system 不向執行物件傳遞引數,而是向系統 shell(通常是 POSIX shell,/bin/sh -c)傳遞一個字串。
int system (const char *command);
system 函式是阻塞的;也就是說,呼叫將等待直到子程序終止並返回它的退出值。 在這段時間內,SIGCHLD 將被阻塞,因為 system 等待子程序死亡;此外,SIGINT 和 SIGQUIT 被忽略,所以為了確保響應性,程式設計師應該檢查返回值以檢視使用者是否試圖終止程序。 發生錯誤時,system 在 fork 之前或在 fork 時失敗將返回 -1(例如,程序數量限制已達到),但在 fork 之後失敗(例如,無法執行 sh)將返回 127;這與命令以狀態 127 退出無法區分。
在 POSIX 下,system 會派生並執行 /bin/sh,帶有兩個引數:"-c" 和 command。 雖然 sh 的行為在其他地方有說明,但值得注意的是,command 不必是一個單一的命令;它實際上可以是一個管道,甚至是一系列管道。 例如,考慮一個想要顯示螢幕截圖的程式
system ("pngtopnm \"My Screenshot.png\" | pnmtoxwd > out.xwd && xwud out.xwd");
這一行展示了一個重要的考慮因素:由於 command 將被解析為一個 shell 命令列,因此必須對例如檔名周圍的引號進行轉義。 但是,這會引發安全問題,因為如果 command 是從使用者提供的資料構建的,攻擊者可能會繞過任何引號並以父程序的上下文中執行任意命令;事實上,這幾乎是規範的程式碼注入漏洞。 因此,只有在預定的命令字串上使用 system 被認為是明智的,使用其他函式(spawn 等)透過 argv 傳遞使用者提供的資料,或者透過管道或臨時檔案傳遞此類資料。
由 system 派生的子程序會繼承父程序的標準流;因此子程序可以接收鍵盤輸入並寫入終端。 請注意,這意味著父程序不會接收子程序的輸出,除非使用重定向或 tee。
- ↑ ISO/IEC 9899:1999 規範, 第 307 頁,§ 7.20.1.1
- ↑ a b http://www.codecogs.com/reference/c/stdlib.h/atoi.php
- ↑ ISO/IEC 9899:1999 規範 (PDF). 第 318 頁,§ 7.20.5.1.
- ↑ UNIX 手冊頁:
man 3 bsearch - ↑ http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf 第 7.20.6.2 章
- ↑ a b FAQ > Explanations of... > Casting malloc 在 Cprogramming.com 上於 2007 年 3 月 9 日訪問
- ↑ comp.lang.c FAQ 列表 · 問題 7.7b 在 C-FAQ 上於 2007 年 3 月 9 日訪問
- ↑ ISO/IEC 9899:1999 規範 (PDF). 第 319 頁,§ 7.20.5.2.
- ↑ ISO/IEC 9899:1999 規範 (PDF). 第 308 頁,§ 7.20.1.3.