多数情况下,编写完全可移植的程序代码是不可能的。因为同样的数据类型在不同的编译环境下所产生的结果(OBJ代码)可能是不同的,特别是针对嵌入式系统,不同的运行平台可能要求不同的代码来实现它所要求的独特功能。为了增加程序代码可移植到多个平台的可行性,比较好的方法是提供一个可移植的数据或功能接口,让那些移植的部分隐藏在这些接口之后,当然,这样的事情应该全部是系统设计的工作。下面介绍有关可移植性编程的一些常规做法:
1、数据大小或长度相关性
C程序库所提供的“sizeof()”函数是一个很好的可移植的功能接口范例,对于不同的嵌入式系统的编译环境或平台,某些数据类型的大小或长度被解析成不一样的结果,而在程序体中,对这些数据类型的访问有十分严格的要求。所以在这种情况下,对这些数据类型的定义必须考虑到在不同环境的共享,也就是说,数据类型的定义将成为可移植的数据接口。例如,程序中可能有对8位、16位和32位的整数类型数据的访问的要求,为了增加程序代码的可移植性,惯常的做法是把这些整数以全局类型定义在某个H头文件中。例如:
<script type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>
typedef signed char INT8;
typedef unsigned char UINT8;
typedef signed int INT16;
typedef unsigned int UINT16;
typedef signed long INT32;
typedef unsigned long UINT32;
2、字节位序
不同的CPU,例如PowerPC和Inter X86系列,对于字节顺序的解析是完全相反的。也就是说,对于高字节在前面还是低字节在前面,它们的处理方法是截然不同的。这是又CPU内部寄存器的存储和访问机制决定的,也就是我们常说的大端模式和小端模式。这样的特点对程序中的字节和位操作将会有相当大的影响,所以可移植性编程应该将涉及位操作的程序设计成仅仅与固定的位序相关,变量或类型同样也定义成与CPU相关的数据接口,例如:
typedef struct{
#if LittleEndian
word hiword;
word loword;
#else
word <script type="text/javascript">
</script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script> loword;
word hiword;
#endif
} DWord;
3、位操作
在嵌入式系统开发中,基于存储空间的限制,我们经常利用位来表示某些设备或操作的状态,也就是说,位操作是一种使用十分频繁并高效的操作。同样位序也和CPU相关,所以习惯上将位的定位定义为一些宏,从而提高它们的可移植性。例如:
#define BYTE_BIT0 0x01
#define BYTE_BIT1 0x02
#define BYTE_BIT2 0x04
#define BYTE_BIT3 0x08
#define BYTE_BIT4 0x10
#define BYTE_BIT5 0x20
#define BYTE_BIT6 0x40
#define BYTE_BIT7 0x80
<script type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>
4、对齐
对齐同样与CPU紧密相关,有些微处理器定义和要求严格的8位、16位或32位对齐,也就是说,对存储地址的访问或数据的读写必须以8位、16位或32位方式对齐,这样可能产生误操作,从而导致系统的不稳定或崩溃。因此,在可移植性编程中,应该对涉及此类操作的函数定义为可移植的接口函数。