《c語言深度剖析》整理 預處理

2021-06-01 13:47:33 字數 4904 閱讀 8700

一、巨集定義

1. 巨集定義的作用域

巨集定義開始,到檔案結束(其他的檔案包含巨集定義的檔案也可引用)。

2. 巨集定義可增加**的可讀性

#define error_poweroff -1

若不採用巨集定義的方式,**中出現-1 時,程式的可讀性變差,**中出現有具體的含義的單獨的數字(比如上面-1) 稱為魔鬼數,別人閱讀**的時候會抓狂,恐怕自己閱讀的時候,也不知具體的含義

3. 巨集預編譯階段巨集定義替換**中具體的定義

這點**中定義比較容易出錯,比如

(1)#define sqr (x)   x * x

當表示式 x = 10+1, sqr(x) * sqr(x)   替換為 10+1*10+1,顯然這不是我們想要的結果,導致出錯

(2)#define add (x)  (x)+(x)

當表示式 x=5, add(x)*add(x) 替換為 (5)+(5)*(5)+5,顯然這不是我們想要的結果,導致出錯

解析:避免這種錯誤,當巨集定義的時候,每部分都加上括號:(1))#define sqr (x)    ((x) *( x)) (2)#define add (x)   ((x)+(x))

4. 當巨集出現在字串中的時候,巨集不會被替換

比如 printf("add(x)");  列印的結果為 add(x) 而不是 (x)+(x)

5. 巨集定義函式的時候,函式識別符號和引數之間不能有空格

比如   #define sqr   (x)   x * x   , 巨集將變成**中用(x)   x * x   替換**中的sqr  ;

但引用巨集的時候可以有空格,比如 #define add (x)   ((x)+(x)), 應用的時候 add       (3)  和 add(3) 都是正確的

6. 取消巨集定義的符號 #undef,此符號之後的巨集的定義將不再起作用

二、條件編譯

條件編譯的形式之一:

(1)     #ifdef 識別符號

程式段1

#else

程式段2

#endif

(2)    #if 常量表示式

程式段1

#else

程式段2

#endif

條件編譯應用於場景之一,比如當在windows平台下編寫**除錯,而程式又是可以執行在linux或者aix平台下,這樣可以進行條件編譯;

條件編譯和 if--else if---else if .....else 語句一樣可以有不同的引申

三、檔案包含#include   

1.  檔案包含是預處理的乙個重要功能,它可用來把多個原始檔連線成乙個原始檔進行編譯,結果將生成乙個目標檔案。c語言提供#include 命令來實現檔案包含的操作,它實際是巨集定義的延伸;

(1)#include

c 編譯系統所提供的並存放在指定的子目錄下的標頭檔案。找到檔案後,用檔案內容替換該語句;

(2)#include 「filename」

預處理應在當前目錄中查詢檔案名為filename 的檔案,若沒有找到,則按系統指定的路徑資訊,搜尋其他目錄。找到檔案後,用檔案內容替換該語句。

#include 是將已存在檔案的內容嵌入到當前檔案中

2. #include 支援相對路徑

比如,#include "./filename" (當前目錄下的檔案filename) #include "./icp/filename"(上層路徑檔案icp目錄下的檔案filename)

四、#pragma comment(...)

該指令將乙個注釋記錄放入乙個物件檔案或可執行檔案中。常用的lib 關鍵字,可以幫我們連入乙個庫檔案。比如:

#pragma comment(lib, "user32.lib")

該指令用來將user32.lib 庫檔案加入到本工程中。

linker:將乙個鏈結選項放入目標檔案中,你可以使用這個指令來代替由命令列傳入的或 者在開發環境中設定的鏈結選項,你可以指定/include 選項來強制包含某個物件,

例如:#pragma comment(linker, "/include:__mysymbol")

五、#pragma warning

#pragma warning( disable : 4507 34; once : 4385; error : 164 )

等價於:

#pragma warning(disable:4507 34) // 不顯示4507 和34 號警告資訊

#pragma warning(once:4385) // 4385 號警告資訊僅報告一次

#pragma warning(error:164) // 把164 號警告資訊作為乙個錯誤。

不過程式設計的時候,盡量少用disable,盡量在編碼的時候,將warning問題解決掉,有的時候warning 也是潛在的bug

六、#pragma once

在標頭檔案的最開始加入這條指令就能夠保證標頭檔案被編譯一次

另外保證標頭檔案只編譯一次的方法:

#ifndef  _filename_h

#define _filename_h

#endif

七、#pragma code_seg

另乙個使用得比較多的pragma 引數是code_seg。格式如:

