为什么在 fopen() 中使用无效模式时 gcc 不给出警告或错误?

Why gcc does not give a warning or error when invalid mode is used in fopen()?

我正在练习C中FILE IO的一些练习题,下面是其中一个程序。

#include<stdio.h>
#include<stdlib.h>

int main()
{
    char fname[]="poem.txt";
    FILE *fp;
    char ch;
    fp = fopen ( fname, "tr");
    if (fp == NULL)
    {
    printf("Unable to open file...\n");
    exit(1);
    }
    while((ch =fgetc(fp)) != EOF)
    {
            printf("%c",ch);
    }
    printf("\n");

    return 0;
}

正如你在声明中看到的那样

  fp = fopen ( fname, "tr");

模式 "tr" 不是有效模式(据我所知)。我期待 gcc 在编译上述程序时给出错误(或警告)。但是,gcc 在编译时不会给出任何错误(或警告)。

然而,正如预期的那样,当我 运行 程序退出打印 "Unable to open file..." 时,这意味着 fopen() 返回 NULL ,因为打开文件时出错。

-bash-4.1$ ./a.out
Unable to open file...
-bash-4.1$

(文件 poem.txt 存在,所以这是因为给 fopen() 的模式无效。我通过将模式更改为 "r" 进行检查,它可以正常显示 [= 的内容31=])

-bash-4.1$ ./a.out
THis is a poem.

-bash-4.1$

我原以为 gcc 会给出无效模式的错误(或警告)消息。

为什么 gcc 对此不给出任何错误(或警告)?

编译器应该如何知道函数的有效参数是什么?

要做到这一点,您将在编译器中积累太多知识 - 它必须通过名称识别函数及其参数。如果你想重写这个函数怎么办?如果不同的模式在不同的平台上有效呢?

这是未定义的行为:

根据附件 J.2 "Undefined Behavior",它是 UDB,如果:

—The string pointed to by the mode argument in a call to the fopen function does not exactly match one of the specified character sequences (7.19.5.3).

虽然附件 J 提供了信息,但请参阅 §7.19.5.3:

/3 The argument mode points to a string. If the string is one of the following, the file is open in the indicated mode. Otherwise, the behavior is undefined.

基本上,编译器可以在这里让你大吃一惊 - 标准库函数名称(和行为)可以在包含标准头文件之外使用(例如,非标准扩展,完全用户定义的行为, ETC。)。该标准指定了符合标准的库实现应包括的内容以及它的行为方式,但不要求您使用该标准库(或为明确指定为 UDB 领域的特定实现定义行为:此时,如果您的参数类型匹配一个合法的函数调用)。

一个非常好的 lint 实用程序可能会对您有所帮助。

编译器不检查你做了什么,它只检查语法。

但是,在运行的时候,如果代码是这样写的:

#include<stdio.h>
#include<stdlib.h>

int main()
{
    char fname[]="poem.txt";
    FILE *fp;
    char ch;

    fp = fopen ( fname, "tr");
    if (fp == NULL)
    {
        perror( "fopen for poem.txt failed");
        exit( EXIT_FAILURE );
    }

    while((ch =fgetc(fp)) != EOF)
    {
            printf("%c",ch);
    }
    printf("\n");

    return 0;
}

然后输出正确的错误信息:

...$ ./无标题

poem.txt 的 fopen 失败:参数无效

在Windows编程中,"tr"是有效模式不是有效模式,尽管"rt"ist 表示文本,r 表示已读。 (如果您正在使用 gcc 并链接到 MS 的 C 运行时,那么您将能够使用它)。

但是您仍然不会经常看到 t,因为它是默认设置,因此是多余的;此设置的另一个选项是 b,意思是 binary。但是 MS 似乎确实在他们的示例中明确使用 t 来明确表示翻译是有意的。

流的文本模式二进制模式的行为是实现定义的,尽管其意图是binary mode 完全按照磁盘上出现的字符读取字符,text mode 可以执行与文本处理相关的翻译;最著名的是,将 MS 文本文件中的 \r\n 转换为程序中的 \n