当main没有参数定义时,argc和argv是否仍然存在于堆栈中?

man*_*ama 21 c mingw

考虑非常简单:

int main(void) {
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我编译它(使用mingw32-gcc)并执行它main.exe foo bar.

现在,我曾预料到由明确声明为丧失生命参数的主函数引起的某种崩溃或错误.缺乏错误导致了这个问题,这实际上是四个问题.

  • 为什么这样做?答:因为标准是这样说的!

  • 输入参数是否被忽略或者是否使用argc&argv静默编写堆栈?答:在这种特殊情况下,堆栈已准备就绪.

  • 我如何验证以上内容?答:请参阅rascher的回答.

  • 这个平台依赖吗?答:是的,不.

pou*_*def 20

我不知道您的问题的跨平台答案.但这让我很好奇.那么我们该怎么办?看堆栈!

对于第一次迭代:

test.c的

int main(void) {
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

test2.c中

int main(int argc, char *argv[]) {
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

现在看看汇编输出:

$ gcc -S -o test.s test.c 
$ cat test.s 
        .file   "test.c"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        movl    $0, %eax
        popl    %ebp
        ret
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
        .section        .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

没什么好激动的.除了一件事:两个C程序都具有相同的汇编输出!

这基本上是有道理的; 我们从来没有真正必须从main()的堆栈中推送/弹出任何东西,因为它是调用堆栈中的第一件事.

那么我写了这个程序:

int main(int argc, char *argv[]) {
   return argc;
}
Run Code Online (Sandbox Code Playgroud)

它的主题是:

main:
        pushl   %ebp
        movl    %esp, %ebp
        movl    8(%ebp), %eax
        popl    %ebp
        ret
Run Code Online (Sandbox Code Playgroud)

这告诉我们"argc"位于 8(%ebp)

所以现在再增加两个C程序:

int main(int argc, char *argv[]) {
__asm__("movl    8(%ebp), %eax\n\t"
        "popl    %ebp\n\t"
        "ret");
        /*return argc;*/
}


int main(void) {
__asm__("movl    8(%ebp), %eax\n\t"
        "popl    %ebp\n\t"
        "ret");
        /*return argc;*/
}
Run Code Online (Sandbox Code Playgroud)

我们从上面偷了"return argc"代码并将其粘贴到这两个程序的asm中.当我们编译并运行它们,然后调用echo $?(回显前一个进程的返回值)时,我们得到"正确"的答案.因此,当我运行"./test abc d"时$?,两个程序都给我"5" - 即使只有一个定义了argc/argv.这告诉我,在我的平台上,argc肯定放在堆栈上.我敢打赌,类似的测试会证实这是针对argv的.

在Windows上试试吧!

  • 如果它们存在与否则取决于启动代码,而不是主要的定义方式.理论上编译器可以根据定义选择要提供的启动代码,在这种情况下,我将在主函数上创建一个新的表达式"反向重载"(调用者根据被调用者的"定义"方式进行更改).它不会发生在我能够做的每一个测试中:无论主要定义如何,启动代码总是相同的.这就是为什么我更喜欢在启动代码传递两个args(argc和argv)而不是`int main(void)`的系统中使用`int main()`. (4认同)

Tim*_*fer 10

从C99标准:

5.1.2.2.1程序启动

程序启动时调用的函数名为main.该实现声明此函数没有原型.它应该使用int的返回类型定义,并且没有参数:

int main(void) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

或者有两个参数(这里称为argc和argv,虽然可以使用任何名称,因为它们是声明它们的函数的本地名称):

int main(int argc,char*argv []){/*...*/}

或同等学历; 或者以某种其他实现定义的方式.


Gre*_*ill 5

在经典C中,您可以执行类似的操作:

void f() {}

f(5, 6);
Run Code Online (Sandbox Code Playgroud)

没有什么可以阻止你调用具有不同数量参数的函数,因为它的定义假定.(现代编译器自然会认为这是一个令人震惊的错误,并且强烈反对实际编译代码.)

你的main()功能也会发生同样的事情.C运行时库将调用

main(argc, argv);
Run Code Online (Sandbox Code Playgroud)

但是你的函数不准备接收这两个参数这一事实对调用者来说并不重要.