我已经有一段时间对编写操作系统很感兴趣。在浏览了几个不同的网站之后,我发现了一个有趣的概念(简单概括一下):如果你从#include开始编写引导程序,那么你已经犯了致命的错误。
我学习了K&R,整本书在每个课程中都包含了它。在学习C时使用了它,但是我不知道自己学到了什么可以不使用stdio。在C中没有stdio能做些什么?
我已经有一段时间对编写操作系统很感兴趣。在浏览了几个不同的网站之后,我发现了一个有趣的概念(简单概括一下):如果你从#include开始编写引导程序,那么你已经犯了致命的错误。
我学习了K&R,整本书在每个课程中都包含了它。在学习C时使用了它,但是我不知道自己学到了什么可以不使用stdio。在C中没有stdio能做些什么?
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.
stdio
代表标准输入/输出。正如其名称所述,它包含与标准 IO 相关的内容。stdio.h 的维基百科文章 列出了 stdio.h 的内容。如果你使用它们,你将需要 stdio.h。你还可以查看 stdio.h 的 man 页面 以获取更多细节。
至于操作系统部分,编写操作系统远不是简单的编程,即使是学术性的。在着手此事之前,您应该学习数据结构、算法、操作系统理论等。 UNIX操作系统设计 是一个很好的学习操作系统的书籍。Nachos 是一个学术性操作系统模拟程序,您也可以了解一下。如果您对操作系统非常着迷,那么您应该阅读Linus Torvalds的自传 Just for Fun。嗯,这不是一本技术书籍,但您会感受到编写操作系统的意义。
stdio是使用read()
和write()
函数实现的。如果这些函数没有被实现,你就不能使用像printf()
、fprintf()
、fgets()
等stdio函数。这并不意味着你不能写入到图形显示器、串口或其他设备中。这只是意味着你没有标准的工具来完成这些操作,需要调用自定义函数。
回答你的问题:在C语言中,你可以不使用stdio做任何事情。它本身就是用C语言编写的,你只是失去了一些在任何C标准库中实现的常见功能。
关于最小化C环境的其他答案都很好,但这里有另一个更抽象的角度。
C标准定义了两种“可观察的副作用”,基本上意味着您的程序执行的操作不能被编译器优化或修改。这些操作首先是调用I/O函数,其次是读取和写入volatile
对象。
如果您无法访问stdio
或其他依赖于操作系统存在的C实现部分,则无法使用I/O函数。这样,对volatile
对象的读取和写入就成为您的程序实际执行的唯一操作。实际上,您可能不必将所有内容都标记为volatile
,因为您的C实现可能提供了超出标准的保证。如果有一个CPU指令可以发出蜂鸣声,您的编译器将像处理I/O函数一样处理它(以及一般的内联汇编等),尽管它实际上并不是一个函数,等等。
正如taskinoor所提到的,维基百科提供了关于stdio.h包含内容的优秀参考。
至于没有使用stdio.h时可能实现的功能,几乎所有可以使用stdio.h实现的功能都是可以实现的,尽管对于某些事情你需要付出更多的努力。例如,如果你想使用文件,则必须定义自己的文件抽象,并为其工作定义自己的接口。如果你想从键盘读取输入,则需要使用汇编代码调用BIOS中断来代替调用scanf()
。
这就是操作系统编程的本质。在你定义它们及其API之前,这些高级构造都不存在。你需要从最基本的功能开始,逐步将它们组合起来创建越来越有用和复杂的构造。
#include
意味着引导加载程序不应使用 C 标准库中的任何函数?你是不是应该自己编写这些函数?我认为这样做肯定会让你的引导加载程序出现错误。 - Praetorian