每个函数在c中都有自己的堆栈吗?

每个函数在c中都有自己的堆栈吗?,c,gcc,stack-overflow,C,Gcc,Stack Overflow,我最近了解了堆栈,所以我正在试验看看堆栈的大小,以及当它溢出时会发生什么。我发现在Unix上,默认堆栈大小是8 MiB,这支持我的发现,因为我无法在我的main函数中声明大小大于或等于8 MiB的字符串。但是,当我在main()中声明变量时,它会影响其他函数。例如: #include <stdio.h> void foo(void) { long int size = 1024*1024*2; char str[size]; str[size - 1] =

我最近了解了堆栈,所以我正在试验看看堆栈的大小,以及当它溢出时会发生什么。我发现在Unix上,默认堆栈大小是8 MiB,这支持我的发现,因为我无法在我的
main
函数中声明大小大于或等于8 MiB的字符串。但是,当我在
main()
中声明变量时,它会影响其他函数。例如:

#include <stdio.h>

void foo(void)
{
    long int size = 1024*1024*2;
    char str[size];
    str[size - 1] = 'a';
    printf("%c\n", str[size - 1]);
}

int main(int argc, char** argv)
{
    long int size = 1024*1024*6;
    char str[size];
    str[size - 1] = 'a';
    printf("%c\n", str[size - 1]);
    foo();
    return 0;
}
#包括
无效foo(无效)
{
长整数大小=1024*1024*2;
字符str[size];
str[size-1]=“a”;
printf(“%c\n”,str[size-1]);
}
int main(int argc,字符**argv)
{
长整数大小=1024*1024*6;
字符str[size];
str[size-1]=“a”;
printf(“%c\n”,str[size-1]);
foo();
返回0;
}

此代码导致分段错误,但如果我在
main()
中设置字符串大小为5 MiB,则不存在分段错误。这是否意味着我的C程序不能为局部变量(所有函数)分配超过8MIB的RAM?如果是这样,堆栈的意义是什么?

不,每个函数都没有自己独立的堆栈空间。您的程序中只有一个堆栈,并且可用的堆栈空间有限

堆栈的工作原理 这种后进先出行为正是函数返回时所做的 调用它的函数

堆栈中的流
  • 调用者将返回地址推送到堆栈上
  • 当被调用函数完成其执行时,它弹出返回 调用堆栈外的地址(此弹出元素也称为 堆栈帧)并将控制权转移到该地址
  • 如果一个被调用函数调用另一个函数,它将推 同一调用堆栈顶部的另一个返回地址,以及 以此类推,将信息堆叠和拆垛作为 程序规定
  • 上述所有过程都发生在同一堆栈内存中。每个函数在堆栈中都有自己的空间,但每个函数都在同一堆栈中分配了自己的空间。这称为程序的全局调用堆栈。 它用于存储函数内部使用的局部变量

    但是,动态分配的空间存储在堆上堆用于存储动态变量。它是进程记忆的一个区域
    malloc()
    calloc()
    resize()
    所有这些内置函数通常用于存储动态变量

    至于堆栈溢出问题,调用堆栈大小是有限的。只能使用一定量的内存。如果发生许多函数调用,堆栈空间最终将耗尽,这将导致堆栈溢出错误,这很可能导致程序崩溃

    如果函数中有很多变量,或者程序中有一些变量需要很大的空间,那么堆栈空间最终会耗尽,并导致堆栈溢出。例如,在大多数情况下,以下情况可能会导致堆栈溢出,并导致程序崩溃:

    int main() {
       int A[100000][100000];
    }
    
    希望这能消除你的疑虑

    注意

    在多线程环境中,每个线程分别获得自己的调用堆栈空间,而不是拥有相同的全局调用堆栈。因此,在多线程环境中,您的问题的答案将是

    堆栈是一个,内存中有共享空间,每个嵌套函数调用都会“推”一个新的“框架”(局部变量的空间集)到该共享堆栈上。是的,内存中堆栈空间的总大小在(当前)正在执行的所有函数之间共享,如果程序运行期间使用的总空间超过操作系统为其预留的空间,则会导致(ahem)“堆栈溢出”崩溃

    关键是为每个函数的调用提供工作空间。通常,堆栈上任何特定函数使用的空间量都非常小——可能是一些整数或几个小数组,等等。想想几十或几百字节,通常不是千字节,当然也不是兆字节。这主要是惯用的,当您使用了足够多的自己和他人的代码时,您会习惯于在堆栈上有意义的东西和没有意义的东西。在生产代码中,将兆字节作为实际的局部变量是非常不寻常的

    实际上,在现实世界中,堆栈溢出错误的主要原因是偶然的无限递归——当您在没有递归基本情况下反复调用相同的函数时。这些堆栈帧可能每个都很小,但如果调用链是无界的,最终会溢出


    当您想要使用实际较大的内存块、较大的字符串缓冲区等时,通常会从称为“堆”的不同共享内存块中分配它们。您可以分配(与
    malloc
    及其近亲一起)您需要的内容,然后在完成后分配
    free
    。堆的内存空间对于您的程序来说是全局的,并且不受特定函数调用的约束或关联

    C语言标准对堆栈一无所知。函数的调用方式、参数的传递方式以及自动存储对象的存储位置取决于实现

    大多数实现实际上只有一个堆栈,但我将给出一些非常常见的示例

  • 罗塞斯。许多RTO将任务作为正常功能执行。作为单独任务的函数将具有单独的堆栈
  • 许多多任务库(如pthread)将为线程(即函数)提供单独的堆栈
  • 许多硬件设计都有不止一个堆栈,例如非常复杂的堆栈