你有沒有想過,當你寫好一段程式碼要讓電腦執行的時候,電腦到底是怎麼讀懂你寫的東西的?
其實,電腦並不認得我們寫的程式碼。
電腦唯一認得的語言叫做「機器碼」(Machine Code),也就是一堆 0 和 1 組成的指令。
但我們寫程式的時候,用的是人看得懂的「原始碼」(Source Code),像是 if、for、print 這些英文單字。
電腦看不懂這些東西,所以原始碼必須先被轉換成機器碼,電腦才能執行。
而這個轉換的過程,主要有三種做法:編譯(Compile)、直譯(Interpret),以及結合兩者的混合型(JIT Compilation)。
這篇文章會用一個簡單的情境,帶你搞懂這三種方式的差別、各自的優缺點,以及實際上哪些語言用哪種方式。
想像一個簡單的場景:你寫了一支程式,想交給另一個人來執行,而對方有自己的電腦。
這時候,根據你用的語言不同,交付程式的方式也不一樣。
Compiled Language:編譯語言的做法
你先在自己的電腦上寫好原始碼。
接著,你用一個叫做「編譯器」(Compiler)的程式,把整份原始碼一次性全部轉換成機器碼,產出一個獨立的檔案。
這個過程就叫做「編譯」(Compile)。
編譯完成後,會產出一個叫做「可執行檔」(Executable)的東西,例如 Windows 上常見的 .exe 檔。
這個可執行檔裡面裝的就是機器碼,電腦可以直接讀懂、直接執行。
所以你只要把這個可執行檔傳給對方就好,對方的電腦收到後可以直接執行。
整個過程中,你的原始碼一直留在你手上,對方拿到的只是編譯後的結果,完全看不到你的程式邏輯。
簡單來說,編譯語言的流程是:原始碼 → 編譯器 → 可執行檔 → 直接執行。
轉換的工作在「你那邊」就做完了,對方不需要做任何轉換。
Interpreted Language:直譯語言的做法
直譯語言完全不同。
你不需要事先把原始碼轉換成任何東西,直接把原始碼本身交給對方。
但對方的電腦上需要裝一個叫做「直譯器」(Interpreter)的程式。
當我要執行你的程式時,直譯器會逐行讀取你的原始碼——讀一行、轉換一行、執行一行,然後再讀下一行。
它不會像編譯器那樣一口氣把整份程式碼轉完,而是邊讀邊跑,所有的轉換都在「執行的當下」即時發生。
直譯器也不會另外產出一個機器碼檔案,程式跑完就結束了,下次要跑的時候再重新逐行直譯一遍。
簡單來說,直譯語言的流程是:原始碼 → 直譯器 → 逐行轉換並執行。
轉換的工作在「對方那邊」才發生,而且每次執行都要重來一次。
Compiled Language 編譯語言的優缺點
優點
執行速度快
因為機器碼已經在你的電腦上事先轉好了,執行的時候完全不需要再做任何轉換,直接跑就行。
而且編譯器在轉換的過程中,還可以針對特定的 CPU 架構做「最佳化」(Optimization)——它會分析你的程式碼,找出可以更有效率執行的寫法,自動幫你調整。
這就像是翻譯一本書的時候,譯者不只是逐字翻譯,還會潤飾語句讓讀起來更順暢。
所以編譯語言在執行速度上通常會比直譯語言快不少。
方便大量發佈
編譯好的可執行檔就是一個獨立的檔案,不依賴任何額外的工具。
你可以把它傳給一百個、一千個、甚至十萬個人,每個人拿到就能馬上執行,不需要額外安裝任何東西。
原始碼不外流
你發佈的是可執行檔,不是原始碼。
對方拿到的只是一堆機器碼,就算打開來看也只會看到一堆 0 和 1,完全看不懂你的程式邏輯。
這對商業軟體來說很重要——你可以賣軟體給別人用,但不需要讓別人知道你是怎麼寫的。
缺點
跨平台很麻煩
編譯器在轉換的時候,會針對特定的作業系統和 CPU 架構來產出機器碼。
這表示在 Windows PC 上編譯出來的 .exe 檔,拿到 Mac 上就完全跑不了,因為兩邊的作業系統和 CPU 指令集不一樣。
如果你想讓程式在 Windows、Mac、Linux 三個平台上都能跑,你就得分別編譯三次,產出三個不同的可執行檔。
甚至同一個作業系統上,不同型號的 CPU(例如 Intel 和 ARM)也可能需要各編譯一次。
開發測試多一道手續
在開發的過程中,工程師經常需要改一小段程式碼,然後馬上跑起來看結果對不對。
但用編譯語言的話,每次改完都要先等編譯器跑一遍,把整份程式碼重新轉換成機器碼,才能看到結果。
如果程式很大,編譯一次可能要花好幾秒甚至好幾分鐘。
這個等待時間在頻繁測試的時候會不斷累積,拖慢整體的開發速度。
Interpreted Language 直譯語言的優缺點
優點
跨平台很方便
因為你發佈的是原始碼本身,不是針對特定平台轉換過的機器碼。
只要對方的電腦上有裝那個語言的直譯器,不管對方用的是 Windows、Mac 還是 Linux,都能跑你的程式。
你不需要為每個平台各準備一份,一份原始碼走天下。
開發測試更快
改完程式碼之後,直接交給直譯器跑就好,不需要等待編譯的步驟。
這讓「改一行、跑一次、看結果」的循環變得非常快,對開發效率很有幫助。
特別是在學習程式設計的階段,這種即時回饋的體驗會讓你更容易理解自己寫的東西到底做了什麼。
除錯比較容易
因為你隨時都能看到完整的原始碼,當程式出錯的時候,直譯器會告訴你「第幾行出了什麼問題」,你可以直接打開原始碼對照著看。
相比之下,編譯語言出錯的時候,如果你拿到的是別人的可執行檔,你根本看不到原始碼,也就很難找出問題在哪裡。
缺點
對方必須安裝直譯器
這是編譯語言不會遇到的問題——編譯好的可執行檔拿到就能跑,但直譯語言不行。
每個要跑你程式的人,電腦上都得先裝好那個語言的直譯器。
例如要跑 Python 程式,對方電腦上就得先安裝 Python 的直譯器。
執行速度比較慢
因為直譯器是在執行的當下才逐行做轉換,等於每次跑程式都要重新翻譯一遍。
而編譯語言只需要翻譯一次,之後每次執行都是直接跑已經翻譯好的機器碼。
這個差異在小程式上可能感覺不出來,但在需要大量運算的場景(像是遊戲引擎、影片處理)就會很明顯。
原始碼會曝光
因為你發佈的就是原始碼本身,所有拿到的人都能直接打開來看你的程式邏輯。
如果你的程式裡有獨特的演算法或商業邏輯,就等於直接公開給所有使用者了。
第三種方式:JIT Compilation 混合型編譯
既然編譯和直譯各有優缺點,那能不能把兩者的好處結合起來?
可以,這就是第三種方式——混合型。
前半段:先編譯到「一半」
混合型的第一步,跟編譯語言很像——你會先用一個編譯器把原始碼做轉換。
但不同的是,它不會直接轉換成特定平台的機器碼,而是轉換成一種叫做「中間語言」(Intermediate Language)的東西,也叫做「位元組碼」(Bytecode)。
你可以把中間語言想成是「翻譯到一半」的狀態——它已經不是人類看得懂的原始碼了,但也還不是某台特定電腦能直接跑的機器碼。
它介於兩者之間,已經做了很多前置處理,但刻意保留了跨平台的彈性。
後半段:對方的電腦完成最後一哩
你把這個中間語言發佈出去。
每個人拿到之後,自己電腦上的「執行環境」(Runtime Environment)會負責把中間語言轉換成當地電腦能跑的機器碼。
這個「最後一哩」的轉換,就叫做 JIT 編譯(Just-In-Time Compilation,即時編譯)。
「Just-In-Time」的意思是「就在需要的那一刻才做」——不是事先全部轉完,而是程式跑到哪一段,就轉換那一段。
為什麼這樣做比較好?
跟純編譯比,混合型不需要為每個平台各編譯一次,一份中間語言就能在不同平台上跑。
跟純直譯比,混合型的前半段已經先做過處理了,所以後半段的 JIT 轉換速度會比從原始碼逐行直譯快很多。
簡單來說,混合型的流程是:原始碼 → 編譯器 → 中間語言(Bytecode) → JIT 編譯 → 機器碼 → 執行。
前半段轉換在你那邊做,後半段轉換在對方那邊做,兩邊各分擔一部分工作。
常見程式語言是 Compiled、Interpreted 還是 Hybrid?
了解了三種方式之後,你可能會好奇:那我們常聽到的程式語言,分別是哪一種?
編譯語言(Compiled)
C、C++、Objective-C 這些語言是典型的編譯語言。
你寫好原始碼之後,需要用編譯器把它轉換成可執行檔,才能執行。
編譯器可以免費下載,也常常內建在 IDE(整合開發環境,Integrated Development Environment)裡面。
IDE 就是工程師寫程式用的專業軟體,像是 Visual Studio、Xcode 這些工具,裡面同時包含了程式碼編輯器和編譯器,讓你在同一個地方寫程式、編譯、測試。
直譯語言(Interpreted)
PHP、JavaScript,以及大部分名稱裡有「Script」的語言,通常是直譯執行的。
其中 JavaScript 你一定用過——只要你打開瀏覽器上網,瀏覽器就在幫你直譯 JavaScript。
當你造訪一個網頁,伺服器會把 HTML、CSS、圖片,還有 JavaScript 的原始碼一起傳到你的電腦上。
你的瀏覽器收到之後,就用內建的直譯器即時執行它。
這就是為什麼網頁可以有動態效果、可以跟你互動——背後都是 JavaScript 在運作。
你不需要額外安裝任何東西,因為瀏覽器本身就內建了 JavaScript 的直譯器。
混合型語言(Hybrid)
Java、C#、VB.NET、Python 這些語言採用混合型的做法。
以 Java 為例,你寫好的原始碼會先被編譯成 Bytecode,然後由 Java 虛擬機(JVM,Java Virtual Machine)在執行時做 JIT 編譯。
只要對方的電腦上裝了 JVM,不管是 Windows、Mac 還是 Linux,都能跑同一份 Bytecode。
這也是 Java 當年喊出「Write Once, Run Anywhere」(寫一次,到處跑)口號的原因。
三種方式的比較
Compiled vs Interpreted:選語言時不用太糾結
看完這篇文章,你可能會想:「那我應該選編譯語言還是直譯語言?」
答案是——這通常不會是你選語言的主要理由。
確實,如果你的程式追求極致的執行速度,而且只需要在單一平台上跑,那編譯語言可能比較適合。
例如遊戲引擎、作業系統核心、嵌入式裝置上的程式,這些場景對效能的要求非常高,所以大多使用 C 或 C++ 這類編譯語言。
如果你希望程式能輕鬆在不同平台上執行,直譯語言或混合型語言會更方便。
例如網頁應用幾乎都用 JavaScript,因為每個人的瀏覽器都內建了直譯器,不需要使用者額外安裝任何東西。
但實際上,多數時候你是先決定「我要做什麼」,語言的選擇自然就跟著出來了。
想做 iPhone App?你會用 Swift(編譯語言)。
想做網頁前端?你會用 JavaScript(直譯語言)。
想做後端伺服器?你可能會用 Java 或 C#(混合型語言),也可能用 Python 或 Node.js。
想先學程式設計的基礎?Python 是目前最多人推薦的入門語言,它的語法簡潔、即時回饋快,很適合新手上手。
你會發現,在這些決策過程中,「這個語言是編譯還是直譯」從來不是第一個考慮的問題。
決定了目標之後,適合的語言自然就浮現了,而那個語言用的是哪種執行方式,就跟著走就好。
理解編譯和直譯的差別,不是為了幫你選語言,而是讓你在未來學習的路上,遇到「為什麼要裝這個東西」「為什麼跑不了」「為什麼這麼慢」這些問題的時候,能更快找到答案。