代码之家  ›  专栏  ›  技术社区  ›  Chris Cooper

为什么C的“fopen”把“const char*”作为第二个参数?

  •  13
  • Chris Cooper  · 技术社区  · 15 年前

    我一直觉得奇怪的是,C函数“fopen”将“const char*”作为第二个参数。我认为,如果stdio.h中定义了位掩码(如“io_read”等),那么读取代码和实现库代码会更容易,因此可以执行以下操作:

    FILE* myFile = fopen("file.txt", IO_READ | IO_WRITE);
    

    它的实际情况是有计划的原因的,还是只是历史性的?(即,“就是这样。”)

    8 回复  |  直到 7 年前
        1
  •  6
  •   Tuomas Pelkonen    15 年前

    一个词:遗产。不幸的是,我们必须忍受它。

    只是猜测:也许在那个时候,“const char*”看起来更灵活,因为它在任何方面都不受限制。位掩码只能有32个不同的值。在我看来现在像个雅格尼。

    更多的猜测:伙计们很懒惰,写“rb”比“mask”“mask”“that:”

        2
  •  4
  •   Michael Burr    15 年前

    我推测这是以下一种或多种情况(不幸的是,我无法快速找到任何支持性参考资料,因此这可能仍然是推测):

    1. Kernighan或Ritchie(或为 fopen() )只是碰巧喜欢用字符串而不是位图指定模式的想法
    2. 他们可能希望接口与Unix相似,但又明显不同。 open() 系统调用接口,因此它会立即熟悉,但不会错误地使用为UNIX定义的常量而不是由C库编译。

    例如,假设神话中的C标准 fopen()。 使用了位映射模式参数的 OPENMODE_READONLY 指定由模式字符串“r”指定的文件。现在,如果有人对在Unix平台上编译的程序进行了以下调用(并且 O_RDONLY 已包含):

    fopen( "myfile", O_RDONLY);
    

    不会有编译器错误,但是除非 打开模式只读 仅限于 被定义为相同的位,你会得到意想不到的行为。当然,将C标准名称定义为与Unix名称相同是有意义的,但也许他们希望避免需要这种耦合。

    再说一次,他们可能根本不会想到这件事……

        3
  •  4
  •   Keith Thompson    10 年前

    最早提到 fopen 我发现的是1978年出版的Kernighan&Ritchie的《C编程语言》(K&R1)的第一版。

    它显示了 FPEN 这大概是当时C标准库实现中代码的简化版本。这是这本书中代码的简略版本:

    FILE *fopen(name, mode)
    register char *name, *mode;
    {
        /* ... */
        if (*mode != 'r' && *mode != 'w' && *mode != 'a') {
            fprintf(stderr, "illegal mode %s opening %s\n",
                mode, name);
            exit(1);
        }
        /* ... */
    }
    

    看看代码, mode 应为1个字符的字符串(否 "rb" ,不区分文本和二进制)。如果传递的字符串较长,则会自动忽略第一个字符之后的任何字符。如果你通过了一个无效的 模式 ,函数将打印一条错误消息并终止程序,而不是返回一个空指针(我猜实际的库版本没有这样做)。这本书强调简单的代码而不是错误检查。

    很难确定,特别是考虑到这本书没有花很多时间解释 模式 参数,但为了方便起见,它似乎被定义为字符串。一个字符也可以,但字符串至少可以使将来的扩展成为可能(这本书没有提到)。

        4
  •  4
  •   Jonathan Leffler    7 年前

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

    FILE *fp = fopen("/dev/something-weird", "r+,bs=4096");
    

    对于这个小发明, open() 呼叫需要告知块大小,不同的呼叫可以使用完全不同的大小,等等。现在,I/O已经组织得很好了(原来不是这样的情况,设备的多样性和访问机制远没有统一),所以似乎很少有必要。但是字符串值的open-mode参数允许这种扩展性更好。

    在IBM的大型机MVS O/S上, fopen() 函数确实会沿着这里描述的常规行接受额外的参数 noted 通过 Andrew Henle (谢谢!)。手册页包括示例调用(稍微重新格式化):

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

    潜在的 打开() 必须通过 ioctl() (I/O控制)呼叫或 fcntl() (文件控制)或隐藏它们以达到类似效果的函数。

        5
  •  3
  •   Steve Jessop    15 年前

    丹尼斯·里奇有话要说,从 http://cm.bell-labs.com/cm/cs/who/dmr/chist.html

    特别是,Lesk写了一个 后来的I/O包'[lesk 72] 重新加工成C标准 I/O例行程序

    所以我说问问 Mike Lesk ,将结果发布在此处作为您自己问题的答案,并为此获得一堆分数。尽管你可能想让问题听起来不那么像批评;-)

        6
  •  2
  •   pm100    15 年前

    我必须说,我很感激——我知道键入“r”而不是io-open-flag-r,还是ioflag-r或sysflags-open-rmode或其他什么类型的

        7
  •  2
  •   Peter Cordes    7 年前

    丹尼斯·里奇(1993年)写道 an article about the history of C 以及它是如何从B逐渐演变而来的。一些设计决策的动机是避免对用B编写的现有代码或C的雏形版本进行源代码更改。

    特别是,Lesk写了一个 后来的I/O包'[lesk 72] 重新加工成C标准 I/O例行程序

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

    1972-3年期间发生了许多其他变化,但最重要的是引入了预处理器,部分原因是在艾伦·斯奈德(Alan Snyder)的敦促下进行的。

    没有 #include #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
    

    magic integer文本显然很糟糕,因此不幸的是,它显然是最有效的选项(Unix后来将其用于 open(2) )由于缺少预处理器而被排除在外。

    字符文本显然是不可扩展的;这对于API设计者来说是显而易见的。但对于早期实现 FPEN :它们只支持单个字符串,检查 *mode 存在 r , w ,或者 a . (见 @Keith Thompson's answer )显然 r+ 对于读+写(不截断),稍后会出现。(见 fopen(3) 现代版。)

    C做了 一种字符数据类型(添加到B 1971中作为产生胚胎C的第一步之一,因此它在1972年仍然是新的。原来B没有 char 已经为将多个字符打包成一个字的机器编写了,因此 char() 是一个索引字符串的函数!见里奇的历史文章。)

    使用单字节字符串有效地传递 烧焦 通过常量引用,因为库函数不能内联,所以内存访问的额外开销都会增加。(原始编译器可能不包含任何内容,甚至同一编译单元中的trival函数(与fopen不同),它会将总代码大小缩小到内联它们;现代风格的微型助手函数依赖于现代编译器来内联它们。)


    附言:史蒂夫·杰索普的回答同样激励了我写这篇文章。

    可能相关: strcpy() return value . strcpy 也可能写得很早。

        8
  •  0
  •   Platinum Azure    15 年前

    正如图马斯佩尔科宁所说,这是遗产。

    就我个人而言,我想知道是否有一些错误的SAP认为它是更好的,因为键入的字符更少?在过去,程序员的时间比现在更受重视,因为它不太容易被访问,编译器也不那么出色。

    这只是猜测,但我可以理解为什么有些人会喜欢在这里和那里保存一些字符(请注意,任何标准库函数名都没有冗长的内容…我提出了string.h的“strstrstr”和“strchr”可能是不必要的简洁性的最好例子。

    推荐文章