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 文件...这是如何工作的? 的相关文章

  • boost::multi_index_container 复合键中的 equal_range 与比较运算符

    我正在尝试从多索引容器查询结果 其中值类型是三个元素的结构 第一个值已给出 但第二个和第三个值必须大于或小于查询参数 经过搜索后 我发现必须实现自定义密钥提取器 并且这里的一些链接建议相同 但我无法实现它 boost multi index
  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • 如何在C++中实现模板类协变?

    是否可以以这样一种方式实现类模板 如果模板参数相关 一个对象可以转换为另一个对象 这是一个展示这个想法的例子 当然它不会编译 struct Base struct Derived Base template
  • 嵌入式系统中的malloc [重复]

    这个问题在这里已经有答案了 我正在使用嵌入式系统 该应用程序在 AT91SAMxxxx 和 cortex m3 lpc17xxx 上运行 我正在研究动态内存分配 因为它会极大地改变应用程序的外观 并给我更多的力量 我认为我唯一真正的路线是为
  • Cygwin 下使用 CMake 编译库

    我一直在尝试使用 CMake 来编译 TinyXML 作为一种迷你项目 尝试学习 CMake 作为补充 我试图将其编译成动态库并自行安装 以便它可以工作 到目前为止 我已经设法编译和安装它 但它编译成 dll 和 dll a 让它工作的唯一
  • 跨多个控件共享事件处理程序

    在我用 C 编写的 Windows 窗体应用程序中 我有一堆按钮 当用户的鼠标悬停在按钮上时 我希望按钮的边框发生变化 目前我有以下多个实例 每个按钮一个副本 private void btnStopServer MouseEnter ob
  • C# 用数组封送结构体

    假设我有一个类似于 public struct MyStruct public float a 我想用一些自定义数组大小实例化一个这样的结构 在本例中假设为 2 然后我将其封送到字节数组中 MyStruct s new MyStruct s
  • 按字典顺序对整数数组进行排序 C++

    我想按字典顺序对一个大整数数组 例如 100 万个元素 进行排序 Example input 100 21 22 99 1 927 sorted 1 100 21 22 927 99 我用最简单的方法做到了 将所有数字转换为字符串 非常昂贵
  • Windows 窗体不会在调试模式下显示

    我最近升级到 VS 2012 我有一组在 VS 2010 中编码的 UI 测试 我试图在 VS 2012 中启动它们 我有一个 Windows 窗体 在开始时显示使用 AssemblyInitialize 属性运行测试 我使用此表单允许用户
  • 如何在 Team Foundation 上强制发表有意义的签入评论?

    我有一个开发团队有一个坏习惯 他们写道poor签入评论 当我们必须在团队基础上查看文件的历史记录时 这使得它成为一场噩梦 我已经启用了变更集评论政策 这样他们甚至可以在签到时留下评论 否则他们不会 我们就团队的工作质量进行了一些讨论 他们很
  • 我的 strlcpy 版本

    海湾合作委员会 4 4 4 c89 我的程序做了很多字符串处理 我不想使用 strncpy 因为它不会终止 我不能使用 strlcpy 因为它不可移植 只是几个问题 我怎样才能让我的函数正常运行 以确保它完全安全稳定 单元测试 这对于生产来
  • 初始化变量的不同方式

    在 C 中初始化变量有多种方法 int z 3 与 int 相同z 3 Is int z z 3 same as int z z 3 您可以使用 int z z 3 Or just int z 3 Or int z 3 Or int z i
  • 像“1$”这样的位置参数如何与 printf() 一起使用?

    By man I find printf d width num and printf 2 1 d width num 是等价的 但在我看来 第二种风格应该与以下相同 printf d num width 然而通过测试似乎man是对的 为什
  • 更改窗口的内容 (WPF)

    我创建了一个简单的 WPF 应用程序 它有两个 Windows 用户在第一个窗口中填写一些信息 然后单击 确定 这会将他们带到第二个窗口 这工作正常 但我试图将两个窗口合并到一个窗口中 这样只是内容发生了变化 我设法找到了这个更改窗口内容时
  • 网络参考共享类

    我用 Java 编写了一些 SOAP Web 服务 在 JBoss 5 1 上运行 其中两个共享一个类 AddressTO Web 服务在我的 ApplycationServer 上正确部署 一切都很顺利 直到我尝试在我的 C 客户端中使用
  • 可空属性与可空局部变量

    我对以下行为感到困惑Nullable types class TestClass public int value 0 TestClass test new TestClass Now Nullable GetUnderlyingType
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • 将日期参数传递给对 MVC 操作的 ajax 调用的安全方法

    我有一个 MVC 操作 它的参数之一是DateTime如果我通过 17 07 2012 它会抛出一个异常 指出参数为空但不能有空值 但如果我通过01 07 2012它被解析为Jan 07 2012 我将日期传递给 ajax 调用DD MM
  • 窗体最大化时自动缩放子控件

    有没有办法在最大化屏幕或更改分辨率时使 Windows 窗体上的所有内容自动缩放 我发现手动缩放它是正确的 但是当切换分辨率时我每次都必须更改它 this AutoScaleDimensions new System Drawing Siz
  • 如何连接字符串和常量字符?

    我需要将 hello world 放入c中 我怎样才能做到这一点 string a hello const char b world const char C string a hello const char b world a b co

随机推荐