下载本文示例代码
为16、32、64位架构编写可移植代码 与16位相比,32位意味着程序更快、可直接寻址访问更多的内存和更好的处理器架构。鉴于此,越来越多的程序员已经开始考虑利用64位处理器所带来的巨大优势了。 克雷研究(Cray Research 应为品牌名)计算机已经开始使用64位字,并可访问更大的内存地址。然而,作为正在向开发标准软件和与其他操作系统相互努力的一部分,我们已经停止了移植那些原本基于32位处理器的代码。事实上,我们不断遇到我们称之为“32位主义”的代码,这些代码都是在假定机器字长为32位的情况下编写的,因而很难移植这种代码,所以必须确立一些简单的指导方针,以便助于编写跨16、32、64位处理器平台的代码。 由于有一些遗留问题,C语言在数据类型和数据构造方面,显得有点过剩了。可以使用的不仅仅是char、short、int和long类型,还有相应的unsigned(无符号)类型,当然你可在结构(structure)和联合(union)中混合使用,可以在联合中包含结构,再在结构中包含联合,如果还嫌数据类型不够复杂,还可以转换成比特位,当然也可以把一种数据类型转换成另一种你想要的数据类型。正是因为这些工具太强大,如果不安全地使用它们,就有可能会伤到自己了。 高级代码的高级结构 在Kernighan和Plauger经典的《The Elements of Programming Style》一书中,他们的建议是“选择使程序看上去简单的数据表示法”。对个人而言,这意味着为高级编程使用高级数据结构,而低级编程使用低级数据结构。 在我们移植的程序中,有一个常见的32位主义bug,不如拿它来做个例子,科内尔大学编写的用于网间互访的路由协议引擎,在伯克利网络环境下(指TCP/IP),自然是使用inet_addr( )来把表示Internet地址的字符串转换成二进制形式。Internet地址碰巧也是32位的,就如运行伯克利网络系统的其他计算机一样,都是有着同样的字宽。 但也存在着有关Internet地址的高级定义:in_ addr结构。这个结构定义包括了子域s_ addr,它是一个包含了Internet地址的unsigned long标量。inet_addr()接受一个指向字符的指针,并且返回一个unsigned long,在转换地址字符串过程中如果发生错误,inet_addr将返回-1。 程序Gated读取以文本格式存放Internet地址的配置文件,并把它们放入sockaddr_in(这是一个包含了结构in_addr的高级结构)。例1(a)中的代码可以在32位电脑上正常运行,但移植到克雷研究计算机上,却无法运行,为什么呢? 例1:高级代码的高级结构 (a)
struct sockaddr_in saddrinchar *str;if ((saddrin.sin_addr.s_addr = inet_addr(str)) == (unsigned long)-1) { do_some_error_handling;} (b)
struct sockaddr_in saddrinchar *str;if (inet_aton(str, &saddrin.sin_addr) ! = OK) { do_some_error_handling;} 因为只要inet_addr能够正确地解析字符串,那么一切OK。当inet_addr在64位计算机上返回一个错误时,这段代码却未能捕捉到。你必须要考虑比较语句中的数据位宽,来确定到底是哪出了错。 首先,inet_addr返回错误值——unsigned long -1,在64位中表示为比特位全为1,这个值被存储在结构in_addr下的子域s_addr中,而in_addr必须是32位来匹配Internet地址,所以它是一个32比特位的unsigned int(在我们的编译器上,int是64位)。现在我们存储进32个1,存储进的值将与unsigned long -1比较。当我们存储32个1于unsigned int时,编译器自动把32位提升为64位;这样,数值0x00000000 ffffffff与0xffffffff ffffffff的比较当然就失败了。这是一个很难发现的bug,特别是在这种因为32位到64位的隐式提升上。 那我们对这个bug怎么办呢?一个解决方法是在语句中比较0xffffffff,而不是-1,但这又会使代码更加依赖于特定大小的对象。另一个方法是,使用一个中间的unsigned long变量,从而在把结果存入sockaddr_in前,执行比较,但这会让程序代码更复杂。 真正的问题所在是,我们期望一个unsigned long值与一个32位量(如Internet地址)相等。Internet地址必须以32位形式进行存储,但有些时候用一个标量,来访问这个地址的一部分,是非常方便的。在32位字长的电脑中,用一个long数值(常被当作32位)来访问这个地址,看上去没什么问题。让我们暂时不想一个低级的数据项(32位Internet地址)是否与一个机器字相等,那么高级数据类型结构in_addr就应该被一直使用。因为in_addr中没有无效值,那么应有一个单独的状态用作返回值。 解决方案是定义一个新的函数,就像inet_addr那样,但返回一个状态值,而且接受一个结构in_addr作为参数,参见例1(b)。因为高级的数据元素可以一直使用,而返回的值是没有溢出的,所以这个代码是可以跨架构移植的,而不管字长是多少。虽然伯克利发布了NET2,其中的确定义了一个新的函数inet_aton(),但如果试着改变inet_addr()中的代码,将会损坏许多程序。 低级代码的低级结构 低级编程意味着直接操纵物理设备或者特定协议的通讯格式,例如,设备驱动程序经常使用非常精确的位模式来操纵控制寄存器。此外,网络协议通过特定的比特位模式传输数据项时,也必须适当地转译。 为了操纵物理数据项,此处的数据结构必须准确地反映被操纵的内容。比特位是一个不错的选择,因为它们正好指定了比特的位数及排列。事实上,正是这种精确度,使比特位相对于short、int、long,更好地映像了物理结构(short、int、long会因为电脑的不同而改变,而比特位不会)。 当映像一个物理结构时,是通过定义格式来使比特位达到这种精度的,这就使你必须一直使用一种编码风格来访问结构。此时的每个位域都是命名的,你写出的代码可直接访问这些位域。当访问物理结构时,有件事可能你并不想去做,那就是使用标量数组(short、int、or long),访问这些数组的代码都假定存在一个特定的比特位宽,当移植这些代码到一台使用不同字宽的电脑上时,就可能不正确了。 在我们移植PEX图像库时遇到的一个问题,就涉及到其映像的协议消息结构。在某台电脑上,如果int整型的长度与消息中的元素一样,那例2(a)中的代码就会工作得很正常。32位的数据元素在32字长的电脑上没有问题,拿到64位的克雷计算机上,它就出错了。对例2(b)而言,不单要改变结构定义,还要改变所有涉及到coord数组的代码。这样,摆在我们面前就有两个选择,要么重写涉及此消息的所有代码,要么定义一个低级的结构和一个高级的结构,然后用一段特殊的代码把数据从一个拷贝到另一个当中,不过我也不期望可以找出每个对zcoord = draw_ msg.coord[2]的引用,而且,当现在需要移植到一个新架构上时,把所有的代码都改写成如例2(c)所示无疑是一项艰苦的工作。这个特殊的问题是由于忽视字长的不同而带来的,所以不能假设在可移植的代码中机器字长、short、int、long都是具有同样的大小。 例2:低级代码的低级结构 (a)
struct draw_msg { int objectid; int coord[3];} (b)
struct draw_msg { int objectid:32; int coord1:32; int coord2:32; int coord3:32;} (c)
int *iptr, *optr, *limit;int xyz[3];iptr = draw_msg.coord;limit = draw_msg.coord sizeof(draw_msg.coord);optr = xyz;while (iptr < limit)*optr = *iptr ;共2页。 1 2 :
为16、32、64位架构编写可移植代码 与16位相比,32位意味着程序更快、可直接寻址访问更多的内存和更好的处理器架构。鉴于此,越来越多的程序员已经开始考虑利用64位处理器所带来的巨大优势了。 克雷研究(Cray Research 应为品牌名)计算机已经开始使用64位字,并可访问更大的内存地址。然而,作为正在向开发标准软件和与其他操作系统相互努力的一部分,我们已经停止了移植那些原本基于32位处理器的代码。事实上,我们不断遇到我们称之为“32位主义”的代码,这些代码都是在假定机器字长为32位的情况下编写的,因而很难移植这种代码,所以必须确立一些简单的指导方针,以便助于编写跨16、32、64位处理器平台的代码。 由于有一些遗留问题,C语言在数据类型和数据构造方面,显得有点过剩了。可以使用的不仅仅是char、short、int和long类型,还有相应的unsigned(无符号)类型,当然你可在结构(structure)和联合(union)中混合使用,可以在联合中包含结构,再在结构中包含联合,如果还嫌数据类型不够复杂,还可以转换成比特位,当然也可以把一种数据类型转换成另一种你想要的数据类型。正是因为这些工具太强大,如果不安全地使用它们,就有可能会伤到自己了。 高级代码的高级结构 在Kernighan和Plauger经典的《The Elements of Programming Style》一书中,他们的建议是“选择使程序看上去简单的数据表示法”。对个人而言,这意味着为高级编程使用高级数据结构,而低级编程使用低级数据结构。 在我们移植的程序中,有一个常见的32位主义bug,不如拿它来做个例子,科内尔大学编写的用于网间互访的路由协议引擎,在伯克利网络环境下(指TCP/IP),自然是使用inet_addr( )来把表示Internet地址的字符串转换成二进制形式。Internet地址碰巧也是32位的,就如运行伯克利网络系统的其他计算机一样,都是有着同样的字宽。 但也存在着有关Internet地址的高级定义:in_ addr结构。这个结构定义包括了子域s_ addr,它是一个包含了Internet地址的unsigned long标量。inet_addr()接受一个指向字符的指针,并且返回一个unsigned long,在转换地址字符串过程中如果发生错误,inet_addr将返回-1。 程序Gated读取以文本格式存放Internet地址的配置文件,并把它们放入sockaddr_in(这是一个包含了结构in_addr的高级结构)。例1(a)中的代码可以在32位电脑上正常运行,但移植到克雷研究计算机上,却无法运行,为什么呢? 例1:高级代码的高级结构 (a)
struct sockaddr_in saddrinchar *str;if ((saddrin.sin_addr.s_addr = inet_addr(str)) == (unsigned long)-1) { do_some_error_handling;} (b)
struct sockaddr_in saddrinchar *str;if (inet_aton(str, &saddrin.sin_addr) ! = OK) { do_some_error_handling;} 因为只要inet_addr能够正确地解析字符串,那么一切OK。当inet_addr在64位计算机上返回一个错误时,这段代码却未能捕捉到。你必须要考虑比较语句中的数据位宽,来确定到底是哪出了错。 首先,inet_addr返回错误值——unsigned long -1,在64位中表示为比特位全为1,这个值被存储在结构in_addr下的子域s_addr中,而in_addr必须是32位来匹配Internet地址,所以它是一个32比特位的unsigned int(在我们的编译器上,int是64位)。现在我们存储进32个1,存储进的值将与unsigned long -1比较。当我们存储32个1于unsigned int时,编译器自动把32位提升为64位;这样,数值0x00000000 ffffffff与0xffffffff ffffffff的比较当然就失败了。这是一个很难发现的bug,特别是在这种因为32位到64位的隐式提升上。 那我们对这个bug怎么办呢?一个解决方法是在语句中比较0xffffffff,而不是-1,但这又会使代码更加依赖于特定大小的对象。另一个方法是,使用一个中间的unsigned long变量,从而在把结果存入sockaddr_in前,执行比较,但这会让程序代码更复杂。 真正的问题所在是,我们期望一个unsigned long值与一个32位量(如Internet地址)相等。Internet地址必须以32位形式进行存储,但有些时候用一个标量,来访问这个地址的一部分,是非常方便的。在32位字长的电脑中,用一个long数值(常被当作32位)来访问这个地址,看上去没什么问题。让我们暂时不想一个低级的数据项(32位Internet地址)是否与一个机器字相等,那么高级数据类型结构in_addr就应该被一直使用。因为in_addr中没有无效值,那么应有一个单独的状态用作返回值。 解决方案是定义一个新的函数,就像inet_addr那样,但返回一个状态值,而且接受一个结构in_addr作为参数,参见例1(b)。因为高级的数据元素可以一直使用,而返回的值是没有溢出的,所以这个代码是可以跨架构移植的,而不管字长是多少。虽然伯克利发布了NET2,其中的确定义了一个新的函数inet_aton(),但如果试着改变inet_addr()中的代码,将会损坏许多程序。 低级代码的低级结构 低级编程意味着直接操纵物理设备或者特定协议的通讯格式,例如,设备驱动程序经常使用非常精确的位模式来操纵控制寄存器。此外,网络协议通过特定的比特位模式传输数据项时,也必须适当地转译。 为了操纵物理数据项,此处的数据结构必须准确地反映被操纵的内容。比特位是一个不错的选择,因为它们正好指定了比特的位数及排列。事实上,正是这种精确度,使比特位相对于short、int、long,更好地映像了物理结构(short、int、long会因为电脑的不同而改变,而比特位不会)。 当映像一个物理结构时,是通过定义格式来使比特位达到这种精度的,这就使你必须一直使用一种编码风格来访问结构。此时的每个位域都是命名的,你写出的代码可直接访问这些位域。当访问物理结构时,有件事可能你并不想去做,那就是使用标量数组(short、int、or long),访问这些数组的代码都假定存在一个特定的比特位宽,当移植这些代码到一台使用不同字宽的电脑上时,就可能不正确了。 在我们移植PEX图像库时遇到的一个问题,就涉及到其映像的协议消息结构。在某台电脑上,如果int整型的长度与消息中的元素一样,那例2(a)中的代码就会工作得很正常。32位的数据元素在32字长的电脑上没有问题,拿到64位的克雷计算机上,它就出错了。对例2(b)而言,不单要改变结构定义,还要改变所有涉及到coord数组的代码。这样,摆在我们面前就有两个选择,要么重写涉及此消息的所有代码,要么定义一个低级的结构和一个高级的结构,然后用一段特殊的代码把数据从一个拷贝到另一个当中,不过我也不期望可以找出每个对zcoord = draw_ msg.coord[2]的引用,而且,当现在需要移植到一个新架构上时,把所有的代码都改写成如例2(c)所示无疑是一项艰苦的工作。这个特殊的问题是由于忽视字长的不同而带来的,所以不能假设在可移植的代码中机器字长、short、int、long都是具有同样的大小。 例2:低级代码的低级结构 (a)
struct draw_msg { int objectid; int coord[3];} (b)
struct draw_msg { int objectid:32; int coord1:32; int coord2:32; int coord3:32;} (c)
int *iptr, *optr, *limit;int xyz[3];iptr = draw_msg.coord;limit = draw_msg.coord sizeof(draw_msg.coord);optr = xyz;while (iptr < limit)*optr = *iptr ;共2页。 1 2 :
下载本文示例代码
新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程新时代的门前 32位世界中的64位编程
阅读(116) | 评论(0) | 转发(0) |