1.分层设计,隔离平台相关的代码。就像可测试性一样,可移植性也要从设计抓起。一般来说,最上层和最下层都不具有良好的可移植性。最上层是GUI,大多数GUI都不是跨平台的,如Win32 SDK和MFC。最下层是操作系统API,大多部分操作系统API都是专用的。

  如果这两层的代码散布在整个软件中,那么这个软件的可植性将非常的差,这是不言自明的。那么如何避免这种情况呢?当然是分层设计了:

  最底层采用Adapter模式,把不同操作系统的API封装成一套统一的接口。至于封装成类还是封装成函数,要看你采用的C还是C++写的程序了。这看起来很简单,其实不尽然(看完整篇文章后你会明白的),它将耗去你大量的时间去编写代码,去测试它们。采用现存的程序库,是明智的做法,有很多这样的库,比如,C库有glib(GNOME的基础类),C++库有ACE(ADAPTIVE Communication Environment)等等,在开发第一个平台时就采用这些库,可以大大减少移植的工作量。

  最上层采用MVC模型,分离界面表现与内部逻辑代码。把大部分代码放到内部逻辑里面,界面仅仅是显示和接收输入,即使要换一套GUI,工作量也不大。这同时也是提高可测试性的手段之一,当然还有其它一些附加好处。所以即使你采用QT或者GTK+等跨平台的GUI设计软件界面,分离界面表现与内部逻辑也是非常有用的。

  若做到了以上两点,程序的可移植性基本上有保障了,其它的只是技术细节问题。

  2.事先熟悉各目标平台,合理抽象底层功能。这一点是建立在分层设计之上的,大多数底层函数,像线程、同步机制和IPC机制等等,不同平台提供的函数,几乎是一一对应的,封装这些函数很简单,实现Adapter的工作几乎只是体力活。然而,对于一些比较特殊的应用,如图形组件本身,就拿GTK+ 来说吧,基于X Window的功能和基于Win32的功能,两者差巨大,除了窗口、事件等基本概念外,几乎没有什么相同的,如果不事先了解各个平台的特性,在设计时就精心考虑的话,抽象出来的抽口在另外一个平台几乎无法实现。

  3.尽量使用标准C/C++函数。大多数平台都会实现POSIX(Portable Operating System Interface)规定的函数,但这些函数较原生(Native) 函数来说,性能上的表现可能较次一些,用起来也不如原生函数方便。但是,最好不要贪图这种便宜而使用原生函数函数,否则搬起的石头最终会轧到自己的脚。比如,文件操作就用fopen之类的函数,而不要用CreateFile之类的函数等。

  4.尽量不要使用C/C++新标准里出现的特性。并不是所有的编译器都支持这些特性,像VC就不支持C99里面要求的可变参数的宏,VC对一些模板特性的支持也不全面。为了安全起见,这方面不要太激进了。

  5.尽量不要使用C/C++标准里没有明确规定的特性。比如你有多个动态库,每个动态库都有全局对象,而且这些全局对象的构造还有依赖关系,那你迟早会遇到麻烦的,这些全局对象构造的先后顺序在标准里是没有规定的。在一个平台上运行正确,在另外一个平台上可能莫明其妙的死机,最终还是要对程序作大量修改。
