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

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

具體示例

1、

#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,可能會報錯。
**

2

一般格式是這樣的:
  #ifndef xxx<標識>//如果沒有定義xxx
  #define xxx<標識>//那麼來定義xxx
  …
  #endif
****//結束上面這個如果條件****

【重要的點】<標識>在理論上來說可以是自由命名的,但每個頭文件的這個“標識”都應該是唯一的。標識的命名規則一般是頭文件名全大寫,前後加下劃線,並把文件名中的“.”也變成下劃線,如:stdio.h
  #ifndef STDIO_H
  #define STDIO_H

    **#endif**

#ifndef xxx//如果沒有定義xxx
#define xxx//定義xxx
#endif //結束這個如果
在c語言中,對同一個變量或者函數進行多次聲明是不會報錯的。所以如果h文件裏只是進行了聲明工作,即使不使用#ifndef宏定義,一個c文件多次包含同一個h文件也不會報錯。使用#ifndef可以避免下面這種錯誤:如果在h文件中定義了全局變量,一個c文件包含同一個h文件多次,如果不加#ifndef宏定義,會出現變量重複定義的錯誤;如果加了#ifndef,則不會出現這種錯。
拓展:百度一下:https://baike.baidu.com/item/%23ifndef?fr=aladdin

需要注意的是

【重要的點】#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()
{
int a;
int b=1;
a=0;
}
對於第一行代碼,編譯器不會做任何事,它不會爲它在棧中分配一點東西,直到第三句,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)
{
int c;
c=a+b;
return c;
}
聲明與定義的區別:

函數的聲明與函數的定義形式上十分相似,但是二者有着本質上的不同。聲明是不開闢內存的,僅僅告訴編譯器,要聲明的部分存在,要預留一點空間。定義則需要開闢內存。
函數的定義
1.函數的定義是一個完整的函數單元,包含函數類型、函數名、形參及形參類型、函數體等。
2.在程序中,函數的定義只能有一次
3.函數首部與花括號間不加分號
函數的聲明
1.函數聲明只是對編譯系統的一個說明,是對定義的函數的返回值的類型說明,以通知系統在本函數中所調用的函數是什麼類型。
2.不包含函數體(或形參)
3.調用幾次該函數就應在各個主調函數中做相應聲明
4.函數聲明是一個說明語句,必須以分號結束

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章