堆
在此上下文中,堆栈是在程序运行时放置数据的最后进先出缓冲区。 最后一次出来(LIFO)意味着你输入的最后一件事总是你要退回的第一件事 - 如果你在堆叠上推2个项目,'A'然后'B',那么你首先要弹出的东西 在堆栈中将是'B',接下来是'A'。
当您在代码中调用函数时,函数调用之后的下一条指令将存储在堆栈中,以及可能被函数调用覆盖的任何存储空间。 您调用的函数可能会为其自己的局部变量使用更多堆栈。 完成后,它释放它使用的局部变量堆栈空间,然后返回到上一个函数。
堆栈溢出
堆栈溢出是指您为堆栈耗尽的内存超出了程序应该使用的内存。 在嵌入式系统中,堆栈可能只有256个字节,如果每个函数占用32个字节,那么你只能有函数调用8深 - 函数1调用函数2调用函数3调用函数4 .... 函数8调用函数9,但函数9覆盖堆栈外的内存。 这可能会覆盖内存,代码等。
许多程序员通过调用函数A然后调用函数B来犯这个错误,然后调用函数C然后调用函数A.它可能在大多数情况下工作,但只是一次错误的输入将导致它永远进入该循环 直到计算机识别出堆栈被夸大了。
递归函数也是导致这种情况的原因,但是如果你正在递归编写(即,你的函数自己调用),那么你需要注意这一点并使用静态/全局变量来防止无限递归。
通常,您正在使用的操作系统和编程语言管理堆栈,而且它不在您的手中。 您应该查看您的调用图(从您的主要显示每个函数调用的树结构),以查看函数调用的深度,以及检测非预期的循环和递归。 如果他们互相称呼过多次,则需要人为地检查故意循环和递归以防错误。
除了良好的编程实践,静态和动态测试之外,您无法在这些高级系统上做太多事情。
嵌入式系统
在嵌入式领域,特别是在高可靠性代码(汽车,飞机,太空)中,您可以进行大量的代码审查和检查,但您还要执行以下操作:
禁止递归和循环 - 由策略和测试强制执行
保持代码和堆栈相隔很远(代码在flash中,堆栈在RAM中,而且twain不会满足)
将保护带放置在堆栈周围 - 空白的内存区域,您填充了一个幻数(通常是软件中断指令,但这里有很多选项),每秒数百或数千次看保护带以确保 他们没有被覆盖。
使用内存保护(即,堆栈上没有执行,堆栈外没有读取或写入)
中断不会调用辅助函数 - 它们设置标志,复制数据,并让应用程序负责处理它(否则你可能会在函数调用树中深入8,有一个中断,然后在内部运行另外几个函数) 中断,引起井喷)。 你有几个调用树 - 一个用于主进程,一个用于每个中断。 如果你的中断可以互相打断......好吧,有龙......
高级语言和系统
但是在操作系统上运行的高级语言:
减少局部变量存储(局部变量存储在堆栈中 - 尽管编译器非常聪明,如果调用树很浅,有时会将大型本地变量放在堆上)
避免或严格限制递归
不要将程序分解成越来越小的函数 - 即使不计算局部变量,每个函数调用在堆栈上消耗多达64个字节(32位处理器,节省一半CPU寄存器,标志等)
保持呼叫树浅(类似于上面的陈述)
Web服务器
它取决于您拥有的“沙盒”,无论您是否可以控制甚至看到堆栈。 很有可能你可以像对待任何其他高级语言和操作系统一样对待Web服务器 - 它很大程度上取决于你的手,但检查你正在使用的语言和服务器堆栈。 例如,可以在SQL服务器上清空堆栈。
-亚当