32位和64位系统对于程序员的影响

时间:2020-11-28 03:35:30
win32,win64编程永恒;语言编程需要注意的64位和32机器的区别
一、数据类型特别是int相关的类型在不同位数机器的平台下长度不同。C99标准并不规定具体数据类型的长度大小,只规定级别。作下比较:
16位平台
char      1个字节8位
short        2个字节16位
int         2个字节16位
long      4个字节32位
指针      2个字节
32位平台
char      1个字节8位
short        2个字节16位
int         4个字节32位
long      4个字节
long long 8个字节
指针      4个字节
64位平台
char      1个字节
short        2个字节
int         4个字节
long      8个字节(区别)
long long 8个字节
指针        8个字节(区别)
二、编程注意事项
为了保证平台的通用性,程序中尽量不要使用long数据库型。可以使用固定大小的数据类型宏定义:
typedef signed char       int8_t
typedef short int          int16_t;
typedef int                int32_t;
# if __WORDSIZE == 64typedef long int           int64_t;# else__extension__typedef long long int      int64_t;
#endif
三、使用int时也可以使用intptr_t来保证平台的通用性,它在不同的平台上编译时长度不同,但都是标准的平台长度,比如64位机器它的长度就是8字节,32位机器它的长度是4字节,定义如下:
#if __WORDSIZE == 64typedef long int             intptr_t;#elsetypedef int                  intptr_t;#endif编程中要尽量使用sizeof来计算数据类型的大小
以上类型定义都有相应的无符号类型。
另外还有ssize_t和size_t分别是sign size_t和unsigned signed size of computer word size。它们也是表示计算机的字长,在32位机器上是int型,在64位机器上long型,从某种意义上来说它们等同于intptr_t和uintptr_t。它们在stddef.h里面定义。需要注意的是socket的accept函数在有些操作系统上使用size_t是不正确的,因为accept接收的int*类型,而size_t可能是long int 类型。后来BSD使用sock_t来替代它。
 
  从1995年windows 95推出至今,绝大多数windows应用程序都已经从win16平台转移到win32平台,windows 3.x及其代表的16位windows编程技术被迅速打入了冷宫。现在,microsoft公司的软件工程师们针对intel公司的ia-64处理器构架又开始了64位windows编程(win64)技术的设计,由于windows 98是windows 9x系列中的最后一个产品,所以win64将被包含在正在开发的windows nt 5.0中,当intel的新一代64位处理器merced推出后,软件开发人员就可以开始使用64位编程技术,nt的企业计算能力势必会得到极大的提高。
 
  因为4gb的地址空间满足了目前绝大多数应用的需求,win64与win32之间的差异远小于win32与win16之间的差异,所以win64不会像win32取代win16那样迅速取代win32。在今后相当长的一段时间内,win64将与win32和平共处,相互补充。软件开发人员需要根据应用的特点选择开发平台,或者同时建立应用程序的win64和win32版本,由于win64与win32编程区别不大,在使用高级语言编程时只要遵循一定的原则,花费极少量的时间与精力就可以为不同平台创建相应的版本,并且在多数时候只要对源程序重新编译连接一次就可以了。
   ·llp64抽象数据模型·
  c语言标准中没有规定整型、长整型和指针等变量各有多少位,而是留给了计算平台来确定,因此每种系统及其应用都必须采用某种默认的抽象数据模型来作为计算的基础,win32采用一种称为llp32的模型,即假定整型、长整型和指针变量的字长都是32位,相应定义的int、uint、long、ulong和handle等数据类型也都是32位,这对于32位windows编程环境来说是合理的规定。如果win64中把整型、长整型和指针变量的字长都改为64位,那么不仅要占有以前两倍的存储空间,而且现有的应用程序及其使用的大多数数据类型都需要作更改,实现两种平台之间的可移植性就变得相当困难了。事实上,在引入64位平台后,应用程序需要实现64位寻址,并只在少数地方需要用到64位数据,而多数时候使用32位数据就足够了,因此,win64采用了一种称为llp64的抽象数据模型,除了把指针变量扩展到了64位外,整型和长整型变量等基本数据类型仍然保持32位。由于基本数据类型都保持32位不变,所以存储在磁盘上的数据不用改变其结构和大小,远程或本地进程之间的共享数据(如内存映射文件)也不用改变其结构和大小,这就大大减轻了程序员的工作量。
  为了实现同一份源代码既能在win32下运行,也能在win64下运行,定义llp64模型只是走出了第一步,接下来还要定义一些与指针相关的数据类型,如对指针进行计算时需要的数据类型等,编译器会根据目标平台来确定这些数据类型是32位还是64位。这些新的数据类型是用c语言的int和long类型定义的,因此保持了对win32及部分windows api函数的向后兼容性,microsoft计划在windows nt 5.0的beta 2版包含这些新数据类型,并逐渐移植所有的windows api函数到64位平台,程序员在nt 5.0 beta 2版及相应的platform sdk推出后就可以开始使用这些数据类型了,当64位平台正式推出后,所编写的程序多数只需重新编译连接一次即可生成64位版本。
  ·新的数据类型·
  新数据类型共有三类:固定精度数据类型(fixed-precision data types)、指针精度数据类型(pointer-precision data type)和指定精度指针(specific-precision pointers)。
  1.固定精度数据类型在win32和win64中有相同的字长,为了便于记忆,它们的名字中包含了其字长。
  ·int32和int64:字长分别为32位和64位的有符号整型数;
  ·long32和long64:字长分别为32位和64位的有符号长整型数;
  ·uint32和uint64:字长分别为32位和64位的无符号整型数;
  ·ulong32和ulong64:字长分别为32位和64位的无符号长整型数。
  2.指针精度数据类型与目标平台的指针的字长相同(由编译器来确定),这样可以把指针安全地转换成指针精度数据类型来进行代数运算和位运算,而不用编程时处处考虑目标平台的类型。
  ·int-ptr和uint-ptr:指针精度的有符号和无符号整型数,win32下字长为32位,win64下字长为64位;
  ·ssize-t和size-t:指针精度的计数器,用于确定指针精度数据类型的字长,前者为有符号计数器。
  3.特定精度指针在win32和win64中都保持相同的字长,因此只在某些特殊情况下才有用。
  ·-ptr64(64位指针):在win32中,32位指针通过符号扩展生成一个64位指针,扩展的结果可能没有任何意义,不能再当做指针来使用;
  ·-ptr32(32位指针):在win64中,64位指针通过截去高32位生成一个32位指针,结果可能没有任何意义,也不能再当做指针来使用。
  使用这些新的数据类型可以更清晰地显示出程序中哪些地方进行的计算实质上与指针相关,这样在进行类型转换时就不容易出错,而win32中原来定义的数据类型就没有这个优点,因此新数据类型有利于我们编写出更健壮的代码,并且为将来移植到64位平台做好了准备。
  ·辅助开发工具·
  在win64下编程与在win32下编程区别很小,因为大家熟悉的windows api函数除了涉及到指针的参数的类型可能改变外,其他没有什么更多的变化,程序员原有的知识仍然有用。为了帮助程序员修改现有的源代码,转而使用新的数据类型,microsoft将在nt 5.0 beta 2版中包含一些开发辅助工具,其中包括一个定义新数据类型的头文件basetsd.h和一个语法检查器。语法检查器可以检查出源程序中不正确的类型转换、指针截断及其他一些与64位相关的问题。例如它会指出下面的代码存在着4311号指针截断警告:
  buff = (puchar)srbcontrol;
  (ulong)buffer += srbcontrol->headerlength;
  为了消除警告,应该把ulong改为uint-ptr,这样才能保证这段代码既能在win32上运行,也能在win64上运行。程序员的目标是消除所有语法检查器发出的警告,尤其是4311号指针截断警告。
  ·编程规则·
  为了顺利实现两种平台的源代码级可移植性,程序员应按照以下规则来编写程序或者修改已有程序。
  1.不能将指针转换成int、uint、long、ulong、dword等字长固定为32位的类型,如果需要对指针做运算,应把指针转换为int-ptr或uint-ptr,这两种类型在不同平台上才有正确的字长。另外,由于handle实质上是一个指针(void *),因此把handle转换成long或ulong等类型也是不正确的。
  2.如果确定需要对指针进行截断,那么应使用ptrtolong()和ptrtoulong()两个函数(在basetsd.h中定义)来进行,它们可以屏蔽掉指针截断警告,不过截断的结果不能够再当指针使用了。
  3.当某个api函数的out参数能返回一个指针时,应小心谨慎处理参数,在win32中,可以把一个ulong变量的地址进行强制转换后传递给api函数,返回的指针就保存在ulong变量中,但在win64中,返回的指针有64位,如果使用ulong变量的话就会破坏其他变量的内容,正确并且简单的方法是直接定义一个指针变量,把指针变量的地址作为参数传递给api函数。
  4.谨慎处理多态参数。在win32中,一个函数可以用一个dword参数来接受多态参数,即该参数在不同情况下可能具有不同的意义,如解释成整型数或指针。在win64中,如果一个多态参数可能被解释成指针,那么决不能把多态参数设为dword类型,而应设为uint-ptr或pvoid类型。win32自身的一部分api函数(如raiseexception())因为不符合该条规则而需要进行修改。
  5.使用新的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);
  6.许多窗口和类的数据结构中包含了指针,因此不能在代码中强行指定偏移量来访问数据成员,而应使用field-offset宏来计算偏移量。
  7.由于lparam、wparam和lresult通常用来存放指针或整数,在win64中它们全部被扩展成为64位,因此不能把它们与dword、ulong、uint、int、int和long等类型混用,否则可能会无意识地把它们截短了。

  关于在win64环境下编程还需要注意的其他问题,以及win64平台下api函数的变化及新增函数,有兴趣的读者可到microsoft的web站点查阅相关资料,或者参考最新版的platform sdk及msdn oline library。

