C語言的預編譯,程式設計師必須懂的知識!

由“原始碼”到“可執行檔案”的過程包括四個步驟:預編譯、編譯、彙編、連結。所以,首先就應該清楚的首要問題就是:預編譯只是對程式的文字起作用,換句話說就是,預編譯階段僅僅對原始碼的單詞進行變換,而不是對程式中的變數、函式等。

預編譯指令的基本知識不作詳細介紹,只稍作彙總,重點是後面的我能想到的 使用時的注意事項。

C語言的預編譯,程式設計師必須懂的知識!

1。 基本內容

預編譯指令基本分類如下

類別

指令

預定義符號__FILE__、__LINE__、__DATE__、__TIME__、__STDC__宏#define檔案包含#include條件編譯#if、#elif、#else、#ifdef、#ifndef、#endif

還有一些指令,名稱和功能如下表:

指令功能#空指令#undef移除一個空定義#error停止編譯,並生成錯誤資訊#line修改__LINE__和__FILE__的值#progma允許編譯器提供額外功能

在定義宏的時候,有兩個運算子:

運算子功能#將宏引數轉換為字串##將多個符號連線成一個識別符號

2。 宏定義

1。 一般在宏定義的結尾不加分號。

我們在使用的時候,要加上分號,像我們平時寫語句一樣。

2。 注意加括號。

在有引數的空定義中,如果含有數值運算,那麼就要在“宏整體”和“宏引數”兩端都要加上括號。

如:#define max(a, b) ((a)+(b));

3。 注意空格。

在有引數的宏定義中,注意“宏名稱”和“引數列表”之間不能有空格。

如:#define max (a, b) ((a)+(b)); 在“max”和”(a, b)”之間不能有空格。

4。 不要使用有副作用的引數區呼叫宏。

常見的有副作用的引數有:a++,getchar()等。

如:宏定義為#define max (a, b) ((a)+(b)); 那麼使用max(i++, j++)呼叫該宏,會造成 i 或 j 中的一個值增加2,而不是我們期望的 1。

5。 可以使用編譯器選項 新增宏 和 移除宏。

我使用的是gcc,新增宏的指令是”-D”,移除宏的指令是”-U”。

6。 宏引數替換的時候,不會替換字串中的字元。

即不會替換雙引號之間的字元,其他的都會被替換,包括單引號之間的。

7。 可以使用#將 宏引數的值 轉化為字串。

直接使用#,是將宏引數的名稱轉化為字串。利用下面的技巧(增加一個過渡宏),可以將“宏引數的值”轉化為字串(當宏引數有值時,這時的宏引數常常也是一個宏)。

[cpp] view plain copy

#include

#include

#define NUMBER ten /* 宏名稱為NUMBER,宏的值為ten */

#define Str(x) #x

#define XStr(x) Str(x) /* 增加的一個 過渡宏 */

int main(){

printf(”Str(NUMBER) == %s /n“, Str(NUMBER));

printf(”XStr(NUMBER) == %s /n“, XStr(NUMBER));

system(”pause“);

return EXIT_SUCCESS;

}

輸出結果為:

[cpp] view plain copy

Str(NUMBER) == NUMBER

XStr(NUMBER) == ten

8。 使用##運算子來實現識別符號連線。

不過,不建議使用運算子##來連線識別符號,因為這個容易是程式可讀性大大降低。

C語言的預編譯,程式設計師必須懂的知識!

3。 檔案包含

1。 要將標頭檔案的定義在保護條件中。

目的是為了防止重複包含標頭檔案。如果你檢視過gcc或者其他編譯器的原始碼,你一定對這個非常熟悉。

例如,你要編寫一個頭檔案,myheader。h,那麼你的標頭檔案的內容形式應該為:(定義一個_MYHEADER宏)

[cpp] view plain copy

#ifndef _MYHEADER

#define _MYHEADER 1

/* 中間是你的標頭檔案內容 */

#endif /* _MYHEADER */

2。 注意windows系統和Unix系統的路徑符號不同。

可以再#include中指定路徑來包含檔案,例如 #include “。。/head。h”。但是注意,windows中使用反斜線”/”作為路徑分隔符,而Unix系統使用的是斜線”/”。

3。 可以使用 編譯器選項 來設定搜尋路徑。

我使用的gcc,使用的-Idir選項,例如: -I”D:/Dev-Cpp/include“。

4。 條件編譯

1。 #ifdef等價於#if defined(),#ifndef等價於#if !defined()。

2。 在#if中可以使用邏輯運算子(&&、||、!)。在#ifdef 中是不可以使用的,這也是#if的優越點。

[cpp] view plain copy

#include

#include

#define A 1

#define B 0

int main()

{

#if defined( A ) && defined( B )

printf(”test logic operation in #if /n“); /* 如果上面的邏輯判斷成立,那麼將打印出一句話;如果不成立,那麼 就不會列印這句話 */

#endif

system(”pause“);

return EXIT_SUCCESS;

}

執行結果:

[cpp] view plain copy

test logic operation in #if

3。 sizeof(int)在預編譯階段是不會被求值的。

只要知道“預編譯階段”在真正的“編譯階段”之前,就很容易理解了。預編譯階段只是對組成原始碼中的字元進行作用,從某種意義上來說,它有時甚至不知道它的操作物件是什麼,它只是按照既定的規則執行替換。

sizeof(int),無論是sizeof的解析,還是型別的解析,都是在“編譯階段”才開始的,編譯階段知道它的操作物件是什麼。

下面的程式碼是錯誤的

[cpp] view plain copy

#if sizeof(int) == 2

printf(”precompile sizeof(int)“);

#endif

5。 額外注意

把一個預處理指令寫成多行的形式,要使用符號”/”,並且在該符號後面應緊跟換行符。而非預處理指令的程式碼行不需要使用該符號,直接換行即可。 原因:編譯階段會自動忽略空白符,而預編譯階段不會。

C語言的預編譯,程式設計師必須懂的知識!