// bala bala
#include就是包含頭文件用的,不是嗎?!
我之前也一直這么認(rèn)為的,直到我看了某些大神寫的代碼,后來我還特意查閱了C99標(biāo)準(zhǔn)。
人家是這么用的
還有這樣用的
當(dāng)時(shí),看得我一愣一愣的……
其實(shí),簡單來說,#include就是“包含”某個文件的意思,但這個“包含”,不能將思維限死在“頭文件”這個概念中,而應(yīng)該有更多的想象!#include在C語言中,算是預(yù)編譯指令(preprocessing directive)范疇,而預(yù)編譯指令在C語言就是一個大學(xué)問了。但是,我們先不要被這個“預(yù)編譯指令”名稱繞暈。上文,我們提到了頭文件這個概念,當(dāng)然我們也知道還有一個叫源文件的概念。這些我就不解釋了。但是,在C99標(biāo)準(zhǔn)中有一段這樣的話,需要研究下:簡單地理解,一個source file和一些由#include包含著的headers和source files,通過預(yù)編譯后,變成一個叫translation unit的東西。 從這里可以看出來,#include不但可以包含headers,還可以包含source files。 所以,我下面這個#include "add.h"和#include "minus.c"都是正確的,編譯一點(diǎn)問題都沒有。Asourcefiletogetherwithalltheheadersandsourcefilesincludedviathepreprocessingdirective#includeisknownasapreprocessingtranslationunit.Afterpreprocessing,apreprocessingtranslationunitiscalledatranslationunit.
ISO/IEC 9899:1999 (E)
//main.c
#include "add.h"
#include "minus.c"
int add(int a, int b)
{
return a+b;
}
int main(void)
{
int c = add(1,2);
int d = minus(2-1);
return 0;
}
//add.h
externintadd(inta,intb);
//minus.c
int minus(int a, int b)
{
return a-b;
}
不妨將腦洞開大一點(diǎn),除了*.h和*.c文件,我還可以include點(diǎn)別的么?答:可以。例如
//main.c
#include"multiply.txt"
int main(void)
{
inte=multiply(2,2);
return 0;
}
甚至,這樣也行
//main.c
#include"devide.fxxk"
int main(void)
{
intf=devide(2,2);
return 0;
}
繼續(xù)啊,#include不是放在文件上方,放中間行么。當(dāng)然
//main.c
int main(void)
{
#include"squel.xx"
intg=squel(2,2);
return 0;
}
好家伙,這么下去,我是不是可以這么干
//data.txt
1,2,3,4,5,6,7,8,9
//main.c
int arr[] =
{
#include "data.txt"
}
int main(void)
{
return 0;
}
然后,你又好奇了,能不能將data.txt換成二進(jìn)制形式的data.bin?呵呵,這種不行,編譯器在預(yù)編譯階段只認(rèn)得是text文本才行。好吧……你不是說這是個預(yù)編譯指令嗎,我很好奇,#include預(yù)編譯后成啥樣子的?這好辦,動動手指頭,一個gcc -E命令即可搞定。就以上面第一個例子,命令行執(zhí)行g(shù)cc ./main.c -E -o main.i
extern int add(int a, int b);
int minus(int a, int b)
{
return a-b;
}
int add(int a, int b)
{
return a+b;
}
int main(void)
{
int c = add(1,2);
int d = minus(2-1);
return 0;
}
看到了吧,#include就是把它后面的文件內(nèi)容直接include進(jìn)來。就這么簡單粗暴。那么#include在C語言中是不是很簡單?你說呢!我見過有人這么寫代碼的,還TM的一整個團(tuán)隊(duì)是這么做的。將整個所以.h文件全部包含在一個includes.h的頭文件中,然后在其他.c文件里面,就直接#include "includes.h"。
// includes.h
#include "adc.h"
#include "uart.h"
#include "spi.h"
#include "iic.h"
#include "dma.h"
#include "pwm.h"
#include"pin.h"
#include "led.h"
#include "os.h"
#include "timer.h"
...
真TM的簡便。我第一次見到這玩意,簡直是驚呆了,還有這種操作。不好嗎?有什么不好?多簡潔啊!從上面的分析看,#include就是將它后面包含的頭文件源文件,全部展開哦。簡潔?你問過編譯器啥感受么?帶來的最直接的感受是,編譯過程慢!includes.h里包含得越多就越慢!另外一個隱含的問題是,會造成include里的內(nèi)容混亂,頭文件里的內(nèi)容全部是全局的了。我絕對不推薦這種玩法的。因?yàn)椋A(yù)編譯還有更好玩的玩法。不過,在介紹新玩法之前,得想個問題,如果一個頭文件,重復(fù)包含多次會怎樣?也許,你會回答,我是不允許出現(xiàn)這種情況的,就算出現(xiàn)這種情況,我也可以用#ifdef...#endif這種方式規(guī)避。如果你是應(yīng)屆生面試,這樣回答,面試官也許是點(diǎn)點(diǎn)頭說你有點(diǎn)經(jīng)驗(yàn)的。因?yàn)橹貜?fù)include,就相當(dāng)于把頭文件重復(fù)展開了多次,C語言中有些定義是不允許重復(fù)多次的。例如,上面的例子
// main.c
這樣是有問題的,因?yàn)樯厦嫦喈?dāng)于重復(fù)定義了兩次int minus(int a,int b)函數(shù)了。
In file included from .main.c:4:
minus.c:1:5: 錯誤:‘minus’重定義
1 | int minus(int a, int b)
| ^~~~~
如果將minus.c改成這樣就行了
int minus(int a, int b)
{
return a-b;
}
這個簡單啊,我也會啊。嗯,但是,我不是想說這個,我真的想說重復(fù)include有意想不到的好處呢。這就不得不提下,我以前寫的X-MACRO大法了。以下是一個MEMORY字段分配的設(shè)想:-
將Memory的物理地址映射到自定義邏輯地址
-
邏輯地址按Memory的Block對齊,邏輯地址從0開始
-
用戶數(shù)據(jù)按邏輯地址分配
-
應(yīng)用接口按實(shí)際內(nèi)容大小操作
-
底層接口根據(jù)邏輯地址對齊讀寫Memory
entry name | address | size |
ID_DATA1 | 0 | 8 |
ID_DATA2 | 8 | 8 |
ID_DATA3 | 16 | 16 |
... |
// defines.h
接著在C文件里面這么玩?
//memory.c
ENTRY(ID_DATA1, 0, 8)
ENTRY(ID_DATA2, 8, 8)
ENTRY(ID_DATA3, 16, 16)
ENTRY(ID_DATA4, 32, 8)
typedef enum
{
ALL_ENTRIES()
MEM_ID_MAX
} MEM_ID;
const uint32_t mem_addr[] =
{
ALL_ENTRIES()
};
const uint16_t mem_size[] =
{
ALL_ENTRIES()
};
你也許會反問我,定義一個結(jié)構(gòu)體不就搞定了嗎?別急,這樣做的好處是enum的ID順序跟addr和size是一一對應(yīng)的,不會錯亂,另一個好處是,可以隨便在ALL_ENTRIES()下面擴(kuò)展條目,也不影響ID的對應(yīng)關(guān)系。如果用結(jié)構(gòu)體去定義的話,也很好,但是會增加數(shù)組遍歷時(shí)間,如果是很龐大的條目數(shù)的話,這個效率問題就要考慮了。
-
C語言
+關(guān)注
關(guān)注
180文章
7608瀏覽量
137164 -
代碼
+關(guān)注
關(guān)注
30文章
4803瀏覽量
68758
原文標(biāo)題:C語言的include沒你想的那么簡單(圖文版)
文章出處:【微信號:embedded_sw,微信公眾號:嵌入式軟件實(shí)戰(zhàn)派】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論