C语言中哪些部分最具可移植性?
我最近阅读了一篇关于 Lua 联合创始人 Luiz H. de Figueredo 和 Roberto Ierusalimschy 的访谈,其中他们谈到了 Lua 的设计和实现,非常有趣。不过,其中一部分引发了我的思考。Roberto 把 Lua 称为“独立应用程序”(也就是说,它是纯 ANSI C,不使用操作系统中的任何东西)。他说,Lua 的核心完全可移植,并且由于它的“纯度”,已经能够更容易地移植到甚至从未考虑过的平台上(比如机器人和嵌入式设备)。
现在我就想知道,C 一般来说是一种非常可移植的语言。那么,C 中哪些部分(尤其是标准库中的部分)最不可移植?哪些部分可以在大多数平台上使用?是否应该仅使用有限的数据类型(例如,避免使用 short
和可能的 float
)?FILE
和 stdio
系统如何?malloc
和 free
呢? Lua 似乎避免了所有这些问题。这是否太过极端了?或者它们是可移植性问题的根源?除此之外,还有哪些其他方法可以使代码极度可移植?
我之所以问这些问题,是因为我正在纯 C89 中编写一个应用程序,希望它尽可能可移植。我愿意采取中间路线来实现它(足够可移植,但不至于让我从头开始写所有东西)。无论如何,我只是想看看写最好的 C 代码的关键是什么。
最后注意,所有这些讨论都与 C89 有关。
原文链接 https://stackoverflow.com/questions/4998822
任何符合C89标准的编译器应该都可以移植C89标准规定的任何内容。如果您坚持使用纯C89,则应该能够相对容易地进行移植。任何移植性问题都应归因于编译器错误或代码调用特定于实现的行为的地方。
C89 允许两种类型的编译器:托管和独立。基本区别在于,托管编译器提供了完整的 C89 库,而独立编译器只需提供 <float.h>
、<limits.h>
、<stdarg.h>
和 <stddef.h>
。如果您只使用这些头文件,您的代码将能在 任何 C89 编译器上运行。
这是一个非常广泛的问题。我不会给出确切的答案,而是会提出一些问题。
请注意,C标准将某些事物指定为“实现定义”;符合要求的程序将始终在任何符合要求的平台上编译并运行,但其行为可能因平台而异。具体而言,存在以下问题:
- 字长。
sizeof(long)
在一个平台上可能为四个字节,在另一个平台上可能为八个字节。short
,int
,long
等的大小都有一些最小值(通常相对于彼此),但除此之外没有任何保证。 - 字节序。
int a = 0xff00; int b = ((char *)&a)[0];
可能在一个平台上将0
分配给b
,在另一个平台上将-1
分配给b
。 - 字符编码。
\0
始终是空字节,但其他字符如何显示取决于操作系统和其他因素。 - 文本模式I / O。
putchar('\n')
在一个平台上可能产生换行符,另一个上可能产生回车,而在另一个平台上可能同时产生换行符和回车。 - 字符的符号。
char
可能有可能承载负值。 - 字节大小。虽然现在在几乎所有地方,一个字节等于八个比特,但 C 却为一些奇异的平台做了兼容。
各种字长和机器字节序往往很常见。 字符编码问题可能会在任何文本处理应用程序中出现。 拥有 9 位字节的机器可能最有可能出现在博物馆中。 这绝不是详尽无遗的列表。
(而且请不要写 C89,那是一种过时的标准。 C99 添加了一些非常有用的东西,如固定宽整数 int32_t
等,用于提高可移植性。)
对于 Lua,我们对 C 语言本身没有太多的抱怨,但是我们发现 C 标准库包含许多函数,这些函数似乎是无害和直观的,直到你考虑到它们没有检查它们的输入是否有效(如果不方便那没关系)。 C 标准规定处理错误输入是未定义行为,允许这些函数做任何它们想做的事情,甚至是崩溃主机程序。例如,考虑 strftime。一些 libc 简单地忽略无效的格式说明符,但其他 libc(例如在 Windows 中)会崩溃!现在,strftime 不是一个关键的功能。为什么要崩溃而不是做一些明智的事呢?因此,Lua 必须在调用 strftime 之前自己验证输入,并导出 strftime 到 Lua 程序变得繁琐。因此,在 Lua 核心处,我们尝试避免这些问题,以针对核心的独立性为目标。但是 Lua 标准库不能这样做,因为它们的目标是向 Lua 程序导出设施,包括 C 标准库中可用的内容。
C被设计成可以编写编译器以生成任何平台的代码,并称其为“C”语言。这种自由与C成为可在任何平台上使用的编写代码的语言形成了对立。
任何写C代码的人都必须决定(无论是故意还是默认)他们将支持什么大小的“int”。虽然可以编写可与任何合法大小的“int”一起使用的C代码,但需要做出相当大的努力,而且生成的代码通常比为特定整数大小设计的代码要难读得多。例如,如果有一个名为x
的uint32_t
类型的变量,并且希望将其乘以另一个“y”,然后对4294967296取模得出结果,语句x *= y;
将在int
为32位或更小的平台以及int
为65位或更大的平台上运行,但将在int
为33到64位的情况下引发“未定义行为”,如果将操作数看作整数而不是代数环的成员,那么结果将超过“INT_MAX”。通过将其重写为x *= 1u * y;
可以使语句不依赖于“int”的大小,但这样做会使代码不够清晰,并且意外地省略其中一个乘法中的1u *
可能会是灾难性的。
根据现有规则,如果代码仅在整数大小符合预期的计算机上使用,则C是相当可移植的。在“int”大小不符合预期的计算机上,除非包含足够的类型强制转换使大多数语言的类型规则无关紧要,否则代码不太可能是可移植的。
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
“Freestanding”在 C 上下文中有特定的含义。大致上来说,独立主机不需要提供任何标准库,包括库函数
malloc
/free
,printf
等。虽然某些标准头文件仍然是必需的,但它们仅定义类型和宏(例如stddef.h
)。