编写可移植的C程序 – 需要考虑哪些事项?

对于大学的项目,我需要扩展现有的C应用程序,该应用程序最终将运行在各种商业和非商业unix系统(FreeBSD,Solaris,AIX等)上。

当我想编写一个最便携的C程序时,我需要考虑哪些事项?

我能给出的最好建议是每天搬到不同的平台,随时测试。
这将使平台差异像拇指一样突出,并同时教你可移植性问题。

保存跨平台测试到最后,将导致失败。

除此之外

  • 整数大小可能有所不同。
  • 浮点数可能表示不同。
  • 整数可以有不同的字节序。
  • 编译选项可能有所不同。
  • 包含文件名可以有所不同。
  • 位字段实现会有所不同。

通常最好将编译器警告级别设置得尽可能高,以查看编译器可以抱怨的各种事情。

我曾经编写C实用程序,然后我会支持16位到64位架构,包括一些60位机器。 它们包括至少三种“字节序”,不同的浮点格式,不同的字符编码和不同的操作系统(尽管Unix占主导地位)。

  1. 尽可能接近标准C。 对于不属于标准的函数/库,请使用尽可能广泛支持的代码库。 例如,对于网络,使用BSD套接字接口,零或少量使用低级套接字选项,带外信令等。要以最少的人员支持大量不同的平台,您将不得不停留具有普通的香草function。
  2. 要非常清楚标准所保证的是什么,这是典型的实现行为。 例如,指针的大小不一定与整数相同,指向不同数据类型的指针可能具有不同的长度。 如果您必须依赖于实现假设,请将其记录下来。 Lint,或–strict,或者您的开发工具集具有的等价物,在这里至关重要。
  3. 头文件是你的朋友。 使用实现定义的宏和常量。 使用标题定义和#ifdef来帮助隔离那些需要涵盖少量备选方案的实例。
  4. 不要假设当前平台使用EBCDIC字符和压缩十进制整数。 有相当数量的ASCII – 两个补充机器。 🙂

有了这些,如果你避免多次写东西和#ifdef代码的主要部分的诱惑,你会发现跨不同平台的编码和测试有助于更快地发现错误。 你最终会制作出更有纪律,可理解,可维护的程序。

  1. 使用至少两个编译器。
  2. 建立一个连续的构建系统,最好建立在各种目标平台上。
  3. 如果您不需要非常低级别的工作,请尝试使用一些提供抽象的库。 您不太可能找不到为您需要的东西提供良好抽象的第三方库。 例如,对于网络和通信,有ACE。 Boost(例如文件系统)也被移植到几个平台。 这些是C ++库,但也可能有其他C库(如curl)。
  4. 如果你必须在低级别工作,请注意平台偶尔会有不同的行为,即使在posix这些应该具有相同行为的东西上也是如此。 您可以查看上面库的源代码。

您可能需要及时了解的一个特定问题(例如,如果您的数据文件可以跨平台工作)是字节序 。

数字在不同体系结构的二进制级别上表示不同。 Big-endian系统首先排序最重要的字节,而little-endian系统首先排序最不重要的字节。

如果以一个字节顺序将一些原始数据写入文件,然后在具有不同字节顺序的系统上读回该文件,则显然会出现问题。

您应该能够在sys/param.h大多数系统上在编译时获得字节序。 如果需要在运行时检测它,一种方法是使用intchar并集,然后将char设置为1并查看int具有的值。

这是一个很长的清单。 最好的办法是阅读例子。 例如,perl的来源 。 如果你看一下perl的来源,你会看到一个巨大的构建头文件的过程,该文件处理大约50个平台问题。

读它,哭,或借。

该列表可能很长,但它并不像支持Windows和MSDOS那么长。 这对很多公用事业来说很常见。

通常的技术是将核心算法模块与处理操作系统的模块分开 – 基本上是分层抽象的策略。

相比之下,在几种unix之间进行区分是相当简单的。 坚持使用所有使用相同RTL名称的function,或者查看支持平台的多数约定和exception中的#ifdef

对于您使用的任何库函数,请不断参考POSIX标准。 标准的某些部分是模糊的,一些系统返回不同类型的错误代码。 这将帮助您主动找到那些真正难以找到略有不同的实现错误。