關於ifndef endif用法的詳解和補充

2021-10-04 03:49:19 字數 3547 閱讀 4405

**

(直接看加粗的部分,if not define如果之前沒有定義這個的話)

#ifndef x

#define x //定義乙個巨集

…#endif

//c語言在對程式進行編譯時,會先根據預處理命令進行「預處理」。c語言編譯系統包括預處理,編譯、彙編和鏈結等部分。

#ifndef x //先測試x是否被巨集定義過

#define x

程式段1 //如果x沒有被巨集定義過,定義x,並編譯程式段 1

#else

程式段2 //如果x已經定義過了則編譯程式段2的語句,「忽視」程式段 1

#endif//終止if

**#ifndef 標識1 //判斷"標識1"是否定義,如果被定義則返回假,如果沒有被定義則返回真。

/**********************************/

語句1 #ifndef 標識1

語句2 #define 標識1

語句3 #endif

語句4 ……

語句5 ……

該段**意思是:如果標識1沒有被定義,則重定義標識1,即執行語句2、語句3;如果標識1已經被定義,則直接跳過語句2、語句3,直接執行語句4、語句5、……

/***********************************/

備註:#ifndef 和 #endif 要一起使用,如果丟失#endif,可能會報錯。

**一般格式是這樣的:

#ifndef ***《標識》//如果沒有定義***

#define ***《標識》//那麼來定義***

…#endif****//結束上面這個如果條件****

【重要的點】《標識》在理論上來說可以是自由命名的,但每個標頭檔案的這個「標識」都應該是唯一的。標識的命名規則一般是頭檔名全大寫,前後加下劃線,並把檔名中的「.」也變成下劃線,如:stdio.h

#ifndef stdio_h

#define stdio_h

**#endif**
【重要的點】#ifndef起到的效果是防止乙個c原始檔兩次包含同乙個標頭檔案,而不是防止兩個c原始檔包含同乙個標頭檔案。網上很多資料對這一細節的描述都是錯誤的。事實上,防止同一標頭檔案被兩個不同的原始檔包含這種要求本身就是不合理的,標頭檔案存在的價值就是被不同的原始檔包含

假如你有乙個c原始檔,它包含了多個標頭檔案,比如標頭檔案a和標頭檔案b,而標頭檔案b又包含了標頭檔案a,則最終的效果是,該原始檔包含了兩次標頭檔案a。如果你在標頭檔案a裡定義了結構體或者類型別(這是最常見的情況),那麼問題來了,編譯時會報大量的重複定義錯誤。

所以如果採用了#ifndef…#endif就可以避免這種重複定義;

例如:要編寫標頭檔案test.h,在標頭檔案開頭寫上兩行:

#ifndef _test_h

#define _test_h //一般是檔名的大寫

標頭檔案結尾寫上一行:

#endif

這樣乙個工程檔案裡同時包含兩個test.h時,就不會出現重定義的錯誤了。

分析:當第一次包含test.h時,由於沒有定義_test_h,條件為真,這樣就會包含(執行)#ifndef _test_h和#endif之間的**,當第二次包含test.h時前面一次已經定義了_test_h,條件為假,#ifndef _test_h和#endif之間的**也就不會再次被包含,這樣就避免了重定義了。

而把頭檔案的內容都放在#ifndef和#endif中,則無論標頭檔案會不會被多個檔案引用,都需要加上這個。一般格式是這樣的:

#ifndef 《標識》

#define 《標識》……

#endif

《標識》在理論上來說可以是自由命名的,但每個標頭檔案的這個「標識」都應該是唯一的。標識的命名規則一般是頭檔名全大寫,前面加下劃線,並把檔名中的「.」也變成下劃線,如:stdio.h

#ifndef _stdio_h

#define _stdio_h

…#endif

從編譯原理上來說,宣告是僅僅告訴編譯器,有個某型別的變數會被使用,但是編譯器並不會為它分配任何記憶體。而定義就是分配了記憶體。

