16

我最近阅读了对 Lua 联合创始人 Luiz H. de Figueredo 和 Roberto Ierusalimschy 的采访,他们讨论了 Lua 的设计和实现。至少可以说非常有趣。然而,讨论的一部分让我想到了一些事情。Roberto 将 Lua 称为“独立应用程序”(也就是说,它是纯粹的 ANSI C,不使用操作系统中的任何内容。)他说,Lua 的核心是完全可移植的,并且由于它的纯洁性,它可以被大量移植更容易,甚至从未考虑过的平台(例如机器人和嵌入式设备)。

现在这让我想知道。C 通常是一种非常可移植的语言。那么,C 的哪些部分(即标准库中的部分)是最不可移植的?哪些可以在大多数平台上运行?是否应该只使用一组有限的数据类型(例如避免short和可能float)?FILEstdio系统呢?mallocfree?Lua 似乎避免了所有这些。这是把事情推向极端吗?还是它们是可移植性问题的根源?除此之外,还可以做哪些其他事情来使代码具有极强的可移植性?

我问所有这些的原因是因为我目前正在用纯 C89 编写一个应用程序,并且它尽可能便携是最佳选择。我愿意在实现它时采取中间道路(足够便携,但没有那么多我必须从头开始编写所有内容。)无论如何,我只是想看看一般来说什么是编写最好的 C 代码的关键。

最后一点,所有这些讨论都只与 C89 相关。

4

6 回答 6

14

就 Lua 而言,我们对 C 语言本身没有太多抱怨,但我们发现 C 标准库包含许多看似无害且易于使用的函数,直到您认为它们没有检查它们的有效性输入(如果不方便也可以)。C 标准说处理错误输入是未定义的行为,允许这些函数做任何他们想做的事情,甚至使主机程序崩溃。例如,考虑 strftime。一些 libc 会简单地忽略无效的格式说明符,但其他 libc(例如,在 Windows 中)会崩溃!现在, strftime 不是一个关键功能。为什么要崩溃而不是做一些明智的事情?因此,Lua 必须在调用 strftime 之前对输入进行自己的验证,并将 strftime 导出到 Lua 程序成为一件苦差事。因此,我们试图通过实现内核的独立性来避免 Lua 内核中的这些问题。但是 Lua 标准库无法做到这一点,因为它们的目标是将设施导出到 Lua 程序,包括 C 标准库中可用的功能。

于 2011-02-15T11:23:01.480 回答
9

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

于 2011-02-15T00:36:07.710 回答
6

C89 允许两种类型的编译器:托管的和独立的。基本区别在于托管编译器提供所有 C89 库,而独立编译器只需要提供<float.h><limits.h><stdarg.h><stddef.h>. 如果您将自己限制在这些头文件中,您的代码将可移植到任何C89 编译器。

于 2011-02-15T00:52:41.417 回答
5

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

请注意,C 标准将某些内容指定为“实现定义的”;符合标准的程序将始终在任何符合标准的平台上编译和运行,但它的行为可能因平台而异。具体来说,有

  • 字大小sizeof(long)在一个平台上可能是四个字节,在另一个平台上可能是八个字节。short,int等的大小long都有一些最小值(通常相对于彼此),但除此之外没有任何保证。
  • 字节序int a = 0xff00; int b = ((char *)&a)[0];可以分配0b一个平台-1上,另一个平台上。
  • 字符编码\0始终是空字节,但其他字符的显示方式取决于操作系统和其他因素。
  • 文本模式 I/Oputchar('\n')可能在一个平台上产生一个换行符,在下一个平台上产生一个回车符,在另一个平台上产生一个组合。
  • char 的签名chara可以取负值,也可以不取。
  • 字节大小。虽然现在,一个字节几乎在任何地方都是 8 位,但 C 甚至可以迎合少数没有它的奇异平台。

各种字长和字节序很常见。在任何文本处理应用程序中都可能出现字符编码问题。9 位字节的机器最有可能在博物馆中找到。这绝不是一份详尽的清单。

(请不要写 C89,这是一个过时的标准。C99 为可移植性添加了一些非常有用的东西,例如固定宽度整数int32_t等。)

于 2011-02-15T01:02:29.073 回答
3

C 被设计成可以编写编译器来为任何平台生成代码,并将其编译的语言称为“C”。这种自由与 C 作为一种用于编写可在任何平台上使用的代码的语言相反。

任何为 C 编写代码的人都必须决定(有意或默认)int他们将支持什么大小;虽然可以编写适用于任何合法大小的 C 代码int,但它需要相当大的努力,并且生成的代码通常比为特定整数大小设计的代码可读性差得多。例如,如果一个变量x类型为uint32_t,并且希望将其与另一个变量相乘y,计算结果 mod 4294967296,则该语句x*=y;将在int32 位或更小,或int65 位或更大的平台上运行,但将调用Undefined Behavior在这种情况下int是 33 到 64 位,如果将操作数视为整数而不是包含 mod 4294967296 的代数环的成员,则乘积将超过INT_MAX. int可以通过将语句重写为来使语句与 的大小无关x*=1u*y;,但这样做会使代码不那么清晰,并且意外地1u*从乘法之一中省略 可能是灾难性的。

根据目前的规则,如果代码仅在整数大小符合预期的机器上使用,则 C 是合理可移植的。在大小int不符合预期的机器上,代码不太可能是可移植的,除非它包含足够的类型强制以使大多数语言的键入规则变得无关紧要。

于 2015-06-19T16:57:08.080 回答
2

任何属于 C89 标准的部分都应该可以移植到任何符合该标准的编译器。如果你坚持使用纯 C89,你应该能够相当容易地移植它。任何可移植性问题都将是由于编译器错误或代码调用特定于实现的行为的地方。

于 2011-02-15T00:37:26.847 回答