【问题标题】:Where are C/C++ main function's parameters?C/C++ 主函数的参数在哪里?
【发布时间】:2011-05-10 22:03:53
【问题描述】:

在C/C++中,main函数接收char*类型的参数。

int main(int argc, char* argv[]){
  return 0;
}

argvchar* 的数组,指向字符串。这些字符串在哪里?它们是在堆、堆栈还是其他地方?

【问题讨论】:

  • 我没有得到反对票。没有投票结束,这似乎是一个完全合理的问题。否决票者,请说明。
  • 可能不赞成票是为了提出一个特定于实现的问题而没有提及实现。 FWIW 我不是反对者之一。
  • @R..:在提出问题并得到一个说它是特定于实现的答案之前,如何知道它是特定于实现的?
  • @Fred:这就是我没有加入反对者的原因。 :-)
  • 我很好奇你为什么想知道这些在哪里。闲散的好奇心,还是一些“好”的理由?

标签: c++ c parameters location main


【解决方案1】:

它们是编译器的魔法,并且依赖于实现。

【讨论】:

  • +1:这与您将获得的非超详细答案非常接近......
  • 一定很喜欢 SO 似乎总是支持“机智”的非答案,而不是实际提供有用信息、背景或示例的答案。
  • 啊,请不要把它当成个人,我真的不是要抨击你或你的回答。我想我应该在我之前的评论中更仔细地措辞 - 对此感到抱歉。我只是想知道为什么这种答案往往会得到最多的支持,而不是更全面(而且通常更有用)的答案更详细地解释情况 - 即使像这里这样的完整解释也不可行。
  • 很公平。我可以给你我个人的答案:很多时候,“正确”的答案(如下面的约翰博德)会让普通提问者的眼睛呆滞——很难。我最初的“答案”是“你到底为什么想知道?”,但这似乎永远不会奏效——所以这是我的妥协。对于很多人来说,它似乎工作得很好。
  • 这样的理由可能很长(几乎不适合评论)。例如对我来说 1.link,这几乎表明 c++ 开发人员会写 public static void main(String[] args)"。2.我:“与该信息无关”+ 我的 c++ 知识仅限于 g++ -std= c++11 将无法编译(需要 char**)-> 让我找到 link 3. 我:如果 char ** 参数没有内存,与 std::vector 分配相比,CLI args 的内存不足会表现相同?
【解决方案2】:

以下是 C 标准 (n1256) 所说的:

5.1.2.2.1 程序启动
...
2 如果它们被声明,main 函数的参数应遵循以下 约束:

  • argc 的值应为非负值。

  • argv[argc] 应为空指针。

  • 如果 argc 的值大于零,则数组成员 argv[0]argv[argc-1] 应包含指向字符串的指针,这些指针是给定的 在程序启动之前由主机环境实现定义的值。这 意图是向程序提供在程序启动之前确定的信息 来自托管环境中的其他地方。如果宿主环境不能 提供带有大写和小写字母的字符串,实现 应确保以小写形式接收字符串。

  • 如果argc的值大于零,则argv[0]指向的字符串 表示程序名argv[0][0] 应为空字符,如果 程序名称在主机环境中不可用。如果 argc 的值为 大于一,argv[1]argv[argc-1]指向的字符串 表示程序参数

  • argcargv 参数以及 argv 数组所指向的字符串应 可由程序修改,并在程序之间保留它们最后存储的值 启动和程序终止。

最后一个项目符号是存储字符串值的最有趣的地方。它没有指定堆或堆栈,但它确实要求字符串是可写的并且具有静态范围,这对字符串内容可能位于的位置设置了一些限制。正如其他人所说,确切的细节将取决于实施。

【讨论】:

  • 我从未注意到的有趣的事情......虽然argv(指向指针的参数)和指向的字符串是可修改的,但标准似乎并未表明指针数组是可修改的。因此,在未明确允许修改 argv 数组的系统上使用 GNU getopt(及其 argv 排列)可能是未定义的行为。
  • @R:只是没有提到,因此标准没有规定当你改变它时会发生什么,是的。但这并不意味着它是 UB,而只是它是特定于实现的。由于规范是char** argv 而不是char*const* argv,人们可能会认为它可能会被修改。这与标准明确指出更改它们是 UB 的字符串文字的情况不同。
【解决方案3】:

实际上是编译器依赖和操作系统依赖的结合。 main() 是一个函数,就像任何其他 C 函数一样,所以两个参数 argcargv 的位置将遵循平台上编译器的标准。例如对于大多数以 x86 为目标的 C 编译器,它们将位于返回地址和保存的基指针上方的堆栈上(记住堆栈向下增长)。在 x86_64 上,参数在寄存器中传递,因此 argc 将在 %edi 中,argv 将在 %rsi 中。编译器生成的 main 函数中的代码然后将它们复制到堆栈中,这就是后面的引用指向的地方。这样寄存器就可以用于来自main 的函数调用。

argv 指向的char*s 块和实际的字符序列可以在任何地方。它们将从某个操作系统定义的位置开始,并且可能被链接器生成的前导码复制到堆栈或其他地方。您必须查看 exec() 的代码和链接器生成的汇编器前导码才能找到答案。