··························································································································································································

C/C++仅仅定义了这些基本数据类型之间的关系,并没有定义严格定义它们的字长。在不同的平台上,根据编译器不同的实现,它们的字长如下表所示:

数据类型

LP64

ILP64

LLP64

ILP32

LP32

char

8

8

8

8

8

short

16

16

16

16

16

_int32

N/A

32

N/A

N/A

N/A

int

32

64

32

32

16

long

64

64

32

32

32

long long

N/A

N/A

64

N/A

N/A

pointer

64

64

64

32

32

在这张表中,LP64,ILP64,LLP64是64位平台上的字长模型,ILP32和LP32是32位平台上的字长模型。

LP64意思是long和pointer是64位,ILP64指int,long,pointer是64位,LLP指long long和pointer是32-bit的。ILP32指int,long和pointer是32位的,LP32指long和pointer是32位的。

32位Windows采用的是LP32数据模型,64位Windows采用的是LLP64数据模型。
所以,Windows上的32位程序设计和64位程序设计最大的不同(也就是IP32和LLP64的不同),就在于指针的长度不同??由32位变成了64位。
Win32 API在很多情况下,都需要将整数转换成指针或者相反。在 32 位的硬件上不会有问题,其中指针的大小和整数的大小是相同的,但在 64 位的硬件上却完全不一样。
为此M$搞了个所谓的“多态类型”:

