C语言中哪些部分最具可移植性?

我最近阅读了一篇关于 Lua 联合创始人 Luiz H. de Figueredo 和 Roberto Ierusalimschy 的访谈,其中他们谈到了 Lua 的设计和实现,非常有趣。不过,其中一部分引发了我的思考。Roberto 把 Lua 称为“独立应用程序”(也就是说,它是纯 ANSI C,不使用操作系统中的任何东西)。他说,Lua 的核心完全可移植,并且由于它的“纯度”,已经能够更容易地移植到甚至从未考虑过的平台上(比如机器人和嵌入式设备)。

现在我就想知道,C 一般来说是一种非常可移植的语言。那么,C 中哪些部分(尤其是标准库中的部分)最不可移植?哪些部分可以在大多数平台上使用?是否应该仅使用有限的数据类型(例如,避免使用 short 和可能的 float)?FILEstdio 系统如何?mallocfree 呢? Lua 似乎避免了所有这些问题。这是否太过极端了?或者它们是可移植性问题的根源?除此之外,还有哪些其他方法可以使代码极度可移植?

我之所以问这些问题,是因为我正在纯 C89 中编写一个应用程序,希望它尽可能可移植。我愿意采取中间路线来实现它(足够可移植,但不至于让我从头开始写所有东西)。无论如何,我只是想看看写最好的 C 代码的关键是什么。

最后注意,所有这些讨论都与 C89 有关。

原文链接 https://stackoverflow.com/questions/4998822

点赞
stackoverflow用户599152
stackoverflow用户599152

“Freestanding”在 C 上下文中有特定的含义。大致上来说,独立主机不需要提供任何标准库,包括库函数 malloc / freeprintf 等。虽然某些标准头文件仍然是必需的,但它们仅定义类型和宏(例如 stddef.h)。

2011-02-15 00:36:07
stackoverflow用户79566
stackoverflow用户79566

任何符合C89标准的编译器应该都可以移植C89标准规定的任何内容。如果您坚持使用纯C89,则应该能够相对容易地进行移植。任何移植性问题都应归因于编译器错误或代码调用特定于实现的行为的地方。

2011-02-15 00:37:26
stackoverflow用户617069
stackoverflow用户617069

C89 允许两种类型的编译器:托管独立。基本区别在于,托管编译器提供了完整的 C89 库,而独立编译器只需提供 <float.h><limits.h><stdarg.h><stddef.h>。如果您只使用这些头文件,您的代码将能在 任何 C89 编译器上运行。

2011-02-15 00:52:41
stackoverflow用户166749
stackoverflow用户166749

这是一个非常广泛的问题。我不会给出确切的答案,而是会提出一些问题。

请注意,C标准将某些事物指定为“实现定义”;符合要求的程序将始终在任何符合要求的平台上编译并运行,但其行为可能因平台而异。具体而言,存在以下问题:

  • 字长sizeof(long)在一个平台上可能为四个字节,在另一个平台上可能为八个字节。 shortintlong等的大小都有一些最小值(通常相对于彼此),但除此之外没有任何保证。
  • 字节序int a = 0xff00; int b = ((char *)&a)[0]; 可能在一个平台上将 0 分配给 b,在另一个平台上将 -1 分配给 b
  • 字符编码\0始终是空字节,但其他字符如何显示取决于操作系统和其他因素。
  • 文本模式I / Oputchar('\n')在一个平台上可能产生换行符,另一个上可能产生回车,而在另一个平台上可能同时产生换行符和回车。
  • 字符的符号char 可能有可能承载负值。
  • 字节大小。虽然现在在几乎所有地方,一个字节等于八个比特,但 C 却为一些奇异的平台做了兼容。

各种字长和机器字节序往往很常见。 字符编码问题可能会在任何文本处理应用程序中出现。 拥有 9 位字节的机器可能最有可能出现在博物馆中。 这绝不是详尽无遗的列表。

(而且请不要写 C89,那是一种过时的标准。 C99 添加了一些非常有用的东西,如固定宽整数 int32_t 等,用于提高可移植性。)

2011-02-15 01:02:29
stackoverflow用户107090
stackoverflow用户107090

对于 Lua,我们对 C 语言本身没有太多的抱怨,但是我们发现 C 标准库包含许多函数,这些函数似乎是无害和直观的,直到你考虑到它们没有检查它们的输入是否有效(如果不方便那没关系)。 C 标准规定处理错误输入是未定义行为,允许这些函数做任何它们想做的事情,甚至是崩溃主机程序。例如,考虑 strftime。一些 libc 简单地忽略无效的格式说明符,但其他 libc(例如在 Windows 中)会崩溃!现在,strftime 不是一个关键的功能。为什么要崩溃而不是做一些明智的事呢?因此,Lua 必须在调用 strftime 之前自己验证输入,并导出 strftime 到 Lua 程序变得繁琐。因此,在 Lua 核心处,我们尝试避免这些问题,以针对核心的独立性为目标。但是 Lua 标准库不能这样做,因为它们的目标是向 Lua 程序导出设施,包括 C 标准库中可用的内容。

2011-02-15 11:23:01
stackoverflow用户363751
stackoverflow用户363751

C被设计成可以编写编译器以生成任何平台的代码,并称其为“C”语言。这种自由与C成为可在任何平台上使用的编写代码的语言形成了对立。

任何写C代码的人都必须决定(无论是故意还是默认)他们将支持什么大小的“int”。虽然可以编写可与任何合法大小的“int”一起使用的C代码,但需要做出相当大的努力,而且生成的代码通常比为特定整数大小设计的代码要难读得多。例如,如果有一个名为xuint32_t类型的变量,并且希望将其乘以另一个“y”,然后对4294967296取模得出结果,语句x *= y;将在int为32位或更小的平台以及int为65位或更大的平台上运行,但将在int为33到64位的情况下引发“未定义行为”,如果将操作数看作整数而不是代数环的成员,那么结果将超过“INT_MAX”。通过将其重写为x *= 1u * y;可以使语句不依赖于“int”的大小,但这样做会使代码不够清晰,并且意外地省略其中一个乘法中的1u *可能会是灾难性的。

根据现有规则,如果代码仅在整数大小符合预期的计算机上使用,则C是相当可移植的。在“int”大小不符合预期的计算机上,除非包含足够的类型强制转换使大多数语言的类型规则无关紧要,否则代码不太可能是可移植的。

2015-06-19 16:57:08