【问题标题】:Is C platform (H/W + S/W) dependent or independentC 平台 (H/W + S/W) 依赖还是独立
【发布时间】:2025-12-05 17:00:02
【问题描述】:

如果您使用所有平台通用的所有标准 C 库编写 C 程序,无论是 Unix(任何风格)还是 Windows(即 stdio.h、math.h 等),并使用标准编译器(即 GCC)并在任何一个相同的 CPU 架构上运行它,为什么二进制文件不能在符合所有上述条件的不同机器上运行?

当我在 X86 Mac 上仅使用标准库编译 C 程序时,为什么编译后的二进制文件不能在相同 CPU 架构的 Linux 或 Windows 下运行?

是因为在 C 运行时发生了特定于平台的库链接还是什么?

【问题讨论】:

  • 这是一堵文字墙,最后你会尖叫。这不是格式化问题的方式。
  • 我的眼睛受伤了...请整理格式
  • 这是我在 SO 上见过的格式最差的问题。你应该得到奖励。
  • 以防万一您不知道:大多数互联网用户可能会考虑详尽的大写用法大喊大叫。如果你改变这一点,你可能会得到更严肃的答案,因为大多数人并不真正喜欢被人骂。
  • 您可能正在编写与操作系统无关的代码,但编译器使用的库不是。例如:printf 调用特定于操作系统的函数来写入控制台。

标签: c gcc


【解决方案1】:

可执行二进制文件不仅仅是源代码和所需库的机器语言的组合,但现在跳过它的答案仍然是否定的:您要链接的库特定于它们要使用的系统继续运行。例如,您质疑 mac 与 linux ......每个系统都有自己的调用方式(例如,对于 I/O),如果您的可执行文件将这些项目链接到其中(而不是通过共享对象),你从一开始就注定要失败。

回到“特定于系统”...不仅系统调用之类的问题是一个问题,而且还有不止一种可执行的二进制格式,这在很多时候会给你带来不只是轻微的痛苦。例如,OS X 使用“Mach-O”二进制格式,而 Linux 使用“ELF”格式。当然,在这些文件中,您会发现相同可执行文件的类似代码,但您会发现该数据的不同二进制布局,这使得在一个系统上看起来很简单的内容在另一个系统上完全未知。许多示例之一... Mach-O 格式嵌入在可执行文件中,其动态加载的库应位于的位置——例如,绝对位置或相对于加载可执行文件的位置。这个概念(据我所知)既不是 ELF 也不是 Windows“PE”格式。

【讨论】:

  • 那么你在说什么(如果我错了,请纠正我)在 C 二进制文件的构建阶段,事实上我使用了标准 C 库例程,如 printf 或 scanf(其中是任何平台上任何 C lib 的一部分)并不重要,因为它们最终会调用操作系统特定的系统例程,这些例程也会参与构建过程。最终的输出文件,如果在不同的操作系统上运行(使用与最初运行可执行文件相同的 GCC 运行时,以及相同的 CPU 架构)将不会解析其绑定和链接(系统例程),因为它们不会定位?
  • @rahulbsb 你基本上已经明白了。您正在调用标准函数,但标准化在函数签名和操作中;没有跨平台的标准化实现使这些库系统依赖。绑定和链接的结果可能是您描述的两个问题,甚至可能同时出现。可能有一些实现在程序加载时无法绑定,但更糟糕的是,它可能在使用不同编码的系统上找到正确的函数,例如 printf 的write() 调用unlink()
  • 这是否意味着在编译时用 Java 等解释性语言编写的代码生成的字节码文件不包括这些操作系统特定的系统调用,除了包括标准的 java API 或库......然后当它们在运行时通过 JVM它们涉及所有特定于操作系统的系统调用。
  • 是的,就是这个意思。运行时本身特定于它显式运行的系统,因此它的字节码可以保持独立。 (不过,这助长了“编译一次,到处运行”的神话,因为通常还有其他系统差异有时会导致问题。)
  • 通过“Mach-O 文件嵌入非绝对位置”,您的意思是它添加了可重定位地址,这些地址随后经过地址转换以映射到实际内存中......对吗?也许我对此很遥远,但是特定程序的虚拟地址空间的这些可重定位地址部分是否需要在物理 m/m 中进行内存映射才能开始最终执行..所以这意味着指针指向的地址不是虚拟地址而是物理地址..我对此有点困惑。
【解决方案2】:

忽略问题第 ~10 行下面的文字墙,二进制文件不独立于操作系统的一些基本原因:

  • 每个操作系统都有一个可执行加载器,它执行某些操作,例如加载依赖库、设置堆栈指针等...
  • 即使 CPU 架构相同,操作系统架构也不同,并且一些 C 库函数是内联实现的,导致特定操作系统的实现被编译到您的可执行文件中。
  • 函数调用的工作方式可能不同(例如 Windows 与 Linux 就是这种情况)。

所有这些实际上都是由操作系统定义的 ABI(应用程序二进制接口)的一部分(有时是编译器,如果操作系统实现由于专利问题而无法实现)。

我能想到的最后一个原因:

  • C 运行时不同;标准定义了很多东西实现,这意味着很多 C 库在这些方面有所不同。理论上,可以为所有操作系统架构实现一个可移植的 libc,但由于操作系统界面中隐藏的细节(在 Windows 或 Mac 的情况下)或结构差异(Linux 与 BSD),这通常是不可能的,最终导致不同的实现。

【讨论】:

    【解决方案3】:

    也请阅读Is C++ platform dependent?

    1. 它回答了你的问题
    2. 它会告诉你如何提问

    【讨论】: