C程序可移植性
1. #define ,typedef ,sizeof() 的使用
(1) typedef
0. typedef使用方法 :
typedef oldtype newtype;
1. typedef定义与平台无关的新类型(提高程序可移植性) :
如:定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
typedef long double REAL;
在不支持 long double 的平台二上,改为:
typedef double REAL;
在连 double 都不支持的平台三上,改为:
typedef float REAL;
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。
2. typedef 不同于#define , 不是简单的文本替换,
而是定义了一个新类型。
如:typedef char * pStr;
char string[4] = "abc";
const char *p1 = string;
const pStr p2 = string;
p1++;
p2++;
是p2++出错了。这个问题再一次提醒我们:
typedef和#define不同,它不是简单的
文本替换。
3. typedef 定义 复杂的类型 :如函数指针。
(2)预定义宏的使用 :
写程序请一定要考虑到你今后可能换个编译器。所以请使用预定义宏!比如MS的编译器就事先定义了__MSC,而Borland的就预先定义了__BORLAND_C。Sunplus GCC编译器也肯定有这样的宏。举个例子:setvect函数在Borland的实现中就叫setvect,而MS C/C++中却是_dos_setvet。如果你仅仅是简单的用setvect,那你的程序就很可能失去了移植到MS C下的可能。不过如果你这么写:
2. int 类型的位数 :
不同机器字长的int 类型数据是不同的,16位机器int 类型是16位的,
32位int字长是32位。
所以最好使用sizeof(int)来表示int 类型的字长。
3. 字符是有符号数还是无符号数?
4. 分层设计,隔离平台相关的代码。
最上层和最下层都不具有良好的可移植性。
最上层是GUI,大多数GUI都不是跨平台的,如Win32 SDK和MFC。
最下层是API.
API(Application Program Interface),API对于程序员来说就是系统提供的接口,任何涉及系统调用都要通过API来完成。对于不同的操作系统都有不同的一套API,也就是说对于不同的操作系统系统调用的接口是完全不同的。所以在API层我们是不能移植的。我们经常会调用系统API,由于这些API在C语言中没有对其封装,所以我们只能用使用其原始的API,对于原始的API在各个操作系统中他们命名不同,就不能跨平台移植。
如:限制系统调用于一个函数中,减少对操作系统 的依赖。
解析:几乎所有的商业软件为了提高其执行速度,
都会含有依赖于特定操作系统的代码。
在C程序设计中,当必须 使用系统调用时,最好是将所有的系统调用
限制于一个主函数中,若要将此程序移植到两一个操作系统中,
只需要修改此函数,而其余函数可原封不动。
如:当需要调用系统功能以清屏,删行,置光标位置时,可生成
以下函数。
如:以下代码可以帮助我们实现各平台之间的可移植:
最底层采用Adapter模式,把不同操作系统的API封装成一套统一的接口。至于封装成类还是封装成函数,要看你采用的C还是C++写的程序了。这看起来很简单,其实不尽然(看完整篇文章后你会明白的),它将耗去你大量的时间去编写代码,去测试它们。采用现存的程序库,是明智的做法,有很多这样的库,比如,C库有glib(GNOME的基础类),C++库有ACE(ADAPTIVE Communication Environment)等等,在开发第一个平台时就采用这些库,可以大大减少移植的工作量。
最上层采用MVC模型,分离界面表现与内部逻辑代码。把大部分代码放到内部逻辑里面,界面仅仅是显示和接收输入,即使要换一套GUI,工作量也不大。这同时也是提高可测试性的手段之一,当然还有其它一些附加好处。所以即使你采用QT或者GTK+等跨平台的GUI设计软件界面,分离界面表现与内部逻辑也是非常有用的。
若做到了以上两点,程序的可移植性基本上有保障了,其它的只是技术细节问题。
5.事先熟悉各目标平台,合理抽象底层功能.
这一点是建立在分层设计之上的,大多数底层函数,像线程、同步机制和IPC机制等等,不同平台提供的函数,几乎是一一对应的,封装这些函数很简单,实现Adapter的工作几乎只是体力活。然而,对于一些比较特殊的应用,如图形组件本身,就拿GTK+ 来说吧,基于X Window的功能和基于Win32的功能,两者差巨大,除了窗口、事件等基本概念外,几乎没有什么相同的,如果不事先了解各个平台的特性,在设计时就精心考虑的话,抽象出来的抽口在另外一个平台几乎无法实现。
6.尽量使用标准C/C++函数。
大多数平台都会实现POSIX(Portable Operating System Interface)规定的函数,但这些函数较原生(Native) 函数来说,性能上的表现可能较次一些,用起来也不如原生函数方便。但是,最好不要贪图这种便宜而使用原生函数函数,否则搬起的石头最终会轧到自己的脚。比如,文件操作就用fopen之类的函数,而不要用CreateFile之类的函数等。
预定义宏,可以用来检测你的C语言编译器是不是一个标准C编译器:
7.小心数据标准数据类型:
不少人已经吃过int类型由16位转变成32位带来的苦头,这已经是陈年往事了,这里且不谈。你可知道char在有的系统上是有符号的,在有的系统是无符号的吗?你可知道wchar_t在Win32下是16位的,在Linux 下是32位的吗?你可知道有符号的1bit的位域,取值是0和-1而不是0和1吗?这些貌合神离的东东,端的是神出鬼没,一不小心着了它的道。
8. 内存对齐与字节序:
要避免写出需要内存对齐的或者对字节序有特殊要求的程序。