如果这是一个主观或重复的问题,我道歉.搜索有点尴尬,所以我不确定要包含哪些术语.
我想知道的是当你不包括像stdio
和的标准库时,C语言中的基础工具/功能是什么stdlib
.
如果没有printf()
,我该怎么办fopen()
?
另外,这些库在技术上是"C"语言的一部分,还是只是非常有用且有效的基本库?
C标准就是这样说的(5.1.2.3/5):
符合实施的最低要求是:
- 在序列点处,易失性对象在先前访问完成且后续访问尚未发生的意义上是稳定的.
- 在程序终止时,写入文件的所有数据应与根据抽象语义产生的程序执行的结果相同.
- 交互设备的输入和输出动态应按照7.19.3的规定进行.
因此,如果没有标准库函数,程序保证具有的唯一行为与易失性对象的值有关,因为您不能使用任何保证文件访问或"交互设备"."Pure C"仅通过标准库函数提供交互.
但是,Pure C并不是全部,因为您的硬件可能具有某些地址,这些地址在读取或写入时会执行某些操作(无论是SATA或PCI总线,原始视频内存,串行端口,还是要发出哔哔声,或者闪烁的LED).因此,了解您的硬件,您可以在不使用标准库函数的情况下在C中进行大量编写.可能您可以实现C标准库,尽管这可能需要访问特殊的CPU指令以及特殊的内存地址.
但是在纯C中,没有扩展,并删除了标准库函数,除了读取命令行参数,做一些工作,并从中返回状态代码之外,你基本上不能做任何事情main
.虽然你的唯一资源是自动和静态变量,没有堆分配,但它仍然没有被嗅到,它仍然是图灵完全受资源限制.它不是一个非常丰富的编程环境.
标准库是C语言规范的一部分,但在任何语言中都倾向于在"如此"的语言和库之间划线.这是一个概念上的差异,但最终在原则上并不是一个非常重要的差异,因为标准说他们聚在一起.任何做非标准事情的人都可以像删除图书馆一样轻松删除语言功能.无论哪种方式,结果都不是C的一致实现.
请注意,C的"独立"实现只需要实现标准的子集,包括不包括任何I/O,因此您处于我上面描述的位置,依赖于特定于硬件的扩展来完成任何有趣的事情. .如果你想根据标准区分"核心语言"和"库",那么这可能是绘制线的好地方.
你能做什么?一切!
除了预处理器之外,C中没有魔法.
最难的,也许是写putchar - 因为这是平台相关的I/O.
创建自己的varargs版本是一个很好的本科练习,一旦你有了它,你可以使用自己的vaprintf版本,然后是printf和sprintf.
1986年,当我对使用Lightspeed C提供的stdio例程不满意时,我在Macintosh上完成了所有操作 - 使用win_putchar,win_printf,in_getchar和win_scanf编写了我自己的窗口处理程序.
整个过程称为bootstrapping,它可以是编码中最令人满意的体验之一 - 使用基本设计,具有相当大的实际意义.
如果您不需要它们,您当然没有义务使用标准库.相当多的嵌入式系统要么没有标准库支持,要么因某种原因无法使用它.该标准甚至专门讨论了没有库支持的实现,C99标准5.1.2.1"独立环境":
在独立环境中(可以在没有操作系统任何好处的情况下执行C程序),程序启动时调用的函数的名称和类型是实现定义的.除了第4节要求的最小集合之外,任何可用于独立程序的库设施都是实现定义的.
通过C99所要求的报头是在一个独立的实行都可以
,
,
,
,
,
,和
.这些头只定义类型和宏,因此不需要函数库来支持它们.
如果没有标准库,您完全依赖于您自己的代码,可能可用的任何非标准库,以及您可能能够与之交互的任何操作系统系统调用(可能被视为非标准库)调用).很可能你必须让你的C程序调用程序集例程来连接设备和/或平台上的任何操作系统.
如果没有printf(),fopen()等,我该怎么办?
只要您知道如何连接您正在使用的系统,您就可以在没有标准C库的情况下生存.在只有几千字节内存的嵌入式系统中,您可能根本不想使用标准库.
这是一个Hello World!不使用任何标准C函数的Linux和Windows上的示例:
例如,在Linux上,您可以直接在内联汇编中调用Linux系统调用:
/* 64 bit linux. */ #define SYSCALL_EXIT 60 #define SYSCALL_WRITE 1 void sys_exit(int error_code) { asm volatile ( "syscall" : : "a"(SYSCALL_EXIT), "D"(error_code) : "rcx", "r11", "memory" ); } int sys_write(unsigned fd, const char *buf, unsigned count) { unsigned ret; asm volatile ( "syscall" : "=a"(ret) : "a"(SYSCALL_WRITE), "D"(fd), "S"(buf), "d"(count) : "rcx", "r11", "memory" ); return ret; } int _start() { const char hwText[] = "Hello world!\n"; sys_write(1, hwText, sizeof(hwText)); sys_exit(12); return 0; }
用以下内容编译:
gcc -nostdlib nostd.c
它输出Hello world!
并退出.
在Windows上,系统调用不会被发布,而是隐藏在另一个抽象层kernel32.dll之后.无论您是否想要,程序启动时始终会加载哪个.所以你可以简单地包含windows.h并使用Win32 API:
#includeint _start() { const char str[] = "Hello world!\n"; HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE); DWORD written; WriteFile(stdout, str, sizeof(str), &written, NULL); ExitProcess(12); return 0; }
这windows.h
与标准C库无关,因为您应该能够使用任何其他语言编写Windows程序.
您可以使用MinGW工具编译它,如下所示:
gcc -nostdlib C:\Windows\System32\kernel32.dll nostdlib.c
然后编译器足够聪明,可以解析导入依赖项并编译程序.
如果你反汇编程序,你只能看到你的代码在那里,其中没有标准的库膨胀.
因此,您可以在没有标准库的情况下使用C.