Visual C++ 64 位遷移的常見問題

64位的win7已經廣泛、深入應用了,趕快看看我的32位程序該怎麼辦吧?

1、用 Visual C++ 創建在 64 位 Windows 操作系統中運行的應用程序時,應注意以下問題:

  • 在 64 位 Windows 操作系統中,intlong 是 32 位值。對於計劃爲 64 位平臺編譯的程序,應注意不要將指針賦給 32 位變量。在 64 位平臺上,指針爲 64 位,如果將該指針賦給 32 位變量,則應截斷該指針值。

  • 在 64 位 Windows 操作系統中,size_ttime_t ptrdiff_t 是 64 位值。

  • 在 32 位 Windows 操作系統上 Visual C++ 2005 之前的 Visual C++ 版本中,time_t 是 32 位值。在 Visual C++ 2005 和更高版本中,默認情況下,time_t 是 64 位整數。有關更多信息,請參見時間管理。

    應注意代碼在哪裏採用 int 值並將其作爲 size_ttime_t 值處理。數字有可能增長得比 32 位數大,並且數據在被傳遞迴int 存儲時有可能被截斷。

%x(十六進制 int 格式)printf 修飾符在 64 位 Windows 操作系統中不會按預期的那樣工作。它只對傳遞給它的值的前 32 位值執行操作。

  • Windows 32 位操作系統使用 %I32x 顯示整數。

  • Windows 64 位操作系統使用 %I64x 顯示整數。

  • %p(指針的十六進制格式)在 64 位 Windows 操作系統中按預期的那樣工作。

2、32位變爲64位編程後:
   SetWindowLong的地方改爲SetWindowLongPtr

   DWORD xxx=(DWORD)ptr;的地方改爲LONG_PTR xxx=(LONG_PTR)ptr;

3、字節對齊的細節和編譯器實現相關,但一般而言,滿足三個準則:
1) 結構體變量的首地址能夠被其最寬基本類型成員的大小所整除;
2) 結構體每個成員相對於結構體首地址的偏移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充字節(internal adding);
3) 結構體的總大小爲結構體最寬基本類型成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充字節(trailing padding)。
4、其它移植性考慮:

A、數據類型特別是int相關的類型在不同位數機器的平臺下長度不同。

B、爲了保證平臺的通用性,程序中儘量不要使用long數據庫型。可以使用固定大小的數據類型宏定義。

      typedef unsigned char int8_t;

C、使用int時也可以使用intptr_t來保證平臺的通用性,它在不同的平臺上編譯時長度不同。
D、程序員的目標是消除所有語法檢查器發出的警告,尤其是4311號指針截斷警告。
例如:
   buff = (puchar)srbcontrol;
  (ulong)buffer += srbcontrol->headerlength;//存在4311號指針截斷警告。
   //應該把ulong改爲uint -ptr,這樣才能既在win32上運行,也能在64位上運行
5、爲了順利實現兩種平臺的源代碼級可移植性,程序員應按照以下規則來編寫程序或者修改已有程序:
   A、不能將指針轉換成 int、uint、long、ulong、dword等字長固定爲32位的類型
