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

Posted

技术标签:

【中文标题】编写可移植的 C 程序 - 需要考虑哪些事项?【英文标题】:Writing a portable C program - which things to consider? 【发布时间】:2010-02-20 20:06:23 【问题描述】:

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

当我想编写一个最便携的 C 程序时,我必须考虑哪些事情?

【问题讨论】:

考虑***.com/questions/1257923/… 和***.com/questions/61499/making-portable-code,事实上,大部分***.com/search?q=[c]+portable。 【参考方案1】:

我能给出的最佳建议是每天转移到不同的平台,随时进行测试。 这将使平台差异变得像大拇指一样突出,同时教会你可移植性问题。

把跨平台测试留到最后,会导致失败。

除此之外

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

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

【讨论】:

虽然这是非常明智的建议 (+1),但对初学者来说,您是否会说这主要适用于更高级的 C,而不是大多数人认为的那样,"直接”的代码——例如,不关心整数是 2 字节还是 200 字节的代码?我的意思是,Hello WorldHello World,对吧? 没有。我全面声称它。在工作中,我们正在将代码移植到新版本的 g++。由于缺少包含文件,之前正在编译的代码无法编译。保持较高的警告级别,并尽可能多地使用多个编译器和多个平台。顺便说一句,我们在我们的域中维护 CRUD/控制台应用程序。【参考方案2】:

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

    尽可能接近标准 C。对于不属于标准的函数/库,请使用尽可能广泛支持的代码库。例如,对于网络,使用 BSD 套接字接口,零使用或最少使用低级套接字选项、带外信令等。为了以最少的人员支持大量不同的平台,你必须留下具有普通的功能。 非常了解标准所保证的内容,以及典型的实施行为。例如,指针的大小不一定与整数相同,指向不同数据类型的指针可能具有不同的长度。如果您必须做出依赖于实现的假设,请彻底记录它们。 Lint 或 --strict 或您的开发工具集具有的任何等效项在这里都至关重要。 头文件是你的朋友。使用实现定义的宏和常量。使用标头定义和#ifdef 来帮助隔离那些您需要涵盖少量备选方案的实例。 不要假设当前平台使用 EBCDIC 字符和压缩十进制整数。也有相当数量的 ASCII - 二进制补码机器。 :-)

尽管如此,如果您避免多次编写代码和#ifdef 主要部分代码的诱惑,您会发现跨不同平台进行编码和测试有助于更快地发现错误。你最终会产生更规范、更易于理解、更易于维护的程序。

【讨论】:

我认为 C 标准库在每个 *nix 系统上都是相同的。 “标准”就这么多…… C 标准库是一样的。许多程序使用 C 标准之外的东西,例如进程间通信。除此之外,“最便携”可能会超越 Unix 系统。 The Single UNIX Specification Version 3 有一个 Interface Tables 的文档,旨在为迁移到或开发适用于单一 UNIX 规范版本 3 的应用程序的程序员提供快速参考指南。Migration Guide 在这里。我想如果您正在考虑选择一个功能,请参考接口表以查看哪些平台要求它。如果数量不多,请考虑另一种方法。【参考方案3】:
    至少使用两个编译器。 有一个持续的构建系统,最好在各种目标平台上构建。 如果您不需要非常底层的工作,请尝试使用一些提供抽象的库。您不太可能找不到为您需要的东西提供良好抽象的第三方库。例如,对于网络和通信,有 ACE。 Boost(例如文件系统)也被移植到多个平台。这些是 C++ 库,但也可能有其他 C 库(如 curl)。 如果您必须在低级别工作,请注意平台偶尔会出现不同的行为,即使在 posix 等本应具有相同行为的东西上也是如此。您可以查看上面库的源代码。

【讨论】:

+1 表示使用多个编译器的想法。是否有任何工具可以自动执行此操作?我想从技术上讲,使用 Makefile 并交换 $(CC) 和 $(FLAGS) 的值可能会做到这一点,但如果有一个守护进程或可以在后台验证这一点的东西会很酷......跨度> 使用 cmake 或其他一些“便携式构建工具”(参见例如***.com/questions/3349956/portable-c-build-system)。例如,请参阅我的项目 cmake 文件(这些是位于 crackpot.svn.sourceforge.net/viewvc/crackpot/trunk/crackpot 及其基目录的 .txt 文件)。【参考方案4】:

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

数字在不同架构上的二进制级别表示不同。 Big-endian 系统优先排序最高有效字节,little-endian 系统优先排序最低有效字节。

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

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

【讨论】:

【参考方案5】:

这是一个很长的列表。最好的办法是阅读示例。例如source of perl。如果您查看 perl 的源代码,您会看到构建一个处理大约 50 个平台问题的头文件的巨大过程。

读完哭泣,或借阅。

【讨论】:

这是一个完全不同的问题。这是perl的来源。它应该让你嚎啕大哭。【参考方案6】:

列表可能很长,但还不如支持 Windows 和 MSDOS。这在许多实用程序中都很常见。

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

相比之下,区分几种风格的 unix 相当简单。要么坚持所有功能都使用相同的 RTL 名称,要么查看支持的平台的多数约定以及例外情况中的 #ifdef

【讨论】:

【参考方案7】:

对于您使用的任何库函数,请继续参考 POSIX 标准。部分标准不明确,一些系统返回不同风格的错误代码。这将帮助您主动发现那些确实很难发现略有不同的实现错误。

【讨论】:

以上是关于编写可移植的 C 程序 - 需要考虑哪些事项?的主要内容,如果未能解决你的问题,请参考以下文章

开发要出售的基于 Web 的应用程序时需要考虑哪些常见事项 [关闭]

编写可移植 C/C++ 程序的一些要点

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

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

编写可移植 C/C++ 程序的一些要点

可移植/可互操作的 WCF 合同