顧名思義,預處理器是在編譯之前處理我們的源代碼的程序。 在C/C++中編寫程序和執行程序之間涉及許多步驟。在實際開始學習預處理器之前,讓我們看一下這些步驟。
您可以在上圖中看到中間步驟。程序員編寫的源代碼存儲在文件program.c中。然後,該文件由預處理器處理,並生成名為program的擴展源代碼文件。該擴展文件由編譯器編譯,並生成一個名為program .obj的目標代碼文件。最後,鏈接器將此目標代碼文件鏈接到庫函數的目標代碼,以生成可執行文件program.exe。
預處理程序提供預處理程序指令,這些指令告訴編譯器在編譯之前對源代碼進行預處理。所有這些預處理程序指令均以“#”(哈希)符號開頭。C/C++程序中語句開頭的(#)符號表示它是預處理程序指令。我們可以將這些預處理程序指令放置在程序中的任何位置。 一些預處理器指令的示例包括:#include,#define,#ifndef等。
預處理器指令有4種主要類型:
- 宏
- 文件包含
- 條件編譯
- 其他指令
現在讓我們詳細瞭解這些指令中的每一個。
1.
宏(Macros):宏是程序中的一段代碼,具有一定的名稱。只要編譯器遇到此名稱,編譯器就會用實際的代碼段替換該名稱。“ #define”指令用於定義宏。現在讓我們藉助程序來了解宏定義:<code>#include <iostream>
// macro definition
#define LIMIT 5
int main()
{
for (int i = 0; i < LIMIT; i++) {
std::cout << i << "\\n";
}
return 0;
} /<iostream>/<code>
輸出:
<code>0
1
2
3
4/<code>
在上述程序中,編譯器執行LIMIT時,將其替換為5。宏定義中的LIMIT稱為宏模板,而5則是宏擴展。
注意:宏定義的末尾沒有分號(';')。宏定義不需要以分號結尾。
帶參數的宏:我們還可以將參數傳遞給宏。用參數定義的宏的作用類似於函數。 讓我們通過一個程序來理解這一點:
<code>#include <iostream>
// macro with parameter
#define AREA(l, b) (l * b)
int main()
{
int l1 = 10, l2 = 5, area;
area = AREA(l1, l2);
std::cout << "Area of rectangle is: " << area;
return 0;
} /<iostream>/<code>
輸出:
<code>Area of rectangle is: 50/<code>
從上面的程序中我們可以看到,只要編譯器在程序中找到AREA(l,b),它就會用語句(l * b)替換它。不僅如此,傳遞給宏模板AREA(l,b)的值還將在語句(l * b)中替換。因此,AREA(10,5)等於10 * 5。
2. 文件包含:這種類型的預處理程序指令告訴編譯器在源代碼程序中包含文件。 用戶可以在程序中包括兩種類型的文件:
1)頭文件或標準文件:這些文件包含預定義函數的定義,例如printf(),scanf()等。必須包含這些文件才能使用這些函數。不同的函數在不同的頭文件中聲明。 例如,標準I/O功能位於“ iostream”文件中,而執行字符串操作的函數位於“ string”文件中。
語法:
<code>#include< file_name >/<code>
其中file_name是要包含的文件的名稱。 “ ”括號告訴編譯器在標準目錄中查找文件。
2) 用戶定義的文件:當程序變得很大時,將其分成較小的文件並在需要時包括在內是一個很好的做法。這些類型的文件是用戶定義的文件。這些文件可以包括為:
<code>#include"filename"/<code>
3. 條件編譯:條件編譯指令是指令的類型,可幫助根據某些條件編譯程序的特定部分或跳過程序某些特定部分的編譯。這可以通過兩個預處理命令“ ifdef”和“endif”來完成。
語法:
<code>#ifdef macro_name
statement1;
statement2;
statement3;
.
.
.
statementN;
#endif/<code>
如果定義了名稱為“ macroname”的宏,則該語句塊將正常執行,但如果未定義,則編譯器將直接跳過該語句塊。
4. 其他指令:除了上述指令外,還有另外兩個不常用的指令。這些是:
1) #undef指令:#undef指令用於取消定義現有的宏。該指令的工作方式如下:
<code>#undef LIMIT/<code>
使用此語句將取消定義現有的宏LIMIT。在此語句之後,每個“ #ifdef LIMIT”語句將評估為false。
2) #pragma指令:此指令是一種特殊用途的指令,用於打開或關閉某些功能。這種類型的指令是特定於編譯器的,即它們隨編譯器的不同而不同。下面討論了一些#pragma指令:
#pragma startup和#pragma exit:這些指令可幫助我們指定程序啟動之前(控件傳遞到main()之前)和程序退出之前(控件從main()返回之前)所需運行的功能。
注意:以下程序不適用於GCC編譯器。
看下面的程序:
<code>#include <stdio.h>
void func1();
void func2();
#pragma startup func1
#pragma exit func2
void func1()
{
printf("Inside func1()\\n");
}
void func2()
{
printf("Inside func2()\\n");
}
int main()
{
void func1();
void func2();
printf("Inside main()\\n");
return 0;
} /<stdio.h>/<code>
輸出:
<code>Inside func1()
Inside main()
Inside func2()/<code>
當在GCC編譯器上運行時,以上代碼將產生如下輸出:
<code>Inside main()/<code>
發生這種情況是因為GCC不支持#pragma啟動或退出。 但是,您可以將以下代碼用於GCC編譯器上的類似輸出。
<code>#include <stdio.h>
void func1();
void func2();
void __attribute__((constructor)) func1();
void __attribute__((destructor)) func2();
void func1()
{
printf("Inside func1()\\n");
}
void func2()
{
printf("Inside func2()\\n");
}
int main()
{
printf("Inside main()\\n");
return 0;
} /<stdio.h>/<code>
#pragma warn指令:該指令用於隱藏在編譯過程中顯示的警告消息。
我們可以隱藏警告,如下所示:
- #pragma warn -rvl:該指令隱藏那些本應返回值的函數未返回值時發出的警告。
- #pragma warn -par:該指令隱藏當函數不使用傳遞給它的參數時引發的警告。
- #pragma warn -rch:此指令隱藏當代碼不可訪問時發出的警告。例如:在函數的return語句之後編寫的任何代碼均不可訪問。
閱讀更多 少兒編程Prog61 的文章