Windows 求生手冊:C 和 C++

在前文中,我們介紹數個應用程式語言,不過,我們有時候仍然需要 C/C++。即使我們不寫 C/C++ 程式,也會要用以 C/C++ 寫成的延伸模組。雖然我們平日都使用腳本語言從事各種任務,必要時還是可以用 C/C++ 撰寫延伸模組,在關鍵步驟為程式加速。大部分高階語言都有 C API,可以用 C/C++ 撰寫延伸模組。使用 SWIG 也可以省下一些撰寫 binding 的功夫。近年來,許多高階語言提供 FFI (foreign function interface) 的機制,撰寫延伸模組的過程又更加簡化。

C 和 C++ 編譯環境可以分為以下三部分:

  • Compiler (編譯器)
  • Toolchain (工具鏈),即自動化工具
  • Library (函式庫)

C 和 C++ 有國際標準,而這兩個語言近年來不斷翻新,C 有 C99 和 C11,而 C++ 有 C++11、C++14、C++17 等各種版本。但 C/C++ 沒有所謂的官方版本編譯器,不同實作品的支援程度不一;通常不會在標準出來時馬上實作全部的特性,而會逐漸加入各種特性,需要查閱各個軟體的相關資料。其實沒有實作全部的特性不一定會對專案造成影響,寫程式時不一定會用到新功能。

由於不同編譯器的 ABI (Application Binary Interface) 不相容,在專案一開始就要選擇想用的編譯器。在 Windows 上最常見的 C 和 C++ 編譯器,除了 Microsoft Visual C++ 外,就是 MinGW (GCC 的 Windows 移植品);由於這兩套編譯器的 ABI 不相容,所産生的函式庫不會混合使用,而會選擇其中一個系統,之後編譯專案的程式碼時,固定選擇該系統産生的機械碼。

隨著專案規模變大,每次編譯時都自行輸入指令較沒效率,發布專案時,讓使用者自行輸入指令來編譯軟體也不符實情。實際上,會使用工作流程自動化工具來簡化編譯軟體的動作。C 和 C++ 沒有所謂的官方指定自動化軟體,不同平台各自發展出不同的方案。在類 Unix 系統,最典型的工具就是 make,後來 GNU 又進一步發展出 Autotools 這套自動化軟體。在 Visual Studio 中,則將流程自動化整合至其開發環境中。但這兩者是各自獨立的,奱成同一份專案要建置兩套編譯設定檔。後來針對這個問題,出現其他新的自動化軟體,像是 CMake 這套跨平台的編譯工具。而 Premake 是一個小型專案,透過撰寫 Lua 命令稿,可轉換成 GNU make 和 Visual Studio 適用的設定檔。

在撰寫程式時,通常不會只用標準函式庫所提供的功能,而會使用第三方函式庫。C 或 C++ 本身沒有既定的套件形式,在不同平台上有不同的方式來因應。在 Linux 和 BSD 上使用系統套件管理程式來因應這個議題,像是 Linux 上以 dev 或是 devel 命名的套件就是 C/C++ 的函式庫標頭檔。Mac 沒有內建的套件管理程式,會用第三方套件管理程式簡化套件管理的工作,像是 Homebrew 等。而 Windows 系統較缺乏固定的套件管理程式,有些函式庫提供安裝程式,有時就是要手動將函式庫加入專案中。後來有 NuGet 這套套件管理程式,解決一部分套件管理的功能,但很多函式庫仍然無法以 NuGet 安裝。

在類 Unix 系統中,C/C++ 編譯環境幾乎是內建的功能,如果要安裝也相當容易。而在 Windows 上則要自行建置環境。開發工具的部分較容易處理,安裝 MinGW 即可。只有編譯器和標準函式庫能從事的任務相對受限,通常也會需要函式庫,而函式庫則是較為困難的部分,有許多函式庫是以原始碼的形式發布,而 Windows 不支援 Autotools 等編譯工具。另外,有些函式庫用到 POSIX 系統特有的功能,在 Windows 上則無法使用。為了順利在 Windows 上使用 Unix-like 系統下的函式庫,於是有 MSYS 計畫。

MSYS 其實有點像是前面章節中提到的 Cygwin,是一個小型的類 Unix 環境,可以視為一個子系統。但和 Cygwin 的目標略有不同,MSYS 不是拿來從事日常生活上的任務,而是提供一個編譯程式的環境,所以,MSYS 內的工具不會像 Cygwin 裡那麼齊全,而是以編譯軟體相關的工具為主。MSYS 編譯出來的軟體是原生的 Windows 應用程式和函式庫,而不像 Cygwin 編譯出來的軟體只能在 Cygwin 環境中使用。

原本的 MSYS 僅提供編譯軟體的環境,但在 MSYS 中編譯軟體不是很容易的過程,要耗費許多時間,也很容易失敗。後來出現 MSYS2 計畫,這個計畫將 MSYS 重寫,除了原先 MSYS 的功能外,也引入 ArchLinux 使用的套件管理程式 pacman,還預先編譯一些常見的函式庫,節省使用者自行編譯函式庫的時間。必要時,也可下載 C/C++ 相關的開發工具,自行編譯其他函式庫。

使用 MSYS2 時要注意,MSYS2 的軟體分為兩種,一種是原生的 Windows 環境下的軟體,一種是 MSYS 環境內的軟體。MSYS2 的軟體共有三種字,分別為 mingw32、mingw64 和 msys,第一種是適用於 32 位元的原生 Windows 軟體,第二種是適用於 64 位元的原生 Windows 軟體,最後一種則是適用於 MSYS 子系統的軟體。由於我們使用 MSYS 的目標是編譯 Windows 原生程式,編譯時應使用 mingw32 或 mingw64 開頭的開發工具為主,而 msys 開頭的軟體主要是用來執行 Autotools 和其他編譯軟體時會用到的工具。MSYS2 內也提供 MinGW,不需從其他來源安裝。

根據自己實際的情況,可選擇 32 位元或是 64 位元的 MSYS2 系統。如果使用 64 位元的系統,即使是要編譯 32 位元的軟體,也不需要刻意選擇 32 位元的 MSYS2 系統,64 位元的 MSYS2 系統即可編譯 32 或 64 位元的軟體。通常在安裝時,會選擇單純的路徑,像是 C:\msys32 或是 C:\msys64 等,以避免不可預期的錯誤。

安裝完後,可在 DOS 環境下輸入 msys2 即可進入 MSYS 子系統,或是從圖形介面按連結開啟該系統。要注意的是,MSYS 子系統的使用方式類似於 Cygwin 而和 DOS 環境不同。