您的位置:首页 > 其它

64位程序与32位程序区别

2013-07-03 08:15 274 查看
概述
        从32位到64位,根本性的区别在于两种数据类型发生了变化:long和pointer。在32位环境下,顾名思义,两者长度都是32位,也就是4个字节;而在64位环境下,都是8个字节。所以,当你把pointer或者long型数据赋给int型时,会发生数据截断(data truncation)。

 

确定在64位操作系统上运行还是在WOW64下运行

  在Visual Studio平台中最后对64位应用程序编译时,需要指定这个应用程序是在64位操作系统上作为本机应用程序运行还是在WOW64(Windows-on-Windows64-bit)环境下运行。这个WOW64环境是一个由操作系统提供的兼容性环境。这个环境主要是让32位应用程序也能够在64位的操作系统上运行。换句话说,在编译时要确定产生的应用程序是真正的64位应用程序(只能够在64位操作系统上运行),还是伪64位应用程序(其实是32位应用程序,只是可以借助WOW64在64位操作系统上运行)。

      WOW64 非常重要,因为当不关注性能和可伸缩性的问题时,它使您可以利用大多数现有的 32 位代码。它是两种方法的最佳结合。您可以将您的服务迁移到 64 位,同时将 Microsoft 管理控制台 (MMC) 配置管理单元保留为 32 位。

      WOW64 是在用户模式下实现的,作为 ntdll.dll 和内核之间的层。WOW64 及其支持的一些 DLL 仅仅是可以加载到 32 位进程中的 64 位的 DLL。对于所有其他情况,进程保持为纯进程。32 位的进程无法加载 64 位的 DLL,反之亦然。

      Wow64.dll 是Windows NT kernel的核心接口, 在32位和64位调用之间进行转换, 包括指针和调用栈的操控. Wow64win.dll 为32位应用程序提供合适的入口指针. Wow64cpu.dll 负责将处理器在32位和64位的模式之间转换。
 
        *********************************** 判断程序的三种情况: ********************************

  一是64bit的程序跑在64bit下,那么不是wow64模式,返回0

  二是32bit程序跑在64bit下,是wow64模式,返回1.

  三是32bit程序跑在32bit下,返回0.

  typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);

  LPFN_ISWOW64PROCESS fnIsWow64Process;BOOL IsWow64()

  {

      BOOL bIsWow64 = FALSE;

      fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( GetModuleHandle(TEXT("kernel32")),"IsWow64Process");

      if (NULL != fnIsWow64Process)

      {

      if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))

          { // handle error

          }

      }

      return bIsWow64;

  }
 

32位与64位API

64位windows的api还叫win32 api,只不过是用64位代码实现的,kernel32、user32这些名字都没变程序编译为64位的PE,就是使用的win64提供的api了。目前微软提供的win64操作系统可以以兼容的方式跑32位程序,也就是同时提供了一天win32的DLL共32位程序使用。32位程序在64位系统上跑肯定没有64位的程序在64位系统上跑的效率高,因为其要转化。

 

32位与64位数据模型之间的区别
32位应用的数据模型我们称作ILP32(之所以这样命名,是因为int,long,pointer都是32位),而64位应用则采用LP64模型(意味着long,pointer变成64位了,其他的不变),在当前的32位环境下,我们可以认为int,long,pointer是等价的,因为它们占用相同的字节嘛,于是,就有很多混用的情况;但是,到了64位的时代,long和Poniter的大小都改变了,原来的假设不再成立。
 
用lint检测long/pointer的64位问题

采用-errchk=longptr64选项可以检查出把long/pointer表达式转换为int的情况,包括显式转换。
 
留心int和pointer
因为integer与pointer大小相同,所以32位代码中常常把pointer转换为int或者unsigned int,以便算术运算。为了移植,你可以把pointer转换为unsigned long,因为long和pointer都是等长的,无论是在ILP32亦或LP64,但是,为了使代码更清晰,推荐用uintptr_t,uintptr_t和intptr_t都需要包含头文件inttypes.h。

例如:下面代码在64位环境下编译出错:

char *p;

p = (char *) ((int)p & PAGEOFFSET);

% cc ..

warning: conversion of pointer loses bits
 
改用uintptr_t后,无论是32位或者64位都没问题:

char *p;

p = (char *) ((uintptr_t)p & PAGEOFFSET);
 
