main 需要一个文件名作为第一个参数...但我也可以通过管道提供 main 文件...这是如何工作的?

2023-12-12

我有一个带有 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

  1. 大多数类 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(使用前将#替换为@)

main 需要一个文件名作为第一个参数...但我也可以通过管道提供 main 文件...这是如何工作的? 的相关文章

  • 删除文件的最后 10 个字符

    我想删除文件的最后 10 个字符 说一个字符串 hello i am a c learner 是文件内的数据 我只是希望该文件是 hello i am a 文件的最后 10 个字符 即字符串 c learner 应在文件内消除 解决方案 将
  • 结构化绑定中缺少类型信息

    我刚刚了解了 C 中的结构化绑定 但有一件事我不喜欢 auto x y some func is that auto正在隐藏类型x and y 我得抬头看看some func的声明来了解类型x and y 或者 我可以写 T1 x T2 y
  • STL 迭代器:前缀增量更快? [复制]

    这个问题在这里已经有答案了 可能的重复 C 中的预增量比后增量快 正确吗 如果是 为什么呢 https stackoverflow com questions 2020184 preincrement faster than postinc
  • 没有特殊字符的密码验证器

    我是 RegEx 的新手 已经进行了大量搜索 但没有找到任何具体内容 我正在编写一个验证密码字符串的正则表达式 可接受的字符串必须至少具有 4 种字符类型中的 3 种 数字 小写字母 大写字母 特殊字符 我对包含有一个想法 也就是说 如果这
  • 通过引用传递 [C++]、[Qt]

    我写了这样的东西 class Storage public Storage QString key const int value const void add item QString int private QMap
  • 机器Epsilon精度差异

    我正在尝试计算 C 中双精度数和浮点数的机器 epsilon 值 作为学校作业的一部分 我在 Windows 7 64 位中使用 Cygwin 代码如下 include
  • 执行命令而不将其保留在历史记录中[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在进行软件开发时 经常需要在命令行命令中包含机密信息 典型示例是将项目部署到服务器的凭据设置为环境变量 当我不想将某些命令存储在命令历史记
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • 如何在 C# 中打开 Internet Explorer 属性窗口

    我正在开发一个 Windows 应用程序 我必须向用户提供一种通过打开 IE 设置窗口来更改代理设置的方法 Google Chrome 使用相同的方法 当您尝试更改 Chrome 中的代理设置时 它将打开 Internet Explorer
  • free 和 malloc 在 C 中如何工作?

    我试图弄清楚如果我尝试 从中间 释放指针会发生什么 例如 看下面的代码 char ptr char malloc 10 sizeof char for char i 0 i lt 10 i ptr i i 10 ptr ptr ptr pt
  • -webkit-box-shadow 与 QtWebKit 模糊?

    当时有什么方法可以实现 webkit box shadow 的工作模糊吗 看完这篇评论错误报告 https bugs webkit org show bug cgi id 23291 我认识到这仍然是一个问题 尽管错误报告被标记为RESOL
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • 方程“a + bx = c + dy”的积分解

    在等式中a bx c dy 所有变量都是整数 a b c and d是已知的 我如何找到整体解决方案x and y 如果我的想法是正确的 将会有无限多个解 由最小公倍数分隔b and d 但我只需要一个解决方案 我可以计算其余的 这是一个例
  • 使用 C# 中的 CsvHelper 将不同文化的 csv 解析为十进制

    C 中 CsvHelper 解析小数的问题 我创建了一个从 byte 而不是文件获取 csv 文件的类 并且它工作正常 public static List
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • 在 WPF 中使用 ReactiveUI 提供长时间运行命令反馈的正确方法

    我有一个 C WPF NET 4 5 应用程序 用户将用它来打开某些文件 然后 应用程序将经历很多动作 读取文件 通过许多插件和解析器传递它 这些文件可能相当大 gt 100MB 因此这可能需要一段时间 我想让用户了解 UI 中发生的情况
  • 为什么 std::uint32_t 与 uint32_t 不同?

    我对 C 有点陌生 我有一个编码作业 很多文件已经完成 但我注意到 VS2012 似乎有以下语句的问题 typedef std uint32 t identifier 不过 似乎将其更改为 typedef uint32 t identifi
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置

随机推荐