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 == 64
typedef long int              int64_t;
# else
__extension__
typedef long long int      int64_t;

#endif

三、使用int时也可以使用intptr_t来保证平台的通用性,它在不同的平台上编译时长度不同,但都是标准的平台长度,比如64位机器它的长度就是8字节,32位机器它的长度是4字节,定义如下:

#if __WORDSIZE == 64
typedef long int                intptr_t;
#else
typedef 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。

posted @ 2010-10-06 17:40  小 楼 一 夜 听 春 雨  阅读(10524)  评论(0编辑  收藏  举报