0. 序 我從一生下來就呆在這個昏暗的地方。 我不明白為什麼程式員這麼喜歡 Dark Mode,Brighten Mode 才是我的最愛。聽說最近連 iphone 都開始支持 Dark Mode 了,沒話講。。。說好的絕不妥協呢? 我周圍是熙熙攘攘的函數群,穿插著變數聲明和巨集定義。 在我們這裡,函數 ...
0. 序
我從一生下來就呆在這個昏暗的地方。
我不明白為什麼程式員這麼喜歡 Dark Mode,Brighten Mode 才是我的最愛。聽說最近連 iphone 都開始支持 Dark Mode 了,沒話講。。。說好的絕不妥協呢?
我周圍是熙熙攘攘的函數群,穿插著變數聲明和巨集定義。
在我們這裡,函數是一等公民。
當然,不光在 C++,在面向過程的 C 語言、面向對象的 Java ,尤其是在那些函數式編程的語言里,我們都扮演著舉足輕重的角色。
能力越大,責任越大。我和一群函數伙伴們就負責維護著程式的功能。每個函數的一小步,合起來就是功能模塊的一大步。
作為一門靜態編譯型語言,我們不像那些解釋語言一樣,寫完就能直接運行,而是要先經過編譯這一道坎,成為機器語言,才能夠運行在我們賴以生存的機器上。
這道坎不是那麼好過的,再頂尖的程式員,也會在這上面栽跟頭。
放在往常,雖然程式偶爾會出 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號警告信息作為一個錯誤’ 。可以說,她是程式員和編譯器之間的信鴿。
對於我來說,預編譯階段是比較輕鬆的,最複雜也只是處理條件預編譯指令——刪除幾行代碼罷了。
未完待續
如果大家對文章有什麼看法和意見,歡迎提出來~