为什么C的"fopen"将"const char*"作为第二个参数?

Chr*_*per 14 c file-io history

它总是让我感到奇怪,C函数"fopen"将"const char*"作为第二个参数.如果在stdio.h中定义了位掩码,比如"IO_READ"等,我认为读取代码和实现库代码会更容易,所以你可以这样做:

FILE* myFile = fopen("file.txt", IO_READ | IO_WRITE);
Run Code Online (Sandbox Code Playgroud)

它的实际存在方式是否存在程序化原因,还是仅仅是历史性的?(即"这就是它的方式.")

Jon*_*ler 10

我相信字符串而不是简单的位掩码的优点之一是它允许不是位设置的特定于平台的扩展。纯假设:

FILE *fp = fopen("/dev/something-weird", "r+,bs=4096");
Run Code Online (Sandbox Code Playgroud)

对于这个 Gizmo,open()需要告诉调用块大小,不同的调用可以使用完全不同的大小等。当然,现在 I/O 已经组织得很好(原来不是这样——设备非常多样化,并且访问机制远未统一),因此似乎很少有必要。但是字符串值的开放模式参数允许更好的可扩展性。

在 IBM 的大型机 MVS o/s 上,该fopen()函数确实按照此处描述的一般路线采用了额外的参数 — 正如Andrew Henle指出的(谢谢!)。手册页包括示例调用(稍微重新格式化):

FILE *fp = fopen("myfile2.dat", "rb+, lrecl=80, blksize=240, recfm=fb, type=record"); 
Run Code Online (Sandbox Code Playgroud)

底层open()必须通过ioctl()(I/O 控制)调用或fcntl()(文件控制)或隐藏它们的函数来增强以实现类似的效果。


Tuo*_*nen 6

一句话:遗产.不幸的是我们必须忍受它.

只是推测:也许在当时"const char*"似乎更灵活的解决方案,因为它不受任何限制.位掩码只能有32个不同的值.现在看起来像YAGNI.

更多猜测:Dudes很懒,写"rb"比MASK_THIS |需要更少的输入 MASK_THAT :)

  • “位掩码只能有 32 个不同的值”——当 C 被发明时,位掩码只能有 16 个不同的值。 (4认同)
  • 当 C 被发明时,open() 的第二个参数的类型是 int,int 有 16 位,unsigned 不存在,long 也不存在。open() 的第二个参数是一个用作位掩码的 int。由于 int 被用作位掩码,因此 16 位可以表示 16 个不同的值。 (4认同)
  • @Windows程序员:不,C没有位掩码类型。任何整型都可以使用(最好是无符号的),并且“unsigned long”至少为 32 位。 (2认同)
  • 请注意,在定义和描述标准 I/O 库的时代(Unix 7 版及更早版本),`open()` 系统调用只接受两个参数。如果 `open()` 因为文件不存在而失败,你必须使用名称和模式调用 `creat()` 来创建文件。 (2认同)

Pet*_*des 6

Dennis Ritchie(1993 年)写了一篇关于 C 的历史以及它如何从 B 逐渐演变的文章。一些设计决策的动机是避免对用 B 编写的现有代码或 C 的胚胎版本进行源代码更改。

特别是,Lesk 编写了一个“可移植 I/O 包”[Lesk 72],后来重新编写成为 C 的“标准 I/O”例程

C 预处理器直到 1972/3 才被引入,所以 Lesk 的 I/O 包是在没有它的情况下编写的! (在非常早期的尚未使用的 C 中,指针适合正在使用的平台上的整数,并且将隐式 int 返回值分配给指针是完全正常的。)

许多其他变化发生在 1972-3 前后,但最重要的是预处理器的引入,部分是在 Alan Snyder 的敦促下 [Snyder 74]

没有#includeand #define,像这样的表达IO_READ | IO_WRITE不是一种选择。

1972 年,fopen在没有 CPP 的情况下,可以在典型源中查看呼叫的选项是:

FILE *fp = fopen("file.txt", 1);       // magic constant integer literals
FILE *fp = fopen("file.txt", 'r');     // character literals
FILE *fp = fopen("file.txt", "r");     // string literals
Run Code Online (Sandbox Code Playgroud)

神奇的整数文字显然是可怕的,因此不幸的open(2)是,由于缺少预处理器,显然最有效的选项(Unix 后来采用的)被排除在外。

字符文字显然是不可扩展的;大概即使在当时,这对 API 设计人员来说也是显而易见的。但是对于 的早期实现来说已经足够(并且更有效)fopen:它们只支持单字符串,检查*modeis r, w, or a。(请参阅@Keith Thompson 的回答。)显然r+,读+写(不截断)是后来出现的。(有关fopen(3)现代版本,请参阅。)

C 确实字符数据类型(在 1971 年添加到 B 作为生产 C 的第一步,所以它在 1972 年仍然是新的。原始 B 没有char,已为将多个字符打包成一个单词的机器编写,char()索引字符串的函数也是如此!请参阅 Ritchie 的历史文章。)

使用单字节字符串有效地传递了一个char按常量引用,由于库函数不能内联,因此会产生所有额外的内存访问开销。(原始编译器可能没有内联任何东西,甚至是同一个编译单元中的琐碎函数(与 fopen 不同),它会缩小总代码大小以内联它们;现代风格的小辅助函数依靠现代编译器来内联它们。)


PS:Steve Jessop 用同样的话回答激发了我写这篇文章的灵感。

可能相关:strcpy() 返回值strcpy可能也很早就写好了。