函數,從編輯到編譯 (上)
- 2019 年 10 月 3 日
- 筆記
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號警告資訊作為一個錯誤』 。可以說,她是程式設計師和編譯器之間的信鴿。
對於我來說,預編譯階段是比較輕鬆的,最複雜也只是處理條件預編譯指令——刪除幾行程式碼罷了。
未完待續
如果大家對文章有什麼看法和意見,歡迎提出來~