,如果需要對指針做運算,應把指針轉換爲int-ptr或 uint-ptr,這兩種類型在不同平臺上纔有正確的字長。另外,由於handle實質上是一個指針(void *),因此把handle轉換成long或ulong等類型也是不正確的。
   B、如果確定需要對指針進行截斷,那麼應使用ptrtolong()和ptrtoulong()兩個函數(在basetsd.h中定義)來進行,它們可以屏蔽掉指針截斷警告,不過截斷的結果不能夠再當指針使用了。
   C、當某個api函數的 out參數能返回一個指針時,應小心謹慎處理參數,在win32中,可以把一個ulong變量的地址進行強制轉換後傳遞給api函數,返回的指針就保存在 ulong變量中,但在win64中,返回的指針有64位,如果使用ulong變量的話就會破壞其他變量的內容,正確並且簡單的方法是直接定義一個指針變 量,把指針變量的地址作爲參數傳遞給api函數。
   D、謹慎處理多態參數。在 win32中,一個函數可以用一個dword參數來接受多態參數,即該參數在不同情況下可能具有不同的意義,如解釋成整型數或指針。在win64中,如果 一個多態參數可能被解釋成指針,那麼決不能把多態參數設爲dword類型,而應設爲uint_ptr或pvoid類型。
   E、使用新的 get/setwindowlongptr和get/setclasslongptr api函數。如果在窗口或類的數據區中存放了指針,就需要調用上面的函數來存取相應的變量,爲了幫助程序員在編程中正確處理這一點,頭文件 winuser.h把索引值gwl_wndproc、gwl_hinstance、gwl_hwdparent和gwl_userdata的定義取消了, 轉而定義了新的索引值gwlp_wndproc、gwlp_hinstance、gwlp_hwdparent和gwlp_userdata。這樣下面的 代碼將會引起編譯錯誤:
  setwindowlong(hwnd,gwl_wndproc,(long)mywndproc);
  因爲gw_wndproc沒有定義,正確的代碼應爲:
  setwindowlongptr(hwnd,gwlp_wndproc,(int_ptr)mywndproc);
   F、許多窗口和類的數據結構中包含了指針,因此不能在代碼中強行指定偏移量來訪問數據成員,而應使用field_offset宏來計算偏移量。
   G、由於lparam、wparam和lresult通常用來存放指針或整數,在win64中它們全部被擴展成爲64位
,因此不能把它們與dword、ulong、uint、int、int和long等類型混用,否則可能會無意識地把它們截短了。
   H、當在Wom64環境下運行32位應用程序時,應用程序只限於在一個處理器上執行。也就是說應用程序不能夠享受64位操作系統所帶來的性能上的提升。而單處理器執行會降低32位應用程序在基於Itanium系統上運行時的性能和可伸縮性。爲此如果對於性能要求比較高的應用程序或者有負載比較高的服務器,儘量不要採用兼容的方式。
   I、轉換爲LP64指導原則:
      請勿假設int和指針的長度相同。

      請勿假設int和long的長度相同。

6、最好這樣去做:

a、符號擴展:

      整數提升:無論有無符號,都可以在調用類型爲int的表達式中使用char、short、枚舉類型或者位字段。如果int可以支持初始類型的所有可能,則值會轉換爲int類型。否則,轉換爲unsigned int類型。

      有符號和無符號整數之間的轉換:將帶符號的整數提升爲同一類的或者更長類的無符號整數時,該整數首先提升爲更長類型的帶符號相同值,然後轉換爲無符號值。
             兩個有符號整數相加的結果是一個有符號整數。
             一個操作數是無符號整數和另外一個操作數是有符號整數,那麼表達式的結果就是無符號整數。
             int 和 doubule 類型的兩個數相加,結果是一個 double 類型的數。
b、使用指針運算,而不是地址運算:通常指針運算都是獨立於數據模型的,而地址運算則可能不是,因此指針運算比地址運算更好。
c、對結構重新壓縮:由於long是64位,因此,儘量將long型的放在結構體開頭或者結尾。
d、檢查聯合類型:
       因爲其字段的長度可能在ILP32和LP64數據模型之間可能會發生變化。
       typedef union{double d; long l[2]}
       應當爲
       typedef union{double d; int  l[2]}
e、指定數據類型:
       在常量表達式後面增加{u,U,l,L}的某些組合,可以指定類型。
       int i = 32;
       long j = 1 << I; long j = 1L << I;
f、注意隱式聲明:
       在某些編譯模式下,編譯器可能會假設針對在模塊中使用卻未在外部定義或者聲明的任何函數或者變量,使用int類型。
g、sizeof是unsigned long
       在LP64環境中,sizeof的有效類型是site_t,該類型是unsigned long。
       在IL32環境中,sizeof的有效類型是size_t,該類型是unsighed int。
h、使用強制類型轉換說明意圖:
i、檢查格式字符串轉換操作:

       對於類型爲long或指針的參數,可能需要更改printf,sprintf,scanf,sscanf的格式字符串。指針要轉換爲%p,long型參數轉換爲添加一個l在格式字符串前面。
       檢查直接使用long類型是否仍有意義:
       例如:pid_t在32位環境中是long類型,在64位系統中是int類型。
