当main()调用main()时,栈帧会发生什么?

3
请考虑以下代码:
#include <stdio.h>

int main()
{
    static int counter=5;

    printf ("Counter = %d\n", counter);

    if (counter--)
    {
        main();
    }

    return 0;
}

编译:
gcc test.c -ansi -Wall –pedantic

执行:

[root@mars home]# ./a.out 
Counter = 5
Counter = 4
Counter = 3
Counter = 2
Counter = 1
Counter = 0

这里的 main() 函数在调用自身。

似乎每次 main() 被自身调用时,原始的堆栈帧都会被覆盖。

但是返回地址将是什么?函数能够返回到自己的堆栈帧吗?

请帮我澄清这个疑问。

谢谢。


8
int counter 被声明为 static,因此它不会被分配在函数的栈上,而是在全局堆上分配。 - alk
4
除此之外,堆栈不会被覆盖。每次调用函数时都会推入一个新的堆栈,并在返回时弹出。 - stijn
@alk 我不小心点了赞。静态变量既不在堆栈中分配,也不在堆中分配。它们通常位于数据段中,具有初始化和未初始化(BSS)两个不同的部分。但是,这可能因实现而异。 - fkl
3个回答

6

不会被覆盖,这是一个正常的函数调用(在这种情况下是递归)。您可能会因为counter变量而感到困惑。该变量被声明为静态,这意味着它只会初始化一次,因此下面的行只会“执行”一次:

static int counter=5;

换句话说,你可以将你的counter想象成一个全局变量,只初始化一次(值为5)。在每次调用main时,它会递减,直到达到零。之后所有的main函数返回,因此堆栈会被unwinded(与普通函数调用相同)。

3
在许多计算机语言中,这是很常见的,被称为“递归”。每次调用函数时都会创建一个新的堆栈帧,因此不会发生重写。
外部main()的返回地址与通常一样是运行时库。内部main()的返回地址是外部main()。
在您的示例中模糊了界限的一件事是,您将counter声明为静态的。这意味着它在数据段中分配,而不是在堆栈中(如评论中所解释的),因此每个main()的调用共享相同的counter实例。

2
不一定在堆上。很可能只是一个全局变量。 - Alexey Frunze
谢谢,是的,我有一种感觉堆栈不太对,但当我写那个时脑子一片空白。 - Ian Goldby
实际上,从不在堆上分配内存,因为堆需要显式清除。它通常是数据段的一部分,具有初始化和未初始化值(BSS)的单独部分。 - fkl

1

简短回答:这是一个简单的递归案例。因此,它将分配新的堆栈帧而不会覆盖先前的帧。

长回答:
main() 调用 main() 时,堆栈帧会发生什么:
C 不会将 main 与任何其他函数区分开来。这只是一个简单的、正常的递归案例。 'main' 只是特殊的,因为编译器附带的默认 crt0.asm(或者在您的编译器中叫什么名字)在基本初始化完成后(如堆栈指针等)调用 main。
除了这个区别之外,main 没有任何特殊之处。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接