留心int和long
在ILP32中,可能从未对int和long加以区分,因此,混用的情况非常多,看下面代码:

int waiting;

long w_io;

long w_swap;

...

waiting = w_io + w_swap;
% cc

warning: assignment of 64-bit integer to 32-bit integer
 
留心对齐

出于访问的效率,结构中通常会有所谓的hole,用来保证其中的所有数据成员,起始地址都是对齐模数的倍数。

例如:

struct bar {

  int i;  

  long j; 

  int k; 

  char *p;

};

在ILP32中,sizeof(bar)应该是16字节;在LP64中,应该是32!因为此时long/char *的对齐模数都变为8,为了保证满足对齐要求,i/k都被扩展为8字节了。
又例如:

struct bar {

  char *p;

  long j;

  int i;

  int k;

}

此时,无需扩展,sizeof(bar)=8+8+4+4=24.
 
留心union
union中的成员,必须保持平衡,也就是说,必须保证大小相等才有意义,所以移植时也要注意。

例如:

typedef union {

  double _d;

  long _l[2];

} llx_

在ILP32中,两者大小相同,都是8字节;移植到LP64,前者不变,后者为16字节,此时union已无意义,应改为:

typedef union {

  double _d;

  int _l[2];

} llx_
 
留心常量类型

在常量表达式中,精度的缺失会导致数据截断,例如:

int i = 32;

long j = 1 << i;

warning: left shift count >= width of type

什么意思?编译器抱怨左移的位数超过了数据类型的长度,结果就是j为0。
怎么修改呢?

int i = 32;

long j = 1L << i:

即可。
 
留心printf系列函数
在格式化串中,要注意:

%p兼容ILP32和L64。

如果有long型参数,必须加上l前缀;

另外就是作为目标的buffer必须够长。
 
留心sizeof

sizeof返回类型为unsigned long,如果返回给int型变量,可能发生截断。
 
留心derived data types

例如,这些定义在sys/types.h中的数据类型,其大小会随ILP32或者LP64而变化:

    * clock_t, which represents the system time in clock ticks

    * dev_t, which is used for device numbers

    * off_t, which is used for file sizes and offsets

    * ptrdiff_t, which is the signed integral type for the result of subtracting two pointers

    * size_t, which reflects the size, in bytes, of objects in memory

    * ssize_t, which is used by functions that return a count of bytes or an error indication

    * time_t, which counts time in seconds
 
留心边缘效应

局部代码发生类型改变,可能导致其他代码发生64位转换,例如函数的返回值由Int变为sszie_t,则所有调用该函数并获取其返回值的地方,都有可能发生意想不到的64位转换。
 
留心long array 对效率的影响

大型Long/unsigned long数组,在LP64下,相比ILP32,效率非常低,所以,如果int就足够,尽量不要使用Long,这一点,对于pointer arrary同样适用。
 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 
        有很多程序员在用C语言编程时"玩忽职守",脑袋中想像着64位CPU,却用32位系统的模式来编程,以下是常见的情况:

  ·以为指针与int大小一样。在64位系统中,sizeof(void *) == 8,而sizeof(int) ==
a54e
4。如果忘记了这个,将导致不正确的赋值以致程序崩溃。

  ·依赖于某一机器字架构的特定字节序。

  ·使用long类型并假定它总是与int有同样大小。由此的直接赋值将导致数值截断,并且问题很难察觉。
  ·堆栈变量的对齐方式。在一些情况中,堆栈变量也许不是按8字节边界对齐,如果你把这些变量转换成64位变量,在某些系统上,将会遇到一些麻烦。但是如果你在堆栈上放置一个64位变量(long或double),这保证是对齐的;还有,堆中分配的内存也是对齐的。

  ·不同的对齐方式决定了结构与类的对齐。在64位架构上,结构成员通常对齐于64位边界,当在通过IPC、网络、或磁盘共享二进制数据时,就会有些问题;另外在包装数据结构以便存储资源时,没有考虑到对齐方式,同样也会有问题。

  ·指针算法。当把一个64位指针像32位指针那样递增时(反之亦然),64 位指针每次递增8字节,而32位指针每次递增4字节。

  ·在缺少函数原型的情况下,返回值一般为int,这在某些时候也会导致数值截断。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  64位 32位 应用 vc