我有一个带有 main() 函数的 C 程序:
int main(int argc, char *argv[])
{
FILE *f = fopen(argv[1], "r");
...
}
请注意,在执行程序时,它期望提供一个文件名作为第一个参数,例如,
main test.dat
当我这样运行时,程序运行得很好。
有趣的是,当我这样运行时,该程序也运行良好:
cat test.dat | main
这并不是为 main() 提供文件名。它将 test.dat 的内容传输到 main()。正确的?那么它是怎样工作的?
进一步阐述: main() 函数是 Bison 解析器中的 main 函数。我在下面展示了 main() 函数。正如我所提到的,无论我以这种方式调用它,解析器都可以正常工作:
main test.dat
或者这样:
cat test.dat | main
这是解析器的 main() 函数:
int main(int argc, char *argv[])
{
yyin = fopen(argv[1], "r");
yyparse();
fclose(yyin);
return 0;
}
根本问题是你没有验证这一点fopen
工作了。Every拨电至fopen()
随后应检查返回值是否不为 NULL。否则,您将永远不会注意到用户拼写错误的文件名等。
通常,尝试使用 NULLFILE*
stdio 函数的参数是未定义行为,这通常会导致段错误。这不会发生在yyin
因为 NULL 永远不会传递到 stdio;柔性扫描仪注意到yyin
为 NULL 并将其转换为stdin
。这样做是因为stdin
是默认输入源(根据 Posix 标准)。类似地,一个 NULLyyout
被视为好像stdout
.
依赖 Flex 的这种行为可能没问题。但它只能是有意使用,而不是偶然使用。
如果您的应用程序是在没有命令行参数的情况下调用的,那么argc
将是 1,argv[0]
将是用于调用程序的名称,并且argv[1]
将为 NULL。 (从技术上讲,argc
可能是 0,后果更糟,但这在实践中不太可能。)然后你就可以通过它NULL
to fopen
,这是未定义的行为(也就是说,一个严重的错误)。实施fopen
标准库中的返回错误指示而不是段错误[注释1],但如上所述,您不检查此错误返回。所以错误的复合会导致yyin
为 NULL,Flex 从中读取stdin
.
您应该始终检查用户输入的有效性。总是。毫无例外。你应该报告错误,或者处理它们。没有任何借口。不检查是危险的,最多会浪费大量时间;您的以及您招募来帮助您的任何人的。
正确的代码可能如下所示:
if (argc > 1) {
yyin = fopen(argv[1], "r");
if (yyin == NULL) {
fprintf("Could not open file '%s': %s\n",
argv[1], strerror(errno));
exit(1);
}
}
else {
/* argc <= 1, so there was no command line argument.
* Read from stdin.
*/
yyin = stdin;
}
Notes
-
大多数类 Unix 系统上的 stdio 库都实现fopen
首先调用 Posix 定义的open
功能。文件名只是简单地传递,因此根本不检查它。open
通常是系统调用,因此在内核模式下执行;这要求它将文件名从用户内存复制到内核内存,这又要求它首先验证地址。所以在 Unix 上,将无效的字符串指针传递给fopen
可能会产生某种错误指示。任何标准都没有要求,也没有具体说明errno
使用的代码。在非 Posix 平台上情况可能并非如此,很可能fopen
在将文件路径传递到本机文件系统之前需要以某种方式转换文件路径。 (例如,可能需要翻译/
目录分隔符到其他内容。)在这样的系统上,很可能不会检查文件名参数的有效性,并且fopen
当库函数尝试使用无效的文件名指针时,它将出现段错误(或等效错误)。
在最常见的 Unix stdio 库实现上,fopen
will段错误如果mode
参数指定为NULL
。与所有库函数一样,fopen
没有义务应付NULL
指针参数; C 标准坚持认为传递是未定义的行为NULL
作为任何库函数的指针参数,除非该库函数被明确记录为接受NULL
对于这个论点。 (例如,参见free
, realloc
, and strtok
对于明确允许的库函数NULL
.) fopen
不是这样的函数,所以你不应该通过NULL
作为任何参数,您当然不应该假设结果只是错误返回。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)