返回

函数栈帧的创建和销毁,入门C语言的进阶之路

后端

函数栈帧的创建

当一个函数被调用时,系统会在内存中创建一个栈帧。栈帧是一个数据结构,它存储着函数调用所需的信息,包括:

  • 函数的参数
  • 局部变量
  • 返回地址

栈帧的创建过程如下:

  1. 系统会将函数的参数压入栈中。
  2. 系统会将函数的局部变量压入栈中。
  3. 系统会将函数的返回地址压入栈中。

栈帧的创建过程如下图所示:

+----------------+
| 返回地址      |
+----------------+
| 局部变量 1   |
+----------------+
| 局部变量 2   |
+----------------+
| ...            |
+----------------+
| 参数 1          |
+----------------+
| 参数 2          |
+----------------+
| 参数 3          |
+----------------+

函数栈帧的销毁

当一个函数返回时,系统会销毁它的栈帧。栈帧的销毁过程如下:

  1. 系统会将函数的局部变量弹出栈。
  2. 系统会将函数的参数弹出栈。
  3. 系统会将函数的返回地址弹出栈。

栈帧的销毁过程如下图所示:

+----------------+
| 返回地址      |
+----------------+
| 局部变量 1   |
+----------------+
| 局部变量 2   |
+----------------+
| ...            |
+----------------+
| 参数 1          |
+----------------+
| 参数 2          |
+----------------+
| 参数 3          |
+----------------+

函数栈帧的应用

函数栈帧在C语言中有着广泛的应用,包括:

  • 参数传递
  • 局部变量
  • 递归调用

参数传递

当一个函数被调用时,它的参数会被压入栈中。这使得函数可以访问它的参数。

例如,以下函数的两个参数xy会被压入栈中:

int add(int x, int y) {
  return x + y;
}

add()函数被调用时,栈帧如下图所示:

+----------------+
| 返回地址      |
+----------------+
| y               |
+----------------+
| x               |
+----------------+

局部变量

函数的局部变量也会被压入栈中。这使得函数可以访问它的局部变量。

例如,以下函数的局部变量sum会被压入栈中:

int add(int x, int y) {
  int sum = x + y;
  return sum;
}

add()函数被调用时,栈帧如下图所示:

+----------------+
| 返回地址      |
+----------------+
| sum             |
+----------------+
| y               |
+----------------+
| x               |
+----------------+

递归调用

当一个函数调用自身时,称为递归调用。递归调用会导致栈帧不断被创建和销毁。

例如,以下函数的递归调用会导致栈帧不断被创建和销毁:

int factorial(int n) {
  if (n == 0) {
    return 1;
  } else {
    return n * factorial(n - 1);
  }
}

factorial()函数被调用时,栈帧如下图所示:

+----------------+
| 返回地址      |
+----------------+
| n               |
+----------------+

factorial()函数递归调用自身时,另一个栈帧会被创建:

+----------------+
| 返回地址      |
+----------------+
| n-1             |
+----------------+

这个过程会一直重复,直到n等于0。

总结

函数栈帧是C语言中一个重要的数据结构。它存储着函数调用所需的信息,包括函数的参数、局部变量和返回地址。函数栈帧在参数传递、局部变量和递归调用中都有着广泛的应用。