1. 程式人生 > >Mac 下的 C++ 開發環境

Mac 下的 C++ 開發環境

log 同時 文件包含 修改 專註 strong 2.3 evel 當前

1. Xcode 創建 C++ 項目

Xcode (版本 4.6.3)默認支持創建 C++ 項目,步驟很簡單:
打開 Xcode,新建一個項目;
在 OS X 中的 Application 中選擇 Command Line Tool;

技術分享

然後,在接下來的項目設置中,將 Type 設置為 C++;

技術分享

如果你不需要用到 ARC 的話,取消勾選(C++ 裏用不到);
這樣,就在 Xcode 裏創建了一個基本的 C++ 項目,Run :

技術分享

需要註意的是, Xcode 默認的 C++ 編譯器是 Apple LLVM Compiler(4.6.3 的版本為 4.2)也就是 Clang,默認的 C++ 標準庫 為 libc++(Clang 提供的標準庫),目前已經支持 C++ 11 (先前的 C++0x)標準。Xcode 同時也提供 LLVM GCC 編譯器,其附帶的 C++ 標準庫為 libstdc++(GNU C++ 標準庫)。在引入一些第三方庫的時候,需要註意這些選擇,如 OpenCV,可能就需要將標準庫設置為 libstdc++。
IDE 由於集成了如文檔、自動補全、可視化的環境配置等特性,很方便用於項目開發,但如果不用 IDE ,那怎樣直接使用命令行編譯 C++ 項目?很簡單,直接使用編譯器。

2. C++ 編譯過程

在介紹編譯器之前,先簡單地說一下 C++ 的編譯過程,以便理解編譯器的工作。
編譯(compiling)並不意味著只創建僅僅一個可執行文件。創建一個可執行文件是一個多級過程,其中最重要的過程是預處理(preprocessing),編譯(compliation)和鏈接(linking)。從源代碼文件到一個可執行文件的整個過程,最好的說法是 build(中文翻譯的話,有叫生成,有叫編譯鏈接,也有叫構建)。compiling 僅僅是 build 過程的一部分,但你經常會碰到許多人把 compile 指代整個過程。通常情況下,你不需要為這幾個過程運行單獨的命令,編譯器自己會調用,如預處理器。

2.1 預處理