6.尽量不要使用准标准函数。有些函数大多数平台上都有,它们使用得太广泛了,以至于大家都把它们当成标准了,比如atoi(把字符串转换成整数)、strdup(克隆字符串)、alloca(在栈分配自动内存)等等。不怕一万,就怕万一,除非明白你在做什么,否则还是别碰它们为好。

  7.注意标准函数的细节。也许你不相信,即使是标准函数,抛开内部实现不论,就其外在表现的差异也有时令人惊讶。这里略举几个例子:

  int accept(int s, struct sockaddr *addr, socklen_t *addrlen);addr/ addrlen本来是输出参数,如果是C++程序员,不管怎么样,你已经习惯于初始化所有的变量,不会有问题。如果是C程序员,就难说了,若没有初始化它们,程序可能莫名其妙的crash,而你做梦也怀疑不到它头它。这在Win32下没问题,在Linux下才会出现。

  int snprintf(char *str, size_t size, const char *format, ……);第二个参数size,在Win32下不包括空字符在内,在Linux下包括空字符,这一个字符的差异,也可能让你耗上几个小时。

  int stat(const char *file_name, struct stat *buf);这个函数本身没有问题,问题出在结构stat上,st_ctime在Win32下代表创建(create)时间,在Linux下代表最后修改 (change)时间。

  FILE *fopen(const char *path, const char *mode);在读取二进制文件,没有什么问题。在读取文本文件可要小心,Win32下自动预处理,读出来的内容与文件实际都长度不一样,在Linux则没有问题。

  8.小心数据标准数据类型。不少人已经吃过int类型由16位转变成32位带来的苦头,这已经是陈年往事了,这里且不谈。你可知道char在有的系统上是有符号的,在有的系统是无符号的吗?你可知道wchar_t在Win32下是16位的,在Linux 下是32位的吗?你可知道有符号的1bit的位域,取值是0和-1而不是0和1吗?这些貌合神离的东东,端的是神出鬼没,一不小心着了它的道。

  9.最好不要使用平台独有的特性。比如Win32下DLL可以提供一个DllMain函数,在特定的时间,操作系统的Loader会自动调用这个函数。这类功能很好用,但最好不要用,目标平台可不能保证有这种功能。

  10.最好不要使用编译器特有的特性。现代的编译器都做很人性化,考虑得很周到,一些功能用起非常方便。像在VC里,你要实现线程局部存储,你都不调用TlsGetValue /Tls TlsSetValue之类的函数,在变量前加一个__declspec( thread )就行了,然而尽管在pthread里有类似的功能,却不能按这种方式实现,所以无法移植到Linux下。同样gcc也有很多扩展,是在VC或者其它编译器里所没有的。

  11.注意平台的特性。比如:

  在Win32下的DLL里面,除非明确指明为export的函数外,其它函数对外都是不可见的。而在Linux下,所有的非static的全局变量和函数,对外全部是可见的。这要特别小心,同名函数引起的问题,让你查上两天也不为过。

  目录分隔符,在Win32下用‘\\’,在Linux下用‘/’。

  文本文件换行符,在Win32下用‘\r\n’,在Linux下用‘\n’,在MacOS下用‘\r’。

  字节顺序(大端/小端),不同硬件平台的字节顺序可能不一样。

  字节对齐,在有的平台(如x86)上,字节不对齐,无非速度慢一点,而有的平台(如arm)上,它完全用错误的方式去读取数据,而且不会给你一点提示。若出问题,可能让你一点头绪都没有。

  12.最好清楚不同平台的资源限制。想必你还记得DOS下同时打开的文件个数限制在几十个的情形吧,如今操作系统的功能已经强大多了,但是并非没有限制。比如Linux下的共享内存默认的最大值是4M。若你对目标平台常见的资源限制了然于胸,可能有很大的帮助,一些问题很容易定位

http://www.cnblogs.com/guoxiaoqian/p/3984974.html

