<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C語言的include沒你想的那么簡單(圖文版)

          共 7812字,需瀏覽 16分鐘

           ·

          2023-09-05 09:03

          C語言中的include很簡單,但不是你想象中的簡單。
          你對#include的認識是不是只停留在包含頭文件的認知中,好像也沒有別的用處,小小東西也翻不起什么風浪?
          #include <stdio.h>#include "user_header.h"// bala bala
          #include就是包含頭文件用的,不是嗎?!
          我之前也一直這么認為的,直到我看了某些大神寫的代碼,后來我還特意查閱了C99標準。
          人家是這么用的
          define DET_START_SEC_VAR_INIT_UNSPECIFIED# include "MemMap.h" 
          # define DET_STOP_SEC_VAR_INIT_UNSPECIFIED# include "MemMap.h"
          # define DET_START_SEC_VAR_NOINIT_8BITinclude "MemMap.h" 
          # define DET_STOP_SEC_VAR_NOINIT_8BIT# include "MemMap.h"
          還有這樣用的
          #define STRUCT_GEN_START
          #include "defines.h"#include "param_gen.h"
          #include "defines.h"#include "param_gen.h"
          #include "defines.h"#include "param_gen.h"
          #include "defines.h"#include "param_gen.h"
          #include "defines.h"#include "param_gen.h"
          當時,看得我一愣一愣的……
          其實,簡單來說,#include就是“包含”某個文件的意思,但這個“”,不能將思維限死在“頭文件”這個概念中,而應該有更多的想象!
          #include在C語言中,算是預編譯指令(preprocessing directive)范疇,而預編譯指令在C語言就是一個大學問了。
          但是,我們先不要被這個“編譯指令”名稱繞暈。上文,我們提到了頭文件這個概念,當然我們也知道還有一個叫源文件的概念。這些我就不解釋了。但是,在C99標準中有一段這樣的話,需要研究下:

          source file together with all the headers and source files included via the preprocessing directive #include is known as a preprocessing translation unit. After preprocessing, a preprocessing translation unit is called a translation unit.

          ISO/IEC 9899:1999 (E)
          簡單地理解,一個source file和一些由#include包含著的headers和source files,通過預編譯后,變成一個叫translation unit的東西。
          從這里可以看出來,#include不但可以包含headers,還可以包含source files。
          所以,我下面這個#include "add.h"#include "minus.c"都是正確的,編譯一點問題都沒有。
          // 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.hextern int add(int a, int b);
          // minus.cint minus(int a, int b){    return a-b;}
          不妨將腦洞開大一點,除了*.h和*.c文件,我還可以include點別的么?答:可以。例如
          // main.c#include "multiply.txt"
          int main(void){    int e = multiply(2,2); return 0;}
          甚至,這樣也行
          // main.c#include "devide.fxxk"
          int main(void){    int f = devide(2,2); return 0;}
          繼續(xù)啊,#include不是放在文件上方,放中間行么。當然
          // main.cint main(void){    #include "squel.xx"    int g = squel(2,2);    return 0;}
          好家伙,這么下去,我是不是可以這么干
          // data.txt1,2,3,4,5,6,7,8,9
          // main.cint arr[] = {    #include "data.txt"}
          int main(void){ return 0;}
          然后,你又好奇了,能不能將data.txt換成二進制形式的data.bin?
          呵呵,這種不行,編譯器在預編譯階段只認得是text文本才行。
          好吧……
          你不是說這是個預編譯指令嗎,我很好奇,#include預編譯后成啥樣子的?
          這好辦,動動手指頭,一個gcc -E命令即可搞定。就以上面第一個例子,命令行執(zhí)行gcc ./main.c -E -o main.i
          # 0 ".\\main.c"# 0 "<built-in>"# 0 "<命令行>"# 1 ".\\main.c"
          # 1 "add.h" 1extern int add(int a, int b);# 3 ".\\main.c" 2# 1 "minus.c" 1int minus(int a, int b){ return a-b;}# 4 ".\\main.c" 2
          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進來。就這么簡單粗暴。
          那么#include在C語言中是不是很簡單?
          你說呢!
          我見過有人這么寫代碼的,還TM的一整個團隊是這么做的。
          將整個所以.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的簡便
          我第一次見到這玩意,簡直是驚呆了,還有這種操作。
          不好嗎?有什么不好?多簡潔?。?br>
          從上面的分析看,#include就是將它后面包含的頭文件源文件,全部展開哦。
          簡潔?你問過編譯器啥感受么?
          帶來的最直接的感受是,編譯過程慢!includes.h里包含得越多就越慢!
          另外一個隱含的問題是,會造成include里的內(nèi)容混亂,頭文件里的內(nèi)容全部是全局的了。
          我絕對不推薦這種玩法的。
          因為,預編譯還有更好玩的玩法。
          不過,在介紹新玩法之前,得想個問題,如果一個頭文件,重復包含多次會怎樣?
          也許,你會回答,我是不允許出現(xiàn)這種情況的,就算出現(xiàn)這種情況,我也可以用#ifdef...#endif這種方式規(guī)避。
          如果你是應屆生面試,這樣回答,面試官也許是點點頭說你有點經(jīng)驗的。
          因為重復include,就相當于把頭文件重復展開了多次,C語言中有些定義是不允許重復多次的。例如,上面的例子
          // main.c#include "add.h"#include "minus.c"#include "minus.c"
          這樣是有問題的,因為上面相當于重復定義了兩次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改成這樣就行了
          #ifndef _MINUS_#define _MINUS_int minus(int a, int b){    return a-b;}#endif
          這個簡單啊,我也會啊。
          嗯,但是,我不是想說這個,我真的想說重復include有意想不到的好處呢。
          這就不得不提下,我以前寫的X-MACRO大法了。
          以下是一個MEMORY字段分配的設想:
          1. Memory

          2. MemoryBlock0

          3. 數(shù)據(jù)

          4. 內(nèi)

          5. 據(jù)Memory

          我想定義一些內(nèi)容條目,這些條目分別對應不同的內(nèi)存地址,不同的長度,以后有需要還可以繼續(xù)從后面添加就這樣:
          entry name address
          size
          ID_DATA1 0
          8
          ID_DATA2 8
          8
          ID_DATA3 16
          16
          ...


          可以在一個頭文件里面做這樣的定義
          // defines.h#ifdef ENTRY_ID  #define ENTRY(id,addr,size) id,  #undef ENTRY  #undef ENTRY_ID#endif
          #ifdef ENTRY_ADDR #define ENTRY(id,addr,size) addr, #undef ENTRY #undef ENTRY_ADDR#endif
          #ifdef ENTRY_SIZE #define ENTRY(id,addr,size) size, #undef ENTRY #undef ENTRY_SIZE#endif
          接著在C文件里面這么玩?
          // memory.c#define ALL_ENTRIES()       \    ENTRY(ID_DATA1, 0, 8)   \    ENTRY(ID_DATA2, 8, 8)   \    ENTRY(ID_DATA3, 16, 16) \    ENTRY(ID_DATA4, 32, 8)
          #define ENTRY_ID#include "defines.h"typedef enum{ ALL_ENTRIES() MEM_ID_MAX} MEM_ID;
          #define ENTRY_ADDR#include "defines.h"const uint32_t mem_addr[] ={ ALL_ENTRIES()};
          #define ENTRY_SIZE#include "defines.h"const uint16_t mem_size[] ={ ALL_ENTRIES()};
          你也許會反問我,定義一個結構體不就搞定了嗎?
          別急,這樣做的好處是enum的ID順序跟addr和size是一一對應的,不會錯亂,另一個好處是,可以隨便在ALL_ENTRIES()下面擴展條目,也不影響ID的對應關系。
          如果用結構體去定義的話,也很好,但是會增加數(shù)組遍歷時間,如果是很龐大的條目數(shù)的話,這個效率問題就要考慮了。
          其實,對上面的做法,我還做了優(yōu)化,寫在了這兩篇文章中,X-MACRO是個很酷的玩法哦,歡迎查閱和討論。
          如果你喜歡我的文章,請關注,并轉發(fā)、點贊在看,這是對我莫大的鼓勵!


          瀏覽 2988
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲成AV人影院在线观看 | 奇米影视亚洲色图 | 俺也去av| 日韩有码日韩无码 | 日韩mv欧美mv国产mv |