在C / C ++中,main函数接收类型为char*
的参数。
int main(int argc, char* argv[]){
return 0;
}
argv
是一个char*
数组,指向字符串。这些字符串在哪里?它们是在堆,堆栈还是其他地方?
答案 0 :(得分:31)
它们是编译器魔术,并且依赖于实现。
答案 1 :(得分:28)
以下是C标准(n1256)所说的内容:
5.1.2.2.1程序启动
...
2如果声明它们,主功能的参数应遵循以下规则 约束:
- argc 的值应为非负。
- argv [argc] 应为空指针。
- 如果 argc 的值大于零,则数组成员 argv [0] 通过 argv [argc-1] 包含应包含指向字符串的指针 程序启动前主机环境实现定义的值。该 意图是在程序启动之前为程序提供信息 来自托管环境中的其他地方。如果主机环境不具备 为字符串提供大写和小写的字母,实现 应确保琴弦以小写形式收到。
- 如果 argc 的值大于零,则 argv [0] 指向的字符串 代表程序名称; argv [0] [0] 如果是,则为空字符 程序名称不能从主机环境中获得。如果 argc 的值是 大于一, argv [1] 指向的字符串通过 argv [argc-1] 代表程序参数。
- 参数 argc 和 argv 以及 argv 数组指向的字符串应 可以由程序修改,并在程序之间保留它们最后存储的值 启动和程序终止。
最后一个项目符号是存储字符串值的最有趣的文件。它没有指定堆或堆栈,但它确实要求字符串是可写的并且具有静态范围,这将一些限制放置在字符串内容可能位于的位置。正如其他人所说,具体细节将取决于实施情况。
答案 2 :(得分:16)
它实际上是编译器依赖和操作系统依赖的组合。 main()
是一个函数,就像任何其他C函数一样,因此两个参数argc
和argv
的位置将遵循平台上编译器的标准。例如对于大多数针对x86的C编译器,它们将位于返回地址和保存的基指针之上的堆栈上(堆栈向下增长,请记住)。在x86_64上,参数在寄存器中传递,因此argc
将在%edi
中,argv
将在%rsi
中。编译器生成的main函数中的代码然后将它们复制到堆栈中,这是后面引用指向的位置。这样寄存器就可以用于来自main
的函数调用。
argv指向的char*
块,实际的字符序列可以在任何地方。它们将在某个操作系统定义的位置启动,并且可以通过链接器生成到堆栈或其他位置的前导码复制。您将不得不查看exec()
的代码和链接器生成的汇编器前导码以查找。
答案 3 :(得分:8)
这个问题的答案是编译器依赖的。这意味着它没有在C标准中处理,因此任何人都可以按照他或她的意愿实现。这是正常的,因为操作系统也没有通用的标准方法来启动流程并完成它们。
让我们想象一个简单的,为什么不是场景。
该进程通过某种机制接收在命令行中写入的参数。然后argc只是一个int,它被编译器作为程序进程(运行时的一部分)的入口点的引导函数推送到堆栈。实际值是从操作系统获得的,并且可以写在堆的内存块中。然后构建argv向量,并将其第一个位置的地址也推入堆栈。
然后调用必须由程序员提供的函数main(),并保存其返回值以供稍后(几乎在中间)使用。释放堆中的结构,并将为main获取的退出代码导出到操作系统。这个过程结束了。
答案 4 :(得分:3)
这些参数与任何其他函数的参数没有什么不同。 如果体系结构的调用序列需要参数通过堆栈,则它们处于堆栈状态。如果,像on,x86-64一些参数进入寄存器,这些参数也会进入寄存器。
答案 5 :(得分:3)
正如许多其他答案所指出的那样,标准未指定编译器实现用于将参数传递给main的精确机制(编译器用于将任何参数传递给函数的机制)。严格地说,编译器甚至不需要传递那些参数中有用的东西,因为这些值是实现定义的。但这些都不是特别有用的答案。
典型的C(或C ++)程序是为所谓的“托管”执行环境编译的(使用函数main()
,因为程序的起点是托管环境的要求之一)。要知道的关键是编译器安排事情,以便在操作系统启动可执行文件时,编译器的运行时最初得到控制 - 而不是main()
函数。运行时的初始化代码执行必要的初始化,包括为main()
的参数分配内存,然后将控制转移到main()
。
main()
的参数的内存可以来自堆,可以在堆栈上分配(可能使用标准C代码不可用的技术),或者可以使用静态分配的内存,尽管这是不太可能的选择只是因为它不太灵活。该标准确实要求用于argv
指向的字符串的内存是可修改的,并且对这些字符串的修改在整个程序的生命周期内都会持续存在。
请注意,在执行到main()
之前,已经运行了相当多的代码来设置运行程序的环境。
答案 6 :(得分:2)
参数列表是过程环境的一部分,类似于(但不同于)环境变量。
答案 7 :(得分: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 */
}
答案 8 :(得分:2)
如pmg
所述,当递归调用main
时,由参数指向的调用者决定。基本上,main
的原始调用的答案是相同的,除了“调用者”是C实现/操作系统。
在UNIX-y系统上,argv
指向的字符串,argv
指针本身以及进程的初始环境变量几乎总是存储在堆栈的最顶层。
答案 9 :(得分:-1)
虽然您可以访问实际参数,但我认为它们的实际位置根本不重要。