编写可移植C/C++程序的要点(12条)的更多相关文章

  1. 编写可移植C/C++程序的要点

    1.分层设计,隔离平台相关的代码.就像可测试性一样,可移植性也要从设计抓起.一般来说,最上层和最下层都不具有良好的可移植性.最上层是GUI,大多数GUI都不是跨平台的,如Win32 SDK和MFC.最 ...

  2. 编写优质嵌入式C程序

    前言:这是一年前我为公司内部写的一个文档,旨在向年轻的嵌入式软件工程师们介绍如何在裸机环境下编写优质嵌入式C程序.感觉是有一定的参考价值,所以拿出来分享,抛砖引玉. 转载请注明出处:http://bl ...

  3. 编写优质嵌入式C程序(转)

    前言:这是一年前我为公司内部写的一个文档,旨在向年轻的嵌入式软件工程师们介绍如何在裸机环境下编写优质嵌入式C程序.感觉是有一定的参考价值,所以拿出来分享,抛砖引玉. 转载请注明出处:http://bl ...

  4. 编写高性能Web应用程序的10个技巧

    这篇文章讨论了: ·一般ASP.NET性能的秘密 ·能提高ASP.NET表现的有用的技巧和窍门 ·在ASP.NET中使用数据库的建议 ·ASP.NET中的缓存和后台处理 使用ASP.NET编写一个We ...

  5. 编写Spark的WordCount程序并提交到集群运行[含scala和java两个版本]

    编写Spark的WordCount程序并提交到集群运行[含scala和java两个版本] 1. 开发环境 Jdk 1.7.0_72 Maven 3.2.1 Scala 2.10.6 Spark 1.6 ...

  6. 编写第一个Qt程序

    http://c.biancheng.net/view/1817.html 学习一种编程语言或编程环境,通常会先编写一个“Hello World”程序.我们也用 Qt Creator 编写一个“Hel ...

  7. 编写安全 PHP 应用程序的七个习惯

    编写安全 PHP 应用程序的七个习惯   在提及安全性问题时,需要注意,除了实际的平台和操作系统安全性问题之外,您还需要确保编写安全的应用程序.在编写 PHP 应用程序时,请应用下面的七个习惯以确保应 ...

  8. C#编写一个控制台应用程序,可根据输入的月份判断所在季节

    编写一个控制台应用程序,可根据输入的月份判断所在季节 代码: using System; using System.Collections.Generic; using System.Linq; us ...

  9. 搭建java开发环境、使用eclipse编写第一个java程序

    搭建java开发环境.使用eclipse编写第一个java程序 一.Java 开发环境的搭建 1.首先安装java SDK(简称JDK). 点击可执行文件 jdk-6u24-windows-i586. ...

随机推荐

  1. 使用Perl批量读取文件最后行

    使用Perl批量读取文件最后行 面对成百上千个文件,有时我们需要查看它的最后行,单个文件打开将耗费大量时间,而通过Perl提取出最后行,将快速的帮助我们处理繁琐的事务. 特性 整个目录完全遍历,自动提 ...

  2. mysql 悲观锁 的运用

    悲观锁: 它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态.悲观锁的实现,往往依靠数据库提供的锁机制(也只有数 ...

  3. USB 3.0规范中译本 第4章 超高速数据流模型

    本文为CoryXie原创译文,转载及有任何问题请联系cory.xie#gmail.com. 本章展示数据和信息如何在超高速上通过的一种高层次的描述.请阅读协议层一章关于低层次协议的细节.本章提供设备架 ...

  4. Thinking in UML 学习笔记(二)——UML核心视图之用例图

    在UML中,需求模型又称为用例模型,它主要用于描述系统的功能性需求,即软件可以实现的功能,如登录.注册.入库.出库.查看库存报表.增加员工信息等.常规的用例建模一般包括两个组成部分:绘制用例图和编写用 ...

  5. 二叉苹果树 - 二叉树树型DP

    传送门 中文题面: 题目描述 有一棵苹果树,如果树枝有分叉,一定是分 2 叉(就是说没有只有 1 个儿子的结点,这棵树共有N 个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一 ...

  6. WPF入门(三)->几何图形之线条(LineGeometry)

    原文:WPF入门(三)->几何图形之线条(LineGeometry) 前一章我们对wpf的xaml语言有了一定的了解,那么我们现在开始来学习如何使用wpf来画出几何图形. LineGeometr ...

  7. Docker + .NET Core(三)-两种发布方式

    原文:Docker + .NET Core(三)-两种发布方式 第一种,自己手写dockerfile发布,上传至hubDocker 正常发布到文件夹中,发布文件上传至linux机器上.如 /www/a ...

  8. 【b604】2K进制数

    Time Limit: 1 second Memory Limit: 50 MB [问题描述] 设r是个2K进制数,并满足以下条件: (1)r至少是个2位的2K进制数. (2)作为2K进制数,除最后一 ...

  9. 【codeforces 791C】Bear and Different Names

    [题目链接]:http://codeforces.com/contest/791/problem/C [题意] 给你n-k+1个限制 要求 a[i]..a[i]+k-1里面有相同的元素,或全都不同; ...

  10. 阿里云centos7.2自己安装mysql5.7远程不能访问解决方案

    版权声明:转载也行 https://blog.csdn.net/u010955892/article/details/72774920 最近,无意中看到阿里云服务器降价,所以一时手痒,买了一年的服务器 ...