没有使用stdio的C语言,有哪些可能性?

7

我已经有一段时间对编写操作系统很感兴趣。在浏览了几个不同的网站之后,我发现了一个有趣的概念(简单概括一下):如果你从#include开始编写引导程序,那么你已经犯了致命的错误。

我学习了K&R,整本书在每个课程中都包含了它。在学习C时使用了它,但是我不知道自己学到了什么可以不使用stdio。在C中没有stdio能做些什么?


没有 #include 意味着引导加载程序不应使用 C 标准库中的任何函数?你是不是应该自己编写这些函数?我认为这样做肯定会让你的引导加载程序出现错误。 - Praetorian
'stdio' - 你是不是指的是'C运行时库'? - Andrey Sidorov
@ Praetorian - 我相信问题在于stdio中的许多函数都是通过系统调用实现的。如果在没有OS提供系统调用API的引导加载程序等环境中调用此类函数,它们将会自我销毁。无论如何,无论是引导加载程序本身提供这个API,还是在加载了提供该API的操作系统之后才调用任何这样的函数。因此,在这个意义上,如果需要它们,您应该自己编写。 - aroth
@aroth,是的,但是有许多适用于嵌入式环境(即没有操作系统)的库。请查看uclibc和newlib,它们是领先的开源候选者。 - djs
5个回答

4
ISO/IEC 9899:1999标准(C语言标准)认可两种实现类型(§4符合性,¶6):
  • Freestanding - where the implementation (compiler plus library) only provides seven headers:

    <float.h>
    <iso646.h>
    <limits.h>
    <stdarg.h>
    <stdbool.h>
    <stddef.h>
    <stdint.h>
    

    These provide basic facilities for the language and declare no functions (the facilities in <stdarg.h> are explicitly defined as macros in the standard). Note that it does not include complex numbers.

  • Hosted - where the implementation provides the complete library defined by the standard.

一个独立实现的主要目的是允许您编写任何所需代码,而不受标准库(特别是标准I/O函数)的限制。缺点是您不仅可以,而且必须提供这些函数,或者利用实现提供的替代品。请注意,在独立实现中,程序的入口点不需要被称为 "main"。
您正在寻找的是独立实现,或使用托管实现的独立部分。您将使用头文件(除非您疯了),但它们可能不会是标准C库头文件,除非列在可用于独立实现的头文件列表中。

在实践中,你最有可能得到一个完整的C标准库实现(而不是为自由环境定义的最小子集),并定义了各种钩子来实现所需的低级功能。这使你能够实现任何你需要的标准库子集。 - djs
这并不是很准确,这些头文件是自由编译器实现的必要最小限度。实际上,大多数自由编译器将实现整个ISO C标准。当您使用面向托管系统的编译器尝试为自由编译系统编写程序时,可能会遇到问题,因为库中可能存在API调用。 - Lundin

1

stdio 代表标准输入/输出。正如其名称所述,它包含与标准 IO 相关的内容。stdio.h 的维基百科文章 列出了 stdio.h 的内容。如果你使用它们,你将需要 stdio.h。你还可以查看 stdio.h 的 man 页面 以获取更多细节。

至于操作系统部分,编写操作系统远不是简单的编程,即使是学术性的。在着手此事之前,您应该学习数据结构、算法、操作系统理论等。 UNIX操作系统设计 是一个很好的学习操作系统的书籍。Nachos 是一个学术性操作系统模拟程序,您也可以了解一下。如果您对操作系统非常着迷,那么您应该阅读Linus Torvalds的自传 Just for Fun。嗯,这不是一本技术书籍,但您会感受到编写操作系统的意义。


一定会去买这本书,谢谢你的反馈。 - Matticus

1
任何引导程序几乎都会有 #include 指令,除非设计很差。我不知道你从哪里得到这个引用,但也许你误解了它的含义。引导程序将始终以一段纯汇编代码开始,用于进行初始低级处理器初始化以及初始化 C 运行时环境。这段代码简单来说是无法用 C 语言编写的。但是其余的代码可以像需要的那样复杂,只要在环境限制(内存等)范围内。
stdio 只是一组与某种可以接收输入和输出的设备连接的文件流。您可以实现 stdin、stdout、stderr 或任何它们的子集。您还可以为其实现文件系统,可以打开任意文件流。在现代操作系统中,这最终会连接到一种通常是某种终端的虚拟终端上,其中涉及相当多的层次,因为它是一个显示在您的监视器上的虚拟终端,因此需要图形驱动程序等。在原始的嵌入式系统上,stdio 可能连接到串口或 LED 显示器。

stdio是使用read()write()函数实现的。如果这些函数没有被实现,你就不能使用像printf()fprintf()fgets()等stdio函数。这并不意味着你不能写入到图形显示器、串口或其他设备中。这只是意味着你没有标准的工具来完成这些操作,需要调用自定义函数。

回答你的问题:在C语言中,你可以不使用stdio做任何事情。它本身就是用C语言编写的,你只是失去了一些在任何C标准库中实现的常见功能。


1

关于最小化C环境的其他答案都很好,但这里有另一个更抽象的角度。

C标准定义了两种“可观察的副作用”,基本上意味着您的程序执行的操作不能被编译器优化或修改。这些操作首先是调用I/O函数,其次是读取和写入volatile对象。

如果您无法访问stdio或其他依赖于操作系统存在的C实现部分,则无法使用I/O函数。这样,对volatile对象的读取和写入就成为您的程序实际执行的唯一操作。实际上,您可能不必将所有内容都标记为volatile,因为您的C实现可能提供了超出标准的保证。如果有一个CPU指令可以发出蜂鸣声,您的编译器将像处理I/O函数一样处理它(以及一般的内联汇编等),尽管它实际上并不是一个函数,等等。

换句话说,您将不得不使用低级硬件访问来实现引导加载程序应该实现的任何内容(这相当于:在屏幕上显示消息,建立操作系统运行所需的一些资源,加载并运行它)。这种访问当然完全取决于实现 - 硬件供应商可能会提供库来帮助您,但它们不会是标准的C头文件。

0

正如taskinoor所提到的,维基百科提供了关于stdio.h包含内容的优秀参考。

至于没有使用stdio.h时可能实现的功能,几乎所有可以使用stdio.h实现的功能都是可以实现的,尽管对于某些事情你需要付出更多的努力。例如,如果你想使用文件,则必须定义自己的文件抽象,并为其工作定义自己的接口。如果你想从键盘读取输入,则需要使用汇编代码调用BIOS中断来代替调用scanf()

这就是操作系统编程的本质。在你定义它们及其API之前,这些高级构造都不存在。你需要从最基本的功能开始,逐步将它们组合起来创建越来越有用和复杂的构造。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接