您的位置:首页 > 其它

简单改变之完美跨越32位与64位平台

2008-03-22 10:02 337 查看
[align=center]简单改变之完美跨越32位与64位平台[/align]

并不能因为现在不会将代码移植到64位,就可以忽略以下新的编码准则。现在,只需一些简单的调整,就可让你的C++代码完美跨越32/64位平台,以便在将来64位需要来临时,让你处于有利的位置。

在90年代初,64位系统还被认为是一个“等待问题的解决方案”;而到了2005年,64位技术已快速地聚集了大量的用户,即使你的开发暂时只针对32位平台,但在代码中加入与64位的兼容性将有助于未来向64位的平滑过渡。本文讲述的就是怎样编写跨越32位及64位平台的代码。

当处于64位时
64位架构提供了以下优势:
²支持更大的文件:大文件是指包含2GB或更多数据的文件。
²64位寻址:64位处理器具有16EB(千兆兆字节)的虚拟寻址空间,这相当于目前4GB寻址上限的40亿倍。
²打破了4GB内存的界限:在64位系统上,理论上最大支持物理内存的上限为16EB。

作者注:64位环境中使用的内存单位:
l1TB=1024GB
l1PB=1024TB=1048576GB
l1EB=1024PB=1048576TB=1,073,741,824GB

为充分利用上述有利条件,必须使用64位编译器及64位库分别重新编译及链接源代码。好消息是,目前的32位可执行文件依然可运行于64位模式下,反之不可,64位二进制代码只能运行于64位模式中。

ILP32vsLP64
编写与64位系统兼容的程序的主要问题,在于32位与64位系统的不同数据类型模型。32位数据类型模型被称为ILP32,也就是说int、long、指针都具有32位的同等大小;64位数据类型模型被称为LP64,在此之下,int依然为32位,然而,long与指针却是64位。下表概述了两种模型的不同之处,并列出了每种基本类型下的所占位数:

C/C++本地类型
ILP32
LP64
char
8
8
bool
通常与char相同
通常与char相同
short
16
16
int
32
32
long
32
64
longlong
64
64
(数据或函数的)指针
32
64
enum
32
32
float
32
32
double
64
64
longdouble
64/80/128
128
当编写同时针对两种平台的代码时,有一点非常重要,就是要清楚地定义哪一个对象该有固定位宽(即定宽类型),而先不要管目标平台架构。在以下情况中,可能需要定宽数据类型:
²从网络连接中读取数据
²32位程序写入到磁盘上的数据
²某些二进制标准的接口

至于定宽数据类型,可使用定义在<intytpes.h>中的标准typedef。
int8_tint16_tint32_tint64_t//
有符号的

uint8_tuint16_tuint32_tuint64_t//
无符号的


严格来说,
<inttypes.h>
是一个
C99
标准的头文件,但大多数的
C++
编译器现在都支持它。



平台相关数据类型的自动映射
C和C++定义了如size_t、ptrdiff_t、fpos_t、time_t等等的typedef,抽象表示了对象的底层类型,因此可使编译器自动地把它们映射为合适的本地数据类型。请记住,在跨两种平台编码时,切记使用以上的typedef类型。拿size_t举例来说,在ILP32中,它定义了一个32位的无符号整型,而在LP64中,它定义了一个无符号的64位整型。
如<fstream>之类的高级库通常都提供了更干净利落的移植,因为它们使用了抽象层。举例来说,函数tellp()返回一个std::streampos对象而不是一个int;而fstream::write()是按如下方式声明的:
typedefsize_tstreamsize;
basic_ostream<charT,traits>&write(constchar_type*s,streamsizen);

这就是说,你仍然能够把int、long这样的基本类型当作循环计数器、数组下标、文件描述符等等使用。

指针的问题
某些库会把指针转换成整型,亦或把整型转换成指针。比如说标准<signal()>函数:
handlersignal(intsignum,handler);
在此,handler可以是一个指向函数的指针,或是两个整型常量SIG_DFL和SIG_IGN其一,POSIX<dlfcn.h>库在它的dlsym()函数中也采取这种用法。此处的问题是,指针大小是依赖于相关平台的,所以当然不会把一个64位指针存入到一个int中。推荐的做法是,使用intprt_t和unintptr_t这样的typedef,它们可作为安全保存指针的整型数据类型。

ABI的问题
应用程序二进制接口(ABI)指定了一种编程语言实体的二进制表示形式,包括名称改编(NameMangling)或者名称修饰(NameDecoration)方案、内存排列和默认对齐方式等等。请看如下结构:
structRecord{
longidx;
boolcached;
};

在一个典型的32位系统上,Record占8字节,然而,在64位系统上,会增加到12或16字节,因此,千万不要假定数据结构的大小是不变的,即使它是由定宽的数据成员组成,而目标平台的对齐方式仍会影响到它的总体大小。甚至抽象类也受ABI问题的影响:

classNetInterface
{
public:
intvirtualConnect(enumconn_type)=0;
//其他成员……
virtual~NetInterface()=0;
};

NetInterface类不包含非公有成员,像所有的多态类一样,它包含一个隐式的vptr,而其大小是依赖于特定平台的。同样地,函数的名称改编也是依赖于ABI的。以上表明,如果意外地把32位目标文件链接至64位库,或把64位目标文件链接至32位库,编译器都会给出明显的错误信息。

I/O格式化
如printf()一类的函数通常使用格式化标志,来控制输出的对齐、符号和宽度。请谨记以下守则:对指针使用“%p”标志、对类型参数使用l-前缀、对long的无符号类型使用ul-,不管是使用<iostream>或是<stdio.h>,都应检查是否有足够的空间保证输出。最后,必须保证对unsignedlong有20个字符及对指针有18个16进制字符的缓冲区。

由以上可以看到,只需对数据类型稍做调整,完美跨越32位及64位平台,并不是件难事!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: