62

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

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

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

4

10 回答 10

31

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

于 2010-11-16T16:08:39.250 回答
30

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

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

  • argc的值应为非负数。

  • argv[argc]应该是一个空指针。

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

  • 如果argc的值大于零,则argv[0]指向的字符串 代表程序名;如果主机环境中没有程序名,则argv[0][0]应为空字符。如果argc的值大于 1,则argv[1]argv[argc-1]指向的字符串 代表程序参数

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

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

于 2010-11-16T17:09:11.933 回答
18

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

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

于 2010-11-16T16:59:15.740 回答
8

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

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

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

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

于 2010-11-16T16:29:50.073 回答
3

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

于 2010-11-16T16:11:05.487 回答
3

如前所述pmg,何时main递归调用,取决于参数指向的调用者。基本上答案在原始调用上是相同的main,除了“调用者”是 C 实现/OS。

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

于 2010-11-16T16:33:16.037 回答
3

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

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

The memory for the arguments to main() could come from the heap, could be allocated on the stack (possibly using techniques that aren't available to standard C code), or could use statically allocated memory, though that's a less likely option just because it's less flexible. The standard does require that the memory used for the strings pointed to by argv are modifiable and that modifications made to those string persist throughout the program's lifetime.

Just be aware that before execution reaches main(), quite a bit of code has already been run that's setting up the environment for your program to run in.

于 2010-11-16T18:27:00.733 回答
2

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

于 2010-11-16T16:09:58.263 回答
2

通常不知道他们在哪里。

#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 */
}
于 2010-11-16T16:27:04.713 回答
-1

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

于 2010-11-16T16:09:43.337 回答