#pragma code_seg( ["section-name"[,"section-class"] ] )

它能夠設定程式中函式**存放的**段,當我們開發驅動程式的時候就會使用到它

八、#pragma message

能夠在編譯資訊輸出視窗中輸出相應的資訊,這對於源**資訊的控制是非常重要的。其使用方法為:

#pragma message(「訊息文字")

當編譯器遇到這條指令時就在編譯輸出視窗中將訊息文字列印出來。 這對於我們進行原始碼控制,**除錯有幫助。

九、記憶體對齊 #pragma pack

1. 記憶體對齊的原理:

字,雙字,和四字在自然邊界上不需要在記憶體中對齊。(對字,雙字,和四字來說,自然邊界分別是偶數字址,可以被4 整除的位址,和可以被8 整除的位址)無論如

何,為了提高程式的效能,資料結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在於,為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;然而,對齊的記憶體

訪問僅需要一次訪問。減少記憶體位址匯流排訪問未對齊的位址的週期。

2. 記憶體對齊例子

(1)struct teststruct1

;解析:此結構體在記憶體中的布局為  1*,11,1*******,1111    (1  代表占用記憶體,* 代表為記憶體對齊補的記憶體空間)

所以 sizeof(teststruct1)  為12

3. 記憶體對齊的避免

可以在程式設計的時候,盡量避免記憶體對齊的情況,盡量自然對齊節省占用記憶體空間,比如上例

struct teststruct2

;sizeof(teststruct2) 為 8

4.

#pragma pack

#pragma pack (n),編譯器將按照n 個位元組對齊

#pragma pack (),編譯器將取消自定義位元組對齊方式 例如

#pragma pack(8)

struct teststruct4

;struct teststruct5

;#pragma pack()

解析:

teststruct4 記憶體布局: 1***,1111

teststruct5 記憶體布局:  1***,1***,1111****,11111111

所以 sizeof(teststruct4) 為 8,sizeof(teststruct5)為 24

5. 記憶體對齊的規則

(1)每個成員分別按自己的方式對齊,並能最小化長度

(2)複雜型別(如結構)的預設對齊方式是它最長的成員的對齊方式,這樣在成員是複雜型別時,可以最小化長度

(3)對齊後的長度必須是成員中最大的對齊引數的整數倍,這樣在處理陣列時可以保證每一項都邊界對齊

(4)對於陣列,比如:char a[3];它的對齊方式和分別寫3 個char 是一樣的.也就是說它還是按1 個位元組對齊,即陣列按照陣列中的每個成員的型別對齊

如果寫: typedef char array3[3];array3 這種型別的對齊方式還是按1 個位元組對齊,而不是按它的長度

(5)不論型別是什麼,對齊的邊界一定是1,2,4,8,16,32,64....中的乙個

十、#預算符

1. # 符號的用處之一: 將巨集中字串中的變數,以變數值的形式列印

(1)比如:

#define sqr(x) printf("the square of x is %d.\n", ((x)*(x)));

如果這樣使用巨集:sqr(8);

則輸出為:

the square of x is 64.

(2) 修改上面的巨集為:

#define sqr(x) printf("the square of "#x" is %d.\n", ((x)*(x)));

再使用:

sqr(8);

則輸出的是:

the square of 8 is 64.

十一、##預算符

##預算符作用:粘合劑

#define display(n) x ## n

如果這樣使用巨集:

display(8)

則會被展開成這樣:

x8

C語言深度解剖 預處理

1 line 表示正在編譯的檔案的行號,2 file 表示正在編譯的檔案的名字,3 date 表示編譯時刻的日期字串,4 time 表示編譯時刻的時間字串,5 stdc 判斷該檔案是不是定義成標準c程式。一 巨集定義 1 數值巨集常量 define pi 3.141592654 define err...

c語言整理編譯預處理

c程式執行過程 源程式 編譯預處理 編譯 優化程式 匯程式設計序 鏈結程式 可執行檔案。編譯預處理時,先要讀取源程式,對預處理指令 開頭指令 以及特殊的符號進行處理,比如define 替換指令,也會進行刪除注釋,多餘的空白字元,然後產生的預處理檔案或者程式傳給編譯器,在程式中以 開頭的編譯指令稱為預...

C語言深度剖析

c語言深度剖析 1,編譯器通常不為普通const唯讀變數分配儲存空間,而是將他們儲存在符號表中,使得它成為乙個編譯期間的值,沒有了儲存與讀記憶體的操作,使得它的效率更高。2,const int p p可變,p指向的物件不變。int const p p可變,p指向的物件不可變 int const p ...