build 過程的第一步就是編譯器運行 C 預處理器,目的是對代碼文件進行文本上的處理。它會處理頭文件包含指令(#include),條件編譯指令(#ifdef……#endif)和宏(#define),這些指令叫做預處理指令,都以井字符 # 開頭。編譯器本身是絕對看不到這些預處理指令的。
比如:

  1. #include <iostream>

這句代碼會告訴預處理指令,要把 iostream 的文件內容抓去到當前文件,你每包含一個頭文件,它就會把這個頭文件的內容粘貼到這個文件中,然後把 #include 指令移除。

  1. #define MY_NAME "Alex"

宏就是一個被其它內容(可能比較復雜)替換掉的字符串內容,此時預處理器會把下面的代碼:

  1. cout << "Hello" << MY_NAME << endl;

展開成:

  1. cout << "Hello" << "Alex" << endl;

由於預處理器在編譯器之前處理代碼,它也可以用來移除代碼——有時,你會要在代碼裏執行某些測試代碼。你可以告訴預處理器,如果定義了某個宏,則包含某些代碼。然後,如果你想執行這個代碼,就定義這個宏,否則就移除掉這個宏的定義。

#include <iostream>  
#define DEBUG  
using namespace std;  
int main()  
{  
int x;  
int y;  
cout << "Enter value for x: ";  
cin >> x;  
cout << "Enter value for y: ";  
cin >> y;  
x *= y;  
#ifdef DEBUG  
cout << "x: " << x << ‘\n‘ << "y: "<< y;  
#endif   
}  

如果你不想執行變量的打印,那麽只需簡單註釋掉 #define DEBUG 就行。
同樣地,你也可以用 #ifndef 來改變條件——如果沒有定義……這個方法通常用在引入多個頭文件的時候。

2.2 編譯

編譯意味著把一個源文件(.cpp)轉變成一個對象文件(object,.o 或 .obj)。
一個對象文件會把你程序裏的每一個函數,封裝成一個計算機處理器能理解的形式——機器指令(machine language instructions)。每一個源文件都是單獨編譯過的,即對象文件包含的機器代碼都是編譯過的源代碼。比如,你有三個源文件,經過編譯,生成了三個對象文件,每一個對象文件都包含了各自對應的機器代碼。
但你還不能運行它們,這時候,就需要鏈接器了。

2.3 鏈接

鏈接(Linking),是把一堆對象文件和庫(有時也可能僅僅是一個對象文件,但也需要鏈接)創建成一個單獨的可執行文件(比如 .exe 或 .dll)。
鏈接器通過一種適當的格式創建一個可執行的文件,並傳遞每個獨立的對象文件內容到一個可執行的結果。鏈接器也處理含有對象文件源代碼之外的其它函數的引用,比如 C++ 標準庫裏的函數。當你調用了一個 C++ 標準庫的函數,如 cout << “Hi”,你就在使用一個自己代碼中沒有定義的函數,它被定義在一個相關的對象文件中,但這是由編譯器提供的,並不屬於你。在編譯時,編譯器知道這個函數是有效的,因為你引出了 iostream 頭文件,但由於這個函數不是 cpp 文件的一部分,編譯器就會在調用樹(call tree)留下一個存根(stub),鏈接器會遍歷對象文件,針對每一個存根,它會找到正確的函數地址,然後從已鏈接過的其它對象文件中,用正確的地址替換掉對應的存根。
這個過程有時也叫做修正(fixup)。當你把你的程序分離成多個源文件時,你就會利用鏈接器來修正所有在源文件中調用過的函數。如果鏈接器找不到這個函數的位置,它就會生成一個 undefined function error,即便代碼被編譯器通過了,也不意味著代碼是正確的。鏈接器是首先以全局的視角來探測這種錯誤的。

3. GCC 和 LLVM

Mac 下,如果你安裝了 Xcode ,那麽你就擁有了 LLVMGCC 兩大編譯工具。
3.1 LLVM
LLVM,原來叫做 「Low Level Virtual Machine」,該項目的領導者和最初作者是 Chris Lattner 和 Vikram Adve,在 2000年的開始於 伊利諾伊大學厄巴納-香檳分校(University of Illinois at Urbana-Champaign),2005年,Apple 雇傭了 Chris Lattner,成立了一個團隊專註於 LLVM 系統在 Apple 開發系統上的各種使用。LLVM 目前是 Apple 的 Mac OSX 和 iOS 的開發工具的重要部分。
LLVM 項目是一個綜合項目(umbrella project),它包括了一系列開發工具相關的技術,如:
編譯器,Clang,LLVM 原生的 C/C++/Objective-C 編譯器,旨在提供一個快速的編譯器
調試器,LLDB,在 LLVM 和 Clang 的基礎上構建的一個調試器
JIT 系統,VMKit, Java 和 .NET 虛擬機的 LLVM 技術實現
優化器,DragonEgg,集成了 LLVM 的優化器和搭配 GCC 解析工具的代碼產生器
……
3.2 GCC
GCC,全稱為 GNU Complier Collection,GCC 是 GNU Project 的關鍵組成部分,是由自由軟件之父 Richard Stallman 在 1983 年9月27日於麻省理工大學發起的,旨在給一切計算機用戶提供自由、可控的計算環境,用戶可以自由的運行、分享、學習以及修改軟件,即自由軟件。
GCC 最初叫做 GNU C Compiler ,只支持 C 語言的編譯,1.0 之後開始支持 C++,再隨後支持了 Objective-C,Objective-C++,Fortran,Java,Ada,Go等其他語言。
至於選擇哪個作為首選,則看具體情況了。
GCC 歷史悠久,支持較為廣泛,且目前許多開源項目都是直接使用 GCC 作為編譯器的。當然,Clang 同為開源項目,可以跨平臺使用,而 Clang 相對 GCC 的優勢: 編譯速度快:在某些平臺上,Clang 的編譯速度顯著的快過 GCC。 占用內存小:Clang 生成的 AST 所占用的內存是 GCC 的五分之一左右。 模塊化設計:Clang 采用基於庫的模塊化設計,易於 IDE 集成及其他用途的重用。 診斷信息可讀性強:在編譯過程中,Clang 創建並保留了大量詳細的元數據 (metadata),有利於調試和錯誤報告。 設計清晰簡單,容易理解,易於擴展增強。與代碼基礎古老的 GCC 相比,學習曲線平緩。

Clang 下編譯一個 C++ 代碼:

技術分享

GCC 下編譯:

技術分享

兩者用法類似。gcc 和 clang 有類似的編譯選項命令,通過這些命令我們可以做許多事情。比如:
GCC 和 Clang 通過相應的編譯選項,可以看到 build 過程某些步驟:
-E, 可以只執行預處理階段,如:clang++ -E HelloWorld.cpp結果會打印出預處理後的代碼,HelloWorld 只有 7 行,預處理過後卻有幾萬行。
-S, 執行到編譯階段:clang++ -S HelloWorld.cpp這個階段,會生成一個對應名稱的 .s 文件,裏面包含了所謂的機器指令——匯編代碼,HelloWorld.cpp 生成了 86 行匯編代碼
-c,執行到編譯後的階段,生成對應名稱的 .o 對象文件
關於這兩個工具的選項,使用 man gcc 和 man clang 可以看到更多詳細的介紹。

Mac 下的 C++ 開發環境