64位配置和編譯
第四講 建立64位的配置
編譯器
首先,你要確定你所使用的Visual Studio的版本允許你編譯程式為64位程式碼。如果你想使用最新的版本(寫本課本時)Visual Studio 2008 來開發64位應用,以下的表格幫助你理解你所需要的Visual Studio 版本。
表1 不同版本的VS2008的能力
如果你使用的VS的版本使你可以去建立64位程式碼,你需要檢查是否安裝了64位的編譯器, 圖1顯示在安裝VS2008時,64位編譯器沒有被選中。
圖1 安裝VS2008時64位編譯器沒有被勾選
建立64位的配置
在VS2005/2008中建立一個64位應用是一個非常簡單的過程。困難會出現在之後編譯新的程式和檢查錯誤的時候。建立一個64位程式,你需要做以下的步驟。
第一步
開啟Configuration Manager
第二步
在Configuration Manager 中選擇new platform
第三步
選擇64位平臺,選擇32位的配置作為基礎,VS會自動修改那些會影響到編譯模式的配置
第四步
你已經添加了新的配置,現在可以選擇64位的配置,然後開始編譯了。
修改引數
如果你幸運的話,你不需要修改64位程式。但這個是取決於你的程式碼,它的複雜性,以及它所用的庫的數量。你現在需要馬上修改的值應該是堆疊的大小(stack size),如果你的程式所用的是堆疊的預設值,即 1 Mbyte,64位版本的時候,應該改為2 Mbyte。你不是必須要這麼做,但是提前這樣做可以更保險。如果你用的是其他值,你需要保證64位使用的數值是之前數值的兩倍。你可以通過改變project setting 中的Stack Reserve Size 和Stack Commit Size值來實現以上所說的變化。
下一步?
有一個64位的配置,不代表你的編譯能夠成功或者是能執行。 下一講,我們會介紹編譯過程,以及怎樣發現隱藏的問題。
第五講 編譯64位應用
我們需要向讀者說明,我們不可能涵蓋編譯一個64位應用的所有細節問題。每個專案有自己的特有的設定,所以你需要特別注意這些設定。本講只會涵蓋對任何程式都重要的步驟,這些步驟只是一個開始,更多的問題需要讀者自己去解決。
庫
當你嘗試編譯一個你產品的64位版本時,首先確認所有必須的64位的庫已經成功安裝了,而且它們的路徑是正確的。例如32位與64位的以“lib”結尾的庫檔案是不同的,而且在不同的目錄中,如果有bug請修改。
注意:如果庫是以原始碼的形式存在的,那麼會存在庫專案的64位的配置。 注意到,當你修改一個原始碼的配置,以編譯適合你的64位版本時,你存在可能侵犯了版權的可能性,請謹慎。
彙編器
Visual C++ 不支援64位的內聯彙編(inline assembler),你可以選擇一個外部的64位彙編器(如,MASM)或者是重寫這些組合語言。
編譯錯誤和警告的例項
當開始編譯你的程式時,你會碰到許多因為顯式型別轉換 和隱式型別轉換所帶來的問題(explicit and implicit type conversions)。以下是一個例子。
這段程式碼在32位上成功編譯,但在64位上不行,Visual C++ 會產生如下warning
因為函式strlen()返回的型別是size_t,在32位系統上,型別size_t與unsigned int的型別一致,所以編譯器選擇"void foo(unsigned int)"來呼叫。但在64位的模式下,size_t與unsigned int的型別不一致。 size_t 變成了64位,但unsigned int型別仍然保持了32位。因而編譯器不知道哪一個foo()函式來呼叫。
現在來考慮一段由Visual C++編譯64位程式碼時,所產生的錯誤:
GetSize()函式返回的型別是 INT_PTR,在32位時,與int型別一致。在64位時,INT_PTR是64位而隱式的轉換為32位。因為可能會導致高位值丟失,這就是系統給出警告的原因。 當陣列中元素數量超過了INT_MAX, 一個隱式型別轉換就有可能會導致錯誤。去除這個警告和可能錯誤的方式就是,你應該將len的型別寫為INT_PTR 或者是ptrdiff_t 型別。
在你已經完全瞭解了64位錯誤的規律前,都不要去隱藏warning。你有可能會隱藏一個錯誤,使得之後更難發現它。你可以在之後的課程中,學到更多的關於64位錯誤的規律和發現他們的方式。你可以同樣參考一下文章 "20 issues of porting C++ code on the 64-bit platform", "A 64-bit horse that can count"。
size_t 和ptrdiff_t 型別
在大多數關於資料不相容的編譯錯誤和警告中,我們應該尤其考慮兩種型別 size_t 和ptrdiff_t, 這兩者在編譯64位程式碼中最容易出現問題。如果你使用的是Visual C++編譯器,這些型別可能被整合到編譯器中,你不需要增加庫描述檔案。但是如果你使用的是GCC, 你可能需要新增標頭檔案“stddef.h”。
size_t 是一個C/C++ 基本unsigned integer型別。 它是sizeof 操作的返回值。這種型別的大小之所以這樣選擇,是因為它可以用來儲存理論上的最大陣列的大度值。例如,size_t在32位系統上是32位,在64位系統上是64位。換句話說,你可以安全的用size_t 型別的變數儲存一個指標,但是不包括指向函式的指標。這個型別經常作為在迴圈中的計數器的型別,作為陣列索引,用來儲存長度,或者用於地址的計算中。以下的這些型別與size_t類似:SIZE_T, DWORD_PTR, WPARAM, ULONG_PTR。 雖然你可以在size_t中儲存一個指標,但是最好使用另外一個unsigned integer 型別uintptr_t ——它的名字就反映了它的用途。size_t與uintptr_t 是同義的。
ptrdiff_t是一個C/C++ 基本signed integer型別。這種型別的大小之所以這樣選擇,是因為它可以用來儲存理論上的最大陣列的大度值。例如,ptrdiff _t在32位系統上是32位,在64位系統上是64位。與size_t類似,你可以安全的用ptrdiff _t 型別的變數儲存一個指標,但是不包括指向函式的指標。ptrdiff_t型別也是表示式"ptr1-ptr2"的返回值。這個型別經常作為在迴圈中的計數器的型別,作為陣列索引,用來儲存長度,或者用於地址的計算中。與它類似的是SSIZE_T, LPARAM, INT_PTR, LONG_PTR。 與它同義的是intptr_t,intptr_t可以用來儲存一個指標。
size_t 和ptrdiff_t被創造就是為保證地址運算的正確性。在較長的時間內,int的型別都與機器字(machine word)的長度一致,因而int型別變數經常被作為陣列索引,或者是用來儲存物體和指標大小。因而地址運算也是通過int和unsigned型別來編譯的。int型別在大多數的C/C++教程中出現在迴圈體做作為索引。下面是一個經典的例子:
當處理器不斷的發展,繼續擴充套件int的長度顯得不那麼合理了。這是由許多因素造成的如:節約使用的記憶體,最大的相容性等。因而,許多的資料模式出現,來描述基本C與C++型別之間的關係。所以現在選擇一個型別來儲存指標和物體大小就不那麼容易了。size_t 和ptrdiff_t型別的出現就成了最好的解決方案。它們肯定可以在地址運算中使用。現在如下的程式碼將成為經典:
這段程式碼才是能提供最好的安全性,移植性和高效能的程式碼。在之後的課程中你將學到原因。
size_t 和ptrdiff_t型別可以被稱為memsize型別。Memsize 的出現是為了簡單的描述所有能作為儲存最大陣列的長度值,和作為這樣陣列的索引的值 的型別。當說到memsize型別時,你需要能立刻想到它們在32位系統上是32位的,在64位系統上是64位的。以下是一些memsize型別:size_t, ptrdiff_t, pointers, SIZE_T, LPARAM。