在最開始接觸到C語言的時候,看到很多大牛們寫的代碼里面就有#ifndef? #define #endif 這三條語句,當時不明白什么意思,慢慢的后來自己也開始用了,也知道了他們合起來使用時為了防止頭文件被二次編譯,因為我們曾經(jīng)寫代碼的過程中一定遇到過<.......重復定義或是重復包含>等類似的錯誤吧。
到了學習C++的時候,我們老師給我們看他的代碼的時候發(fā)現(xiàn)他的頭文件里用的是#pragma once,就奇怪這是什么呢?然后私下百度了后才知道,這也是防止頭文件二次編譯的,然后就開始用#pragma once了,這一用就是一個學期了,后來發(fā)現(xiàn)用著很好,再也沒有重復編譯或是重復包含的錯誤了,當時寫C++代碼的時候習慣了單個類放在獨立的頭文件里,這樣子好糾錯,也好擴展,調(diào)理更清晰。
最近一段時間為了給學弟學妹們講C語言的知識,又回過頭來看宏定義的方法。就想寫這么一篇博客來說明一下這兩者之間的區(qū)別,過程中也百度了很多,也看了很多別人是怎么寫的,怎么認識的。這里面肯定有他人文章中的影子,但是這絕對不是簡單的復制粘貼,而是自己看了那么多之后的思考,總結(jié)之作。
#ifndef這種是最早期使用的方法,是基于語言的宏定義名字不能沖突的前提下的。這種方法不僅能保證同一個頭文件不會被包含兩次,也可以保證內(nèi)容完全相同的兩個文件也只能被包含一次。但是他優(yōu)缺點,就是你的#ifndef 后面跟的宏名字和你程序中的其他宏名字發(fā)生了”撞車“,那么會出現(xiàn)以下兩種后果
第一種:file1頭文件中有一個宏
//file1.h
#define BOOK_H ? //宏名
現(xiàn)在又有一個文件 book.h 里面使用了宏定義方式防止頭文件二次編譯
#ifndef BOOK_H
#define BOOK_H
// ?program codes
#endif
下面是你的主函數(shù)所在文件內(nèi)容
#include"fil1.h"
#include"book.h" ? //這兩個都是你自己的頭文件
#include<........>
..................
預編譯階段把file1文件展開,就得到了宏 BOOK_H,在處理book.h文件時就發(fā)現(xiàn)BOOK_H這個宏已經(jīng)存在了,就不會包含book.h頭文件了,這就是弊端所在了。
第二種就是:
兩者順序反過來了,弊端類似。
#pragma once這種方式,是微軟編譯器獨有的,也是后來才有的,所以知道的人并不是很多,用的人也不是很多,因為他不支持跨平臺。如果你想寫跨平臺的代碼,最好使用上一種。這是一種由編譯器提供支持的方式,防止同一文件的二次編譯,這里的同一文件指的是物理文件。
他也是有弊端的:
假如你的某一個頭文件有多份拷貝,那么這些文件雖然在邏輯上都是一樣的,但是在物理上他們卻是不同的,所以當你把這些文件包含的時候,就會發(fā)現(xiàn)真的都包含進來了,然后就是編譯錯誤了。還有,當物理上的同一文件被嵌套包含的時候,使用第一種方法預處理會每一次打開該文件做判斷的,但是第二種方法則不會,所以在此#pragma once 會更快些。下面舉例說明
// Test1.h
#ifndefine ?TEST1_H
#defineTEST1_H
...
#endif
// Test2.h
#pragma once
...
// Test.cpp
#include "Test1.h"????? // line 1
#include "Test1.h"????? // line 2
#include "Test2.h"????? // line 3
#include "Test2.h"????? // line 4 這里的Test2.h是同一物理文件
預處理器在執(zhí)行這四句的時候,先打開Test1.h然后發(fā)現(xiàn)里面的宏TEST1_H沒有被定義,所以會包含這個文件,第二句的時候,同樣還是會打開Test1.h的發(fā)現(xiàn)宏已定義,就不包含該文件了。第三句時,發(fā)現(xiàn)之前沒有包含Test2.h則會把該文件包含進來,執(zhí)行第四句的時候,發(fā)現(xiàn)該文件已經(jīng)被包含了,所以不用打開就直接跳過了