C 語言中的 #ifndef
Jinku Hu
2023年10月12日
本文將演示如何在 C 語言中使用 #ifndef
避免重入包含的多種方法。
在 C 語言中使用 ifndef
保護標頭檔案不被多次包含
C 語言中的標頭檔案用於定義同名原始檔中實現的函式的介面。介面通常包括函式原型、公開訪問的資料結構的定義以及其他一些雜項。
注意,標頭檔案可能會多次包含在原始檔中,導致編譯器出錯。通常情況下,用 #ifndef
前處理器指令可以防止這種情況發生,這個指令叫做包裝器 #ifndef
。當標頭檔案的內容被封裝成如下例所示的結構時,其中指令 #ifndef MY_GUARD
是起點,#endif
是終點。ifndef
指令檢查是否定義了 MY_GUARD
巨集,如果沒有則繼續,用下一個指令定義。如果使用者第二次包含相同的頭,ifndef
指令將評估為 false,並忽略 #endif
指令之前的程式碼。因此,編譯器將只從這個標頭檔案中得到一份程式碼,併成功編譯。
#include <stdio.h>
#ifndef MY_GUARD
#define MY_GUARD 1
#define PER(D) #D
#define JOIN(A, B) (A##B)
#define JOINX(A, B) JOIN(A, B)
int power(int base, int n) {
int p = base;
for (size_t i = 0; i < n; i++) {
p *= base;
}
return p;
}
#endif
另一種達到同樣效果的方法是在標頭檔案中包含 #pragma once
指令。前處理器只對這些標頭檔案掃描一次,並保證不被再次讀取。下面的方法有一個缺點,就是它在不同的前處理器之間的可移植性很低,所以大家不妨堅持使用包裝器 #ifndef
方法,以保證程式碼庫有更好的靈活性。
#include <stdio.h>
#pragma once
#define PER(D) #D
#define JOIN(A, B) (A##B)
#define JOINX(A, B) JOIN(A, B)
int power(int base, int n) {
int p = base;
for (size_t i = 0; i < n; i++) {
p *= base;
}
return p;
}
使用 ifndef
指令來確保巨集在 C 語言中不會被多次定義
另外,我們也可以使用 ifndef
指令檢查給定的巨集表示式是否已經被定義。邏輯工作原理和上一個例子完全一樣,如果表示式沒有被定義,下一個 #define
指令就會進行相應的處理。#ifndef
和 #endif
之間只有一行是一個巨集定義,這意味著如果條件為假,只跳過給定的巨集定義。
#include <stdio.h>
#include <stdlib.h>
#define PER(D) #D
#ifndef DLEVEL
#define DLEVEL 6
#endif
int main() {
for (int j = 0; j < DLEVEL; ++j) {
printf("%s\n", PER(stringify this));
}
exit(EXIT_SUCCESS);
}