函數,從編輯到編譯 (上) --帶你瞭解預編譯作了什麼

0. 序

我從一輩子下來就呆在這個昏暗的地方。linux

我不明白爲何程序員這麼喜歡 Dark Mode,Brighten Mode 纔是個人最愛。據說最近連 iphone 都開始支持 Dark Mode 了,沒話講。。。說好的毫不妥協呢?c++

我周圍是熙熙攘攘的函數羣,穿插着變量聲明和宏定義。程序員

在咱們這裏,函數是一等公民。express

固然,不光在 C++,在面向過程的 C 語言、面向對象的 Java ,尤爲是在那些函數式編程的語言裏,咱們都扮演着舉足輕重的角色。編程

能力越大,責任越大。我和一羣函數夥伴們就負責維護着程序的功能。每一個函數的一小步,合起來就是功能模塊的一大步。windows

做爲一門靜態編譯型語言,咱們不像那些解釋語言同樣,寫完就能直接運行,而是要先通過編譯這一道坎,成爲機器語言,纔可以運行在咱們賴以生存的機器上。iphone

這道坎不是那麼好過的,再頂尖的程序員,也會在這上面栽跟頭。函數式編程

放在往常,雖然程序偶爾會出 bug ,但你們齊心合力,可謂蟲(bug)擋殺蟲,過五關斬六將,整個程序也稱得上是層次分明。函數

但此次,咱們遇到了大問題。調試

1. 預編譯

今天的一切看起來都很平凡,至少我是這麼認爲的。

屏幕外的程序員像日常同樣敲着代碼,咱們像日常迎接着新函數的到來,像日常同樣嬉笑怒罵,像日常同樣期待着預編譯進程的到來。

預編譯進程是整個編譯進程的先鋒。

像往常同樣,咱們從磁盤出發,沿着總線來到了內存。這裏就是進程的工做車間。

預編譯進程第一步會 刪除全部 #define,展開宏定義。處理條件預編譯指令。

#define WINDOWS
#define BUFSIZE 1024
#define DEPTH 4
#define DECODE "utf-8"
...

上面的就是宏定義,每次咱們都要在預編譯進程的指揮下,把語句裏出現的宏替換成對應的值。

這一步其實原本不須要咱們乾的,程序員怕麻煩,想要作到「一處修改,到處更改」,就發明了宏定義,讓編譯器來幹這些「髒活累活」。

處理條件預編譯指令就有點不同了:

//windows or linux
#ifdef WINDOWS
<experssion1>
#else
<experssion2>
#endif

若是宏定義有這個 WINDOWS,就只留下 <experssion1>,沒有的話就留 <experssion2> 。說白了,就是個預編譯階段能執行的 if... else ... 語句。上面的語句一處理,就變成了:

<experssion1>

對,註釋也會被刪除。

可憐那些註釋,這一生都未曾領略 CPU 裏的風景。

第二步是處理 #include預編譯指令。

這一步就比上面的複雜多了。用專業的話來講,處理 「#include 」預編譯指令,就是將被包含的文件插入到該預編譯指令的位置。這個過程是遞歸進行的,也就是說被包含的文件可能還包含其餘文件。

#include "config.h"
<expressions>

別看他們如今就只有短短兩句,等把 config.h文件內容複製過來,信息量一會兒就大了。

#ifndef _CONFIG_H_
#define _CONFIG_H_

#define VERSION "1.0.0"
#define MODE 1
...
...
#endif

<expressions>

補充一句,這個 .h 後綴的傢伙,叫頭文件。他是咱們與其餘文件的函數公民的溝通渠道。

頭文件這個傢伙和源文件不太同樣,他是包含功能函數、數據接口聲明的載體文件,主要用於保存程序的聲明。也就是說,頭文件裏是沒有函數的——咱們曾屢次試圖佔領頭文件的領地,但都沒有成功——都是由於程序員的約束。

每一個頭文件都會帶有一組條件預編譯語句,用來防止本身被屢次編譯。

至於怎麼作到的,這太簡單了,我不說你也能想出來。

據說有的編譯器還支持 #pragma once ,添在頭文件第一行就能作到相同的事情。惋惜咱們的編譯器有點舊,不兼容他們。

最後這步就比較快了,添加行號和文件名標識

走到這裏,咱們已經獲得了編譯器調試的須要的行號信息,若是編譯到哪一步出錯,或者出現 warning 這樣的警告,就能把行號顯示出來,方便程序員及時發現問題源頭。

今天的預編譯比我想象中要快一點,可能此次沒什麼進程跟咱們搶 CPU 資源吧。

預編譯階段結束,# 的數量大大減小,僅剩下幾個 #pragma 指令留在這裏。

和其餘宏定義指令不同的是,#pragma 是可以跟編譯器分庭抗禮的存在,預編譯進程見了都得避讓三分。

#pragma  warning( disable: 4507 34; once: 4385; error: 164 )

像這條指令,就是專門給編譯器看的,意思是 ‘不顯示4507和34號警告信息 ,4385號警告信息僅報告一次,把164號警告信息做爲一個錯誤’ 。能夠說,她是程序員和編譯器之間的信鴿。

<!--要注意的是,這裏面的第幾步第幾步是不嚴謹的,預編譯沒有劃分這樣的界限-->

對於我來講,預編譯階段是比較輕鬆的,最複雜也只是處理條件預編譯指令——刪除幾行代碼罷了。

未完待續

若是你們對文章有什麼見解和意見,歡迎提出來~

相關文章
相關標籤/搜索