j、對顯示32位與64位原型使用#ifdef
       某些情況下,一個接口存在對特定的32位和64位版本是不可避免的。可以通過頭文件使用_LP64或_ILP32功能測試宏來區分這些版本。
LD_LIBRARY_PATH
可以將32位的lib和64位的lib的路徑都設置到這個變量中,也可以將64位的lib路徑設置到
LD_LIBRARY_PATH_64中。
k、高級主題:
指令大小仍然是32位的。
整數乘除可以完全在硬件中實現。
結構的傳遞和返回以不同的方式實現。小型數據結構和某些浮點參數可以直接在寄存器中傳遞。
所有數據類型現在都與其長度對齊。
系統中存在兩種不同的庫,32位的庫和64位的庫。
Ioctl或者ipc機制的接口,通過對這些接口仔細進行編碼,並且謹慎地使用#Pragma pack或_Pack指令,可避免對齊問題。
l、進程間通訊:使用相同長度的類型。
m、64位應用程序的優點:

針對64位值更高效地執行算術和邏輯運算。
運算過程使用全寄存器寬度、全寄存器集合新指令。
值得參數傳遞效率更高。
小型數據結構和浮點數值得參數傳遞效率更高。
提供了額外的整數寄存器和浮點寄存器。
對於amd64,提供了PC相關尋址模式,從而可提高位置無關代碼的執行效率。
n、64位應用程序的缺點:
64位應用程序需要更多的棧空間才能容納更大的寄存器。
由於使用了更大的指針,因此應用程序需要更大的高速緩存。
64位應用程序不能運行在32位平臺上。
o、EOVERFLOW的含義:
許多32位系統調用在遇到64位內核中的大對象時,都會返回EOVERFLOW,由於daddr_t,dev_t,time_t,及其派生類型struct timeval和timespec_t現在包含的64位值,因此可能意味着32位應用程序會遇到更多的EOVERFLOW返回值。
p、謹慎使用ioctl:
請考慮使用兩個icotl調用:一個用來處理32位值(IOP32)的指針,另一個用來處理長值(IOPLONG)的指針。
int a,d;
long b;
if (icotl(d, IOP32, &b) == -1)
if (icotl(d, IOPLONG, &a) == -1)
編譯此代碼並將其作爲32位應用程序的一部分運行時,這兩個ioctl調用均可正常工作。
但是,編譯作爲64位應用程序時,這兩個調用均可返回。但是,這兩個均無法正常工作。第一個,傳遞的容量太大,其結果和大端,小端字節序有關。第二個,則會溢出,覆蓋棧上的附近的地址的內容。
q、派生類型更改:
例如:
#if defined(_LP64)
Typedef ulong_t size_t;
#else
Typedef uint_t size_t;
#endif
r、如何確定系統運行的是32位還是64位的?
RedHeat和CentOS
file /bin/ls  使用file察看任何一個文件在/bin路徑下都可以,顯示結果爲:
       [root@localhost bin]# file date
date: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), stripped
       [root@hfgs126 bin]# file wbxgs

wbxgs: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.4.0, dynamically linked (uses shared libs), not stripped

7、就像 32 位 Windows 操作系統一樣,在 64 位 Windows 操作系統上運行 64 位託管應用程序時,可以創建的對象的大小也有一個 2GB 的限制。

許多情況下,程序集在 32 位或 64 位 CLR 上可以同等地運行。 在 64 位 CLR 上運行時,導致程序的行爲發生改變的一些原因有:

  • 結構中包含大小隨平臺而改變的成員,例如任何指針類型。

  • 指針算法中包含固定大小。

  • 不正確的平臺調用或 COM 聲明,對句柄使用 Int32,而不是 IntPtr。

  • 將 IntPtr 強制轉換爲 Int32。

8、本來就充滿荊棘的編程之路又要左躲右閃、小心翼翼了。不知道是幸福,還是可憐。

 

注:本文轉載自:http://blog.csdn.net/noodle123/article/details/6749381

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章