对于特定的精度,您可以使用固定精度的数据类型。不管处理器的词大小如何,它们的大小都是一致的。大多数这些类型都在它们的名称中包含精度,可以从下面的表中看出:

表 1. 固定精度的数据类型
类型 定义

DWORD32            

32 位无符号整数

DWORD64

64 位无符号整数

INT32

32 位有符号整数

INT64

64 位有符号整数

LONG32

32 位有符号整数

LONG64

64 位有符号整数

UINT32

无符号 INT32

UINT64

无符号 INT64

ULONG32

无符号 LONG32

ULONG64

无符号 LONG64

此外,当您需要数据类型的精度随着处理器词大小变化时,请使用指针精度数据类型。这些类型又称为“多态”数据类型。这些类型通常以 _PTR 后缀结尾,如下面的表格所示:

表 2. 指针精度的数据类型
类型 定义

DWORD_PTR              

指针精度的无符号长类型

HALF_PTR

指针大小的一半。用于包含一个指针和两个小型字段的结构中

INT_PTR

指针精度的有符号整型

LONG_PTR

指针精度的有符号长类型

SIZE_T

指针可以引用的最大字节数。用于必须跨指针的整个范围的计数

SSIZE_T

有符号 SIZE_T

UHALF_PTR

无符号 HALF_PTR

UINT_PTR

无符号 INT_PTR

ULONG_PTR

无符号 LONG_PTR

LPARAM

与 LONG_PTR 为同义词,(在WTypes.h 中定义)

WPARAM

与 UINT_PTR 为同义词,(在 WTypes.h 中定义)

通过整数参数传递参数或上下文信息的所有 Win32 API 都更改为使用这些新的类型。

此外,还出现了定长指针:POINTER_32和POINTER_64:
#define POINTER_32 __ptr32
#define POINTER_64 __ptr64
PS:M$DN中说是在Basetsd.h中定义的,但实际上是在WinNT.h中定义的。