【问题标题】:Why there is no error when passing command line arguments when declaring main as `int main(void)`?为什么在将 main 声明为 `int main(void)` 时传递命令行参数时没有错误?
【发布时间】:2016-01-23 20:50:09
【问题描述】:

案例 1:

void hello(void) {
    //something
}

int main()
{
    hello(1); //error
    return 0;
}

案例 2:

int main(void) {
    //something
    return 0;
}

执行:

./a.out something something //No error, Why?

为什么没有错误? main 将无法接受任何参数。那么为什么可以从命令行提供参数呢?

【问题讨论】:

  • 为什么会出现错误?
  • @OliverCharlesworth 因为我们将参数传递给 main?编译器没有出现,但在运行时/加载时为什么没有错误?
  • shell 总是 将它的参数发送给程序——即使你没有提供任何参数(通常它发送argv[0])。 mainhello 不是同一种函数。
  • 我也有兴趣听到答案。运行时中必须有一些东西用这些参数调用 main 。那么为什么当你定义一个偏离标准的 main 时没有链接器错误呢? (即使main(float) 只生成一个警告,没有错误。而且只有 -Wall 甚至。)
  • 出于好奇,我做了一个小实验。我编译了两次相同的程序,一次使用不同的签名。 (如果您有兴趣,VS2013,因为这是我手头的。)忽略调试信息和时间戳,两个生成的文件完全相同,精确到字节。这向我表明编译器要么用标准签名替换 void 签名(这根本不会改变你的程序),要么足够聪明,知道如果你不使用 args 也没关系。 (当然,我们希望编译器知道这一点。:))

标签: c command-line-arguments main


【解决方案1】:

因为 C 编译器和命令行解释器(或用于调用程序的任何东西)是不同的东西。

C 语言允许以多种方式声明 main()。

命令行解释器将使程序可以使用任何参数。如果程序忽略它们,那不关它的事。

命令行解释器甚至不知道您使用 C 编译程序。在我的计算机上,该程序可以用 C、C++、Objective-C、Objective-C++、Swift、Fortran、Ada 等编写。这些编译器中的每一个都可能会或可能不会做一些事情来接受来自命令行的命令。

【讨论】:

    【解决方案2】:

    不检查规范也不编译结果,不会出错,因为C运行时会获取参数并传递给main(),但是这种main()会忽略传递的参数,如果是调用者的清理用作参数的内存(堆栈)的责任,它不会导致任何问题,就像获取一些参数而不在代码中使用它们一样。

    此代码不会在 C 中发出错误:

    void hello(); // in C, the compiler won't check arguments
    
    int main() {
        hello(1); //no error
        return 0;
    }
    
    void hello(void) {
        //something 
    }
    

    【讨论】:

    • @Jongware 传递给main 的参数由调用 main 的函数从堆栈中清除,该函数是C 库提供的垫片,并且函数不知道也不关心main 是如何声明的。这对main 并不特别;这是所有函数的正常 C 调用约定。 (像__stdcall 这样的编译器扩展可以改变约定。如果你将它们应用到main——或任何其他由C 库调用的函数,就会发生坏事。)
    • @Jongware 啊,你可能一直想问的 other 事情。 argv 首先来自哪里?内核将它与程序一起加载到内存中,作为execve 操作的一部分,并构造一个初始调用帧,告诉C 库的启动代码在哪里找到它。它通常在堆栈的最顶端,但它不是必须的,而且它永远不会被释放。
    • @Jongware 如果 C 库的启动代码从初始堆栈帧返回,程序将崩溃。需要改为拨打exit。例如,参见sourceware.org/git/?p=glibc.git;a=blob;f=csu/libc-start.c 的末尾。
    • @zwol:这一定是 OP 正在寻找的答案。运行时启动 always 告诉 C 代码在哪里可以找到参数 - 是否有参数。而且它并不关心 C 代码是否真的需要检查。
    • 你不能依赖这种代码编译运行没有错误。 C11 标准,6.5.2.2.6:“如果参数的数量不等于参数的数量,则行为未定义。”您依赖于实现的怪癖,而不同的实现或任何类型的优化都可能会破坏此代码。
    【解决方案3】:

    因为./a.out something something 没有直接调用你的主函数。 C 运行时库正在调用主函数。命令行参数由 loader/c 运行时放置在堆栈上某处(最开始)的区域中。是否要访问这些参数取决于您。

    另外,正如在其中一个 cmets 中指出的那样,至少始终传递至少一个命令行参数(确切地说是程序名称 ./a.out) - 所以您一定想知道在这种情况下会出现错误,因为好吧。

    【讨论】:

      【解决方案4】:

      回想一下,ISO C 指定了 main 的两个可能签名:int main(void)int main(int, char *[]) 及其等效版本,例如 int main(int, char **),因为数组到指针的衰减。这在here 中有更详细的介绍。

      这个问题可以通过考虑相反的问题来回答:C 运行时如何知道main 调用什么? C没有重载决议!这是解释here。简而言之,其他的被推送但没有被访问,因为 C 没有指示这样做。

      【讨论】:

        【解决方案5】:

        当您使用gcc program_name.c 编译程序时,编译器将报告任何可能的编译时警告或错误。由于在编译时没有传递命令行参数,编译器不知道它,程序只是忽略该参数。

        hello 的情况下,编译器知道这个函数的原型,并且不希望在其调用中传递任何参数,因此在传递给它的任何参数的情况下报告错误。

        【讨论】:

          猜你喜欢
          • 2018-03-16
          • 2013-11-04
          • 2016-07-24
          • 2022-08-26
          • 2016-04-18
          • 1970-01-01
          • 1970-01-01
          • 2013-04-14
          • 2012-01-30
          相关资源
          最近更新 更多