【问题标题】:Are functions such as printf() implemented differently for Linux and Windows诸如 printf() 之类的函数在 Linux 和 Windows 中的实现方式是否不同
【发布时间】:2018-12-21 04:24:06
【问题描述】:

我还没有完全理解的东西。例如,标准 C 函数,如 printf()scanf(),它们处理将数据发送到标准输出或从标准输入获取数据。实现这些功能的源代码是否会因我们是在 Windows 还是 Linux 上使用而有所不同?

我猜快速回答是“是”,但它们真的必须不同吗?

我可能是错的,但我的猜测是实际的函数代码是相同的,但最终被这些函数调用的操作系统的底层函数是不同的。所以任何编译器都可以编译这些相同的 C 函数,但它是链接之后的(这些函数依赖于在较低层上工作)是什么给了我们所需的行为?

【问题讨论】:

  • 为什么要强制它们相同?
  • 库函数只需要执行符合标准,无论如何实现。
  • 操作系统有很多不同,因此实现C标准的函数的编写方式也有很多不同。对于 printf() 等 I/O 函数,您需要担心与代码集和语言环境相关的问题,例如,它们在 Windows 和 Unix 上的处理方式不同。
  • printf 中通常没有操作系统依赖项,因此它可以可移植地实现——如果有一个可移植的FILE * 和其他 stdio 基础设施来实现它的条款,没有。 (虽然理论上你可以用putchar 编写一个 100% 可移植的printf。)但是往往会有其他平台依赖关系潜入printf,比如使用%p 打印指针的适合平台的方式,以及任何平台扩展或限制。 (往往有很多。微软已经对printf 进行了很多修改,FSF 也有。)

标签: c printf scanf stdio


【解决方案1】:

是的,它们有不同的实现。

此外,您可能在同一个操作系统上使用多个不同的实现。例如:

  • MinGW 附带了自己的标准库实现,与 MSVC 使用的不同。
  • 即使对于 Linux,也有许多不同的 C 库实现:glibcmusldietlibc 等。

显然,这意味着社区中存在一些代码重复,但有很多充分的理由:

  • 人们对如何实施和测试事物有不同的看法。仅此一项就足以“分叉”项目。
  • 许可证:实现对它们的使用方式施加了一些限制,并且可能需要最终用户执行一些操作(在某些情况下,GPL 要求您共享代码)。不是每个人都能遵守这些要求。
  • 人们有非常不同的需求。有些环境是多线程的,有些则不是。 printf 可能需要也可能不需要使用某些线程同步机制。有些人需要语言环境支持,有些人不需要。所有这些最终都会使代码膨胀,并不是每个人都愿意为他们不使用的东西付费。甚至strerror 也是vastly different on different OSes
  • 上述同步机制通常是特定于操作系统的,并且以特定的方式工作。语言环境处理、信号处理和其他事情也是如此,包括实际的数据写入和读取。
  • 一些实现添加了非标准扩展,可以让您的生活更轻松。并非所有这些在其他操作系统上都有意义。例如 glibc 添加'e' 模式说明符以使用O_CLOEXEC 标志打开文件。这对 Windows 没有意义。
  • 许多复杂的东西不能用纯 C 实现,需要一些特定于编译器的扩展。这可以将实现与有限数量的编译器联系起来。

最后,拥有许多 C 库比尝试创建一个万能的实现要简单得多。

【讨论】:

    【解决方案2】:

    实现这些功能的源代码会不会不同 取决于我们是在 Windows 还是 Linux 上使用它们?

    大概吧。它甚至可能在不同的 Linux 和不同的 Windows 程序上有所不同。有几种不同的 C 标准库实现可用于 Linux,甚至可能不止一种用于 Windows。不同的实现会有不同的实现代码,否则会涉及到律师。

    我的猜测是实际功能代码相同,但较低 最终被这些调用的操作系统的层函数 功能不同。那么任何编译器都可以编译这些相同的 C 功能,但它是之后链接的内容(这些功能是什么 依赖于在较低层上工作)是我们需要的 行为?

    可以想象,标准库函数将以一种将环境依赖抽象到某个较低层的方式编写,以便这些函数本身的相同源可以在多个环境中使用,具有某种环境-特定的兼容层。由于 GNU C 库支持多种环境,它作为一般原则的示例,尽管 Windows 不在它支持的环境之列。然而,即便如此,即使在链接阶段之前,环境区分也将是有效的。不同的环境有多种二进制格式。

    然而,在实践中,您不太可能看到您描述的 Windows 和 Linux 情况。

    【讨论】:

      【解决方案3】:

      正如您所说的 printf 之类的实现的更高级别部分,例如用于使用参数格式化字符串的代码,可以以跨平台方式编写并在 Linux 和 Windows 之间共享。我不确定是否有一个 C 库可以做到这一点。

      但是要与硬件交互或使用其他操作系统工具(例如当 printf 写入控制台时),libc 实现必须使用操作系统的接口:system calls。这些在 Windows 和 Unix-likes 之间非常不同,甚至在 Unix-likes 之间也不同(POSIX 指定了很多,但有特定于操作系统的扩展)。例如在这里您可以找到LinuxWindows 的系统调用表。

      【讨论】:

        【解决方案4】:

        像 printf() 这样的函数有两个部分。第一部分解析格式字符串,并组装一个准备输出的字符数组。如果这部分是用 C 编写的,那么没有理由阻止它在所有 C 库中通用,也没有理由阻止它与众不同,只要实现 printf() 所做的标准定义。碰巧的是,不同的库开发人员已经阅读了标准对 printf() 的定义,并提出了不同的解析和作用于格式字符串的方法。 他们中的大多数都正确地做到了。

        第二部分,将这些字符输出到标准输出的位,是不同之处所在。这取决于使用内核系统调用接口;它是负责输入/输出的内核/操作系统,并且以特定方式完成。让 Linux 内核输出字符所需的源代码与让 Windows 输出字符所需的源代码非常不同。

        在 Linux 上,通常使用 glibc;这使用 printf() 做了一些复杂的事情,在管道中缓冲输出字符,直到输出换行符,然后才调用 Linux 系统调用以在屏幕上显示字符。这意味着来自不同线程的 printf() 调用被巧妙地分开,每个线程都在自己的行上。但是相同的程序源代码,针对另一个 Linux 的 C 库编译,不一定会做同样的事情,导致不同线程的 printf() 输出都混乱且不可读。

        也没有理由为什么包含 printf() 的库应该用 C 编写。只要遵守 C 编译器使用的相同函数调用约定,您就可以用汇编程序编写它(尽管那会是有点生气!)。或者 Ada(调用约定可能有点棘手……)。

        【讨论】:

          【解决方案5】:

          实现这些功能的源代码会不会不同

          让我们尝试另一种观点:竞争。

          没有。 C 规范不要求行业中的竞争对手共享源代码以发布兼容的编译器——各种标准 C 库开发人员也不会总是想要。

          C 不需要“开源”。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-05-05
            • 2017-12-29
            • 1970-01-01
            • 2018-01-09
            相关资源
            最近更新 更多