對於下面的兩句**:

void func()

對於第一行**,編譯器不會做任何事,它不會為它在棧中分配一點東西,直到第三句,a=0;時,編譯器才會將其壓入棧中。而對於int b=0;這一句,編譯器就會生成一條指令,為它賦值。如果反彙編,看到的**可能是這樣的:

push 1;

push 0;

當然,並不一定編譯器就會樣做,也有可能在宣告int a時,編譯器就會把乙個廢值入棧,到第三條再為其賦值,這要看編譯器的具體取捨,所以,宣告不一定不是定義,而定義一定是定義。

但是,下面的宣告,一定僅僅是宣告:

extern int a;

這表示,有乙個int變數a,它一定是在另外其他地方定義的,所以編譯器此時一定不會做什麼分配記憶體的事,因為它就是宣告,僅僅表明下面的**引用了乙個符號,而這個符號是int型別的a而已;

變數的定義用於為變數分配儲存空間,還可以為變數指定初始值。在乙個程式中,變數有且僅有乙個定義。

宣告用於向程式表明變數的型別和名字,定義包括宣告:當定義變數時宣告了它的型別和名字。可以通過使用extern關鍵字宣告變數名而不定義它。不定義變數的宣告包括物件名、物件型別前的關鍵字extern。

****

c語言編譯系統是由上往下編譯的.一般被調函式放在主調函式後面的話,前面就該有宣告.不然c由上往下的編譯系統將無法識別。正如變數必須先宣告後使用一樣,函式也必須在被呼叫之前先宣告,否則無法呼叫!函式的宣告可以與定義分離,要注意的是乙個函式只能被定義一次,但可以宣告多次。

函式宣告由函式返回型別、函式名和形參列表組成。形參列表必須包括形參型別,但是不必對形參命名。這三個元素被稱為函式原型,函式原型描述了函式的介面。定義函式的程式設計師提供函式原型,使用函式的程式設計師就只需要對函式原型編輯即可。

【返回型別】 函式名(引數1型別 引數1,引數2型別 引數2,……);

int fun (int a, int b);

函式宣告中的形參名往往被忽略,如果宣告中提供了形參的名字,也只是用作輔助文件。另外要注意函式宣告是乙個語句,後面不可漏分號!

函式定義:

【返回型別】 函式名(引數型別1 引數名1,·····,引數型別n 引數名n)

int fun(int a,int b)

宣告與定義的區別:

函式的宣告與函式的定義形式上十分相似,但是二者有著本質上的不同。宣告是不開闢記憶體的,僅僅告訴編譯器,要宣告的部分存在,要預留一點空間。定義則需要開闢記憶體。

函式的定義

1.函式的定義是乙個完整的函式單元,包含函式型別、函式名、形參及形參型別、函式體等。

2.在程式中,函式的定義只能有一次

3.函式首部與花括號間不加分號

函式的宣告

1.函式宣告只是對編譯系統的乙個說明,是對定義的函式的返回值的型別說明,以通知系統在本函式中所呼叫的函式是什麼型別。

2.不包含函式體(或形參)

3.呼叫幾次該函式就應在各個主調函式中做相應宣告

4.函式宣告是乙個說明語句,必須以分號結束

關於oracle with as用法

with as 語法 針對乙個別名 with tmp as select from tb name 針對多個別名 with tmp as select from tb name tmp2 as select from tb name2 t as select from tb name3 1 2 3 ...

關於oracle with as用法

with as 語法 針對乙個別名 with tmp as select from tb name 針對多個別名 with tmp as select from tb name tmp2 as select from tb name2 t as select from tb name3 1 2 3 ...

關於oracle with as用法

with as 語法 針對乙個別名 with tmp as select from tb name 針對多個別名 with tmp as select from tb name tmp2 as select from tb name2 t as select from tb name3 1 2 3 ...