我正在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");
模式&#34; tr&#34;不是有效模式(据我所知)。我期待gcc在编译上述程序时给出错误(或警告)。但是,gcc在编译时不会给出任何错误(或警告)。
然而,正如预期的那样,当我运行该程序时,它退出打印&#34;无法打开文件......&#34;这意味着fopen()返回NULL,因为打开文件时出错。
-bash-4.1$ ./a.out
Unable to open file...
-bash-4.1$
(文件poem.txt存在所以这是因为给fopen()的模式无效。我通过将模式更改为&#34; r&#34;来检查它是否正常显示&#34的内容;一下poem.txt&#34)
-bash-4.1$ ./a.out
THis is a poem.
-bash-4.1$
我期待gcc为无效模式发出错误(或警告)消息。
为什么gcc不会给出任何错误(或警告)?
答案 0 :(得分:2)
编译器没有检查你做了什么,它只检查语法。
但是,在运行时,如果代码是这样写的:
#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;
}
然后输出正确的错误消息:
... $ ./untitled
fopen for poem.txt失败:参数无效
答案 1 :(得分:1)
编译器如何知道函数的有效参数是什么?
要做到这一点,你将在编译器中构建太多的知识 - 它必须通过名称识别函数及其参数。如果要覆盖该功能怎么办?如果不同模式在不同平台上有效会怎样?
答案 2 :(得分:1)
这是未定义的行为:
根据附件J.2“未定义的行为”,如果出现以下是UDB:
- 调用fopen函数时mode参数指向的字符串与指定的字符序列之一不完全匹配(7.19.5.3)。
虽然附件J是提供信息的,但请参阅§7.19.5.3:
/ 3参数模式指向一个字符串。如果字符串是以下之一,则文件以指示的模式打开。否则,行为未定义。
基本上,编译器可以在这里吹你 - 标准库函数名称(和行为)可以在包含标准头之外使用(例如,非标准扩展,完全用户定义的行为等。 )。标准规定了符合标准的库实现应包括的内容及其行为方式,但不要求您使用该标准库(或定义明确指定为UDB区域的特定实现的行为:此时,如果您的参数类型与其匹配)法律职能电话)。
非常好的lint
实用程序可能会对您有所帮助。
答案 3 :(得分:0)
在Windows编程中,"tr"
是有效模式不是有效模式,尽管"rt"
is。 t
表示文字,r
表示读取。 (如果您正在使用gcc
并链接到MS的C运行时,那么您将能够使用它。)
但是你仍然经常看不到t
,因为它是默认的,因此是多余的;此设置的另一个选项是b
,意思是二进制。但MS似乎确实在他们的示例中明确使用t
来明确表示翻译是有意的。
流的文本模式和二进制模式的行为是实现定义的,尽管意图是二进制模式读取字符与它们出现在磁盘上完全一样,文本模式可以执行与文本处理相关的翻译;最着名的是,将MS文本文件中的\r\n
转换为程序中的\n
。