C語言預處理命令是什麼?
前面各章中,已經多次使用過#include命令。使用庫函式之前,應該用#include引入對應的標頭檔案。這種以#號開頭的命令稱為預處理命令。
C語言原始檔要經過編譯、連結才能生成可執行程式:
- 編譯(Compile)會將原始檔(.c檔案)轉換為目標檔案。對於 VC/VS,目標檔案字尾為.obj;對於GCC,目標檔案字尾為.o。
編譯是針對單個原始檔的,一次編譯操作只能編譯一個原始檔,如果程式中有多個原始檔,就需要多次編譯操作。
- 連結(Link)是針對多個檔案的,它會將編譯生成的多個目標檔案以及系統中的庫、元件等合併成一個可執行程式。
關於編譯和連結的過程、目標檔案和可執行檔案的結構、.h 檔案和 .c 檔案的區別,我們將在《C語言多檔案程式設計》專題中講解。
在實際開發中,有時候在編譯之前還需要對原始檔進行簡單的處理。例如,我們希望自己的程式在 Windows 和 Linux 下都能夠執行,那麼就要在 Windows 下使用 VS 編譯一遍,然後在 Linux 下使用 GCC 編譯一遍。但是現在有個問題,程式中要實現的某個功能在 VS 和 GCC 下使用的函式不同(假設 VS 下使用 a(),GCC 下使用 b()),VS 下的函式在 GCC 下不能編譯通過,GCC 下的函式在 VS 下也不能編譯通過,怎麼辦呢?
這就需要在編譯之前先對原始檔進行處理:如果檢測到是 VS,就保留 a() 刪除 b();如果檢測到是 GCC,就保留 b() 刪除 a()。
這些在編譯之前對原始檔進行簡單加工的過程,就稱為預處理(即預先處理、提前處理)。
預處理主要是處理以#開頭的命令,例如#include <stdio.h>等。預處理命令要放在所有函式之外,而且一般都放在原始檔的前面。
預處理是C語言的一個重要功能,由預處理程式完成。當對一個原始檔進行編譯時,系統將自動呼叫預處理程式對源程式中的預處理部分作處理,處理完畢自動進入對源程式的編譯。
編譯器會將預處理的結果儲存到和原始檔同名的.i檔案中,例如 main.c 的預處理結果在 main.i 中。和.c一樣,.i也是文字檔案,可以用編輯器開啟直接檢視內容。
C語言提供了多種預處理功能,如巨集定義、檔案包含、條件編譯等,合理地使用它們會使編寫的程式便於閱讀、修改、移植和除錯,也有利於模組化程式設計。
例項
下面我們舉個例子來說明預處理命令的實際用途。假如現在要開發一個C語言程式,讓它暫停 5 秒以後再輸出內容,並且要求跨平臺,在 Windows 和 Linux 下都能執行,怎麼辦呢?
這個程式的難點在於,不同平臺下的暫停函式和標頭檔案都不一樣:
- Windows 平臺下的暫停函式的原型是void Sleep(DWORD dwMilliseconds)(注意 S 是大寫的),引數的單位是“毫秒”,位於 <windows.h> 標頭檔案。
- Linux 平臺下暫停函式的原型是unsigned int sleep (unsigned int seconds),引數的單位是“秒”,位於 <unistd.h> 標頭檔案。
不同的平臺下必須呼叫不同的函式,並引入不同的標頭檔案,否則就會導致編譯錯誤,因為 Windows 平臺下沒有 sleep() 函式,也沒有 <unistd.h> 標頭檔案,反之亦然。這就要求我們在編譯之前,也就是預處理階段來解決這個問題。請看下面的程式碼:
#include <stdio.h> //不同的平臺下引入不同的標頭檔案 #if _WIN32 //識別windows平臺 #include <windows.h> #elif __linux__ //識別linux平臺 #include <unistd.h> #endif int main() { //不同的平臺下呼叫不同的函式 #if _WIN32 //識別windows平臺 Sleep(5000); #elif __linux__ //識別linux平臺 sleep(5); #endif puts("http://www.cdsy.xyz/"); return 0; }
#if、#elif、#endif 就是預處理命令,它們都是在編譯之前由預處理程式來執行的。這裡我們不討論細節,只從整體上來理解。
對於 Windows 平臺,預處理以後的程式碼變成:
#include <stdio.h> #include <windows.h> int main() { Sleep(5000); puts("http://www.cdsy.xyz/"); return 0; }
對於 Linux 平臺,預處理以後的程式碼變成:
#include <stdio.h> #include <unistd.h> int main() { sleep(5); puts("http://www.cdsy.xyz/"); return 0; }
你看,在不同的平臺下,編譯之前(預處理之後)的原始碼都是不一樣的。這就是預處理階段的工作,它把程式碼當成普通文字,根據設定的條件進行一些簡單的文字替換,將替換以後的結果再交給編譯器處理。