编程基础:栈帧
外观
我们之前讨论过 使用函数和子程序在编程中的好处,但由于每个函数都独立存储,计算机需要能够存储它当前正在处理的内容(包括其变量和参数)并将其放在一边,以便它可以转到新函数,然后能够返回到它所在的位置。这就是栈帧的用途。
考虑以下程序
Sub Main
openFile("birthday_picture.png")
Console.WriteLine("Finished opening file... goodbye!")
End Sub
Sub openFile(fileName as String)
If fileExists(fileName) Then
' code to open file goes here!
Else
Console.WriteLine("The file called {0} doesn't exist!", fileName)
End If
End Sub
Function fileExists(fileNameToCheck as String) as Boolean
Dim filePath as String
filePath = "C:\\Users\\My Pictures\\" + fileNameToCheck
' code to check if filePath exists and return True if it does, or False if it doesn't
End Sub
该程序尝试通过调用 openFile 子程序来打开一个文件(名为 birthday_picture.png)。该子程序调用一个函数来确保文件存在,并根据其返回的布尔值(True 或 False),子程序将继续打开文件或打印错误消息。当文件被打开(或者在文件不存在的情况下,打印错误消息)时,程序会在结束前写入一条消息。
要理解为什么需要栈帧,请考虑程序开始运行的第一个子程序:Main。程序开始执行后,它会查看第一行指令并转到 openFile 子程序。但是,计算机需要记住在完成 openFile 后返回到 Main,否则它永远无法打印告别消息!类似地,如果我们查看 openFile,它有一个对 fileExists 的调用,但是计算机也需要知道返回到 openFile,并且在 openFile 内的正确位置,否则它可能会尝试打开一个不存在的文件!
计算机跟踪所有这些信息的方式是每次它被调用到另一个子程序或函数时创建一个栈帧。在这个栈帧中,它存储
- 返回地址;这是它在完成调用后应该返回的点
- 局部变量;这些是当前子程序/函数中应该恢复的变量
- 参数;传递给函数/子程序的数据
一个图表可以帮助你形象地理解这个过程
当计算机遇到函数或子程序调用时,它会将局部变量、参数和当前地址存储在内存中,并将其添加到栈中。完成调用后,它会从栈中删除该帧,并查看下一帧的返回地址(该地址将属于调用它的函数),然后从该点继续执行。这个过程一直持续到栈为空(即它返回到程序中的第一个子程序)并且程序结束。