为什么在 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"
is。 t
表示文本,r
表示已读。 (如果您正在使用 gcc
并链接到 MS 的 C 运行时,那么您将能够使用它)。
但是您仍然不会经常看到 t
,因为它是默认设置,因此是多余的;此设置的另一个选项是 b
,意思是 binary。但是 MS 似乎确实在他们的示例中明确使用 t
来明确表示翻译是有意的。
流的文本模式和二进制模式的行为是实现定义的,尽管其意图是binary mode 完全按照磁盘上出现的字符读取字符,text mode 可以执行与文本处理相关的翻译;最著名的是,将 MS 文本文件中的 \r\n
转换为程序中的 \n
。
我正在练习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"
is。 t
表示文本,r
表示已读。 (如果您正在使用 gcc
并链接到 MS 的 C 运行时,那么您将能够使用它)。
但是您仍然不会经常看到 t
,因为它是默认设置,因此是多余的;此设置的另一个选项是 b
,意思是 binary。但是 MS 似乎确实在他们的示例中明确使用 t
来明确表示翻译是有意的。
流的文本模式和二进制模式的行为是实现定义的,尽管其意图是binary mode 完全按照磁盘上出现的字符读取字符,text mode 可以执行与文本处理相关的翻译;最著名的是,将 MS 文本文件中的 \r\n
转换为程序中的 \n
。