為何有些專案要用多種程式語言?
從網站開發到作業系統核心,許多軟體都巧妙地融合了多種語言。有些情況很直觀,例如網頁前後端分離;但有些情況則更為深入,多種語言被編譯成單一的執行檔。本頁將帶您探索這背後的底層秘密。
編譯的真相:不只是個黑盒子
我們常說「編譯器將原始碼變成執行檔」,但這其實是一個精細的多步驟過程。了解這個過程,是理解多語言混合的關鍵第一步。點擊下方各階段,看看一段簡單的C語言程式碼是如何一步步轉換的。
輸出結果:
階段說明:
連結的魔法:靜態 vs. 動態
「連結」是編譯流程的最後一步,它負責將所有需要的程式碼片段組合成一個完整的執行檔。主要有兩種方式,它們在效率和彈性上有著截然不同的取捨。
- ✔
- ✘
混合語言實戰
正因為編譯流程是模組化的,且最終都由「連結器」來組合,我們才有可能混合不同語言。只要每種語言都能被編譯成連結器看得懂的「目的檔」,它們就能合作。
範例一:C + 組合語言
這是最常見的組合。當對效能有極致要求時,開發者會用組合語言手寫最核心的計算部分,然後由C語言來呼叫它,處理其他邏輯。
範例二:C + Rust
高階語言之間也可以混合。例如,我們可以利用Rust的記憶體安全特性來編寫一個函式庫,然後在現有的C專案中呼叫它,兼顧安全與效能。
溝通的規則:應用程式二進位介面 (ABI)
能連結在一起還不夠,不同語言編譯出來的機器碼還必須遵守相同的「溝通規則」,才能正確地呼叫彼此。這個規則就叫做 ABI,它定義了函式呼叫時參數如何傳遞、回傳值放哪裡等底層細節。
情境一:ABI 不匹配
語言A認為參數要放在暫存器0和1,但語言B卻期望從暫存器1和2讀取。這種誤會會導致程式執行錯誤,甚至崩潰。
情境二:ABI 一致 (例如都遵守C ABI)
透過 `extern "C"` 等關鍵字,我們可以指示編譯器遵循一個共同的標準ABI。這樣,雙方就能正確無誤地溝通。
那麼,為何要這麼做?
混合多種語言雖然增加了複雜度,但在特定情境下能帶來巨大好處,這是一種工程上的權衡取捨。
極致的效能
用高階語言快速開發大部分功能,再將效能瓶頸部分用C或組合語言改寫,達到兩全其美。
利用現有生態系
許多成熟、穩定、高效的函式庫都是用C寫的。新語言可透過 FFI 直接呼叫這些函式庫,無需重造輪子。
使用最適合的工具
不同的語言有不同的專長。例如,用Python做數據分析,用C++做圖形渲染,並讓它們在同一個專案中協同工作。