【讨论】:

  • "main() 是一个函数,就像任何其他 C 函数一样" 不在 c++ 中,从另一个函数调用它是非法的,即使它被声明为返回 int 你实际上并不需要返回任何东西
  • @John, @JeremyP, main 也不像 C 中的任何其他函数。至少 C99 还允许省略 return 语句并明确定义正在发生的事情。跨度>
  • @John, @Jens,就参数的传递方式而言,main() 与任何其他函数相同,即使相关标准还定义了其他特殊语义。
  • 我不相信他们必须。它们根本不能“传递”给 main,编译器可以在 main 的开头插入代码来检索它们。因为你不能自己打电话给 main 它可以做到这一点,你永远不会知道。但实际上可能不会
【解决方案4】:

这个问题的答案取决于编译器。这意味着它不在 C 标准中处理,因此任何人都可以按照他或她的意愿实现它。这是正常的,因为操作系统也没有通用的标准方式来启动和完成进程。

让我们想象一个简单的为什么不的场景。

进程通过某种机制接收命令行中写入的参数。然后 argc 只是一个 int ,它由编译器作为程序进程(运行时的一部分)的入口点的引导函数推入堆栈。实际值是从操作系统获取的,并且可以写入堆的内存块中。然后构建 argv 向量并将其第一个位置的地址也压入堆栈。

然后调用必须由程序员提供的函数 main(),并保存其返回值以供以后(几乎是中间)使用。释放堆中的结构,并将为 main 获得的退出代码导出到操作系统。该过程结束。

【讨论】:

    【解决方案5】:

    这些参数与任何其他函数的参数没有什么不同。 如果架构的调用序列需要参数通过堆栈,则它们在堆栈上。如果像 x86-64 一样,某些参数进入寄存器,这些参数也进入寄存器。

    【讨论】:

    • 不确定这在 c++ 中是否一定是正确的。与 C 不同,您不能在 C++ 中将 main 作为普通函数调用,因此编译器可以根据需要对传递参数做出不同的安排。
    • 字符串不是参数,参数是指向字符串指针数组的指针。
    • argcargv 本身可能是正确的,但我认为问题更多的是关于 argv[0] 和朋友。
    【解决方案6】:

    正如pmg 所提到的,当main 被递归调用时,由参数指向的调用者决定。基本上答案与 main 的原始调用相同,只是“调用者”是 C 实现/OS。

    在 UNIX-y 系统上,argv 指向的字符串、argv 指针本身以及进程的初始环境变量几乎总是存储在堆栈的最顶部。

    【讨论】:

    • +1 表示真正的答案,当然,部分答案。 + freeBSD/gcc 就是这种情况。
    【解决方案7】:

    正如这里的许多其他答案所指出的,标准未指定编译器实现用于将参数传递给 main 的精确机制(编译器用于将任何参数传递给函数的机制也是如此)。严格来说,编译器甚至不需要在这些参数中传递任何有用的东西,因为这些值是实现定义的。但这些都不是特别有用的答案。

    典型的 C(或 C++)程序是为所谓的“托管”执行环境编译的(使用函数 main() 作为程序的起点是托管环境的要求之一)。要知道的关键是编译器会安排一些事情,以便当操作系统启动可执行文件时,编译器的运行时最初获得控制 - 而不是main() 函数。运行时的初始化代码执行任何必要的初始化,包括为main() 的参数分配内存,然后将控制权转移给main()

    main() 的参数的内存可以来自堆,可以在堆栈上分配(可能使用标准 C 代码不可用的技术),或者可以使用静态分配的内存,尽管这更少可能的选择只是因为它不太灵活。该标准确实要求用于argv 指向的字符串的内存是可修改的,并且对这些字符串所做的修改在程序的整个生命周期中都保持不变。

    请注意,在执行到达 main() 之前,已经运行了相当多的代码来设置程序运行的环境。

    【讨论】:

      【解决方案8】:

      参数列表是进程环境的一部分,类似于(但不同)环境变量。

      【讨论】:

      • 不完全。 C标准不知道“过程”这个词。 (虽然 许多 C 的实现就是这种情况)
      【解决方案9】:

      通常不知道它们在哪里。

      #include <stdlib.h>
      #include <string.h>
      
      int main(int argc, char *argv[]) {
        char **foo;
        char *bar[] = {"foo", "bar"};
      
        (void)argv; /* avoid unused argv warning */
      
        foo = malloc(sizeof *foo);
        foo[0] = malloc(42);
        strcpy(foo[0], "forty two");
      
        /* where is foo located? stack? heap? somewhere else? */
        if (argc != 42) main(42, foo); else return 0;
      
        /* where is bar located? stack? heap? somewhere else? */
        if (argc != 43) main(43, bar); else return 0;
        /* except for the fact that bar elements
        ** point to unmodifiable strings
        ** this call to main is perfectably reasonable */
      
        return 0;
        /* please ignore memory leaks, thank you */
      }
      

      【讨论】:

        【解决方案10】:

        虽然您可以访问实际参数,但我认为它们的实际位置并不重要。

        【讨论】:

          猜你喜欢
          • 2017-12-12
          • 2013-01-06
          • 1970-01-01
          • 2023-04-02
          • 2011-11-23
          • 2015-08-13
          • 1970-01-01
          • 2022-01-03
          • 2011-04-03
          相关资源
          最近更新 更多