07 | 函数调用:为什么会发生stack overflow?
该思维导图由 AI 生成,仅供参考
为什么我们需要程序栈?
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了函数调用在计算机指令层面的实现,以及栈溢出错误的发生情况。通过分析函数调用的内部实现和栈溢出的原因,读者可以更好地理解这一常见错误的产生机制。文章以简洁清晰的语言解释了栈溢出错误的技术特点,对于想要深入了解程序运行机制的读者来说,是一篇值得阅读的文章。 文章通过分析简单的C程序function_example.c,介绍了函数调用和栈溢出错误的相关内容。在讲解函数调用的内部实现时,文章以汇编代码的形式展示了函数调用和返回的过程,深入剖析了函数调用时的压栈和出栈操作。同时,文章还探讨了函数调用中的跳转指令和程序调用寄存器的使用,以及栈的数据结构和布局。通过这些详细的分析,读者可以更好地理解函数调用和栈溢出错误的产生机制。 此外,文章还介绍了如何构造栈溢出错误以及如何利用函数内联进行性能优化。通过讲解这些内容,读者可以了解到在CPU指令层面程序的函数间调用是如何执行的,以及函数内联和栈溢出的优化方案和常见Bug。这些内容为读者提供了更深入的技术理解和启发。 总的来说,本文通过深入浅出的方式解释了函数调用和栈溢出错误的技术特点,为读者提供了一篇有价值的技术文章。
《深入浅出计算机组成原理》,新⼈⾸单¥68
全部留言(140)
- 最新
- 精选
- kdb_reboot倒数第二图比较好 补充一下寄存器说明 rbp - register base pointer (start of stack) rsp - register stack pointer (current location in stack, growing downwards) 建议将图编号这样评论的时候也能有所指代
作者回复: kdb_reboot,谢谢建议。这个建议不错,我麻烦编辑稍后加上。
2019-05-10857 - chengzise老师这里需要补冲一下,函数调用call指令时,(PC)指令地址寄存器会自动压栈,即返回地址压栈,函数返回ret指令时会自动弹栈,即返回地址赋值给PC寄存器,把之前。图片有显示压栈,没有文字说明,其他同学可以不太理解。
作者回复: 👍,谢谢 chengzise 同学,谢谢补充。call 在调用的时候会做push eip的操作,而在ret的时候会做pop eip的操作。
2019-05-10246 - 秋天现在有点模糊的是栈只是用来做函数调用,记录跳转地址的?它和寄存器的本质区别吗?这两者能给解释一下吗?谢谢!
作者回复: 秋天同学你好, 我们在这里先要分清楚 抽象概念 和 实际的硬件实现部分。 寄存器 和 内存,是在硬件层面就是放在不同的位置,使用不同的物理硬件来实现的。 而栈是一个抽象概念,实际是存放在内存里面的。栈是用来管理函数调用的“现场”的。确保函数调用完成后,还能回到调用者那里。
2019-05-27230 - Better mepush rbp; mov rbp rsp; 老师,想问这两句是如何控制函数调用的
作者回复: 这两个是在维护函数调用的栈帧。 指令地址本身的压栈和出栈是在 call 和 ret 的部分进行的。 你可以认为 call 的同时进行了一次 push rip 把PC寄存器里面的内容压栈了,而在 ret 的时候 pop 把这部分数据出栈写回到PC寄存器里面了。
2019-05-10520 - 蚂蚁内推+v老师 巨大数组为什么是分配在栈空间的呢?(java里面是分配到堆上的 c预约和java不同吗)
作者回复: 如果是函数作用域内的临时变量,就是分配在栈上的啊。 首先Java运行时候的JVM自己就是一个应用程序,和C编译出来的机器码就不一样。 Java通过New出来的对象是在堆上,但是函数作用域里面的临时变量,以及对应的引用都是放在栈上的。
2019-06-06217 - Allenint main() { d: 55 push ebp e: 89 e5 mov ebp,esp 10: 83 ec 18 sub esp,0x18 int x = 5; 13: c7 45 f4 05 00 00 00 mov DWORD PTR [ebp-0xc],0x5 int y = 10; 1a: c7 45 f8 0a 00 00 00 mov DWORD PTR [ebp-0x8],0xa 老师,请教下: sub esp,0x18 的目的是干什么? 0x18 是怎么计算的?
作者回复: 这个是在维护栈帧,因为后面有两个临时变量需要在调用其他函数之前保留到栈里面。0x18是16进制的24 两个int各需要8 bit,一共16bit,然后ebp本来就要8bit,一共只有24bit,考虑对齐到16bit的整数倍还要额外的8bit一共24bit
2019-05-16216 - once老师 call指令已经将pc寄存器里的下一个指令(add函数执行完的跳转地址)压栈了 那 add函数里面的 push rbp压的又是什么栈 还有把main函数从栈底压到栈顶这个是什么意思 没有图看了好几遍也懵懵的 help老师
作者回复: 这两个是在维护函数调用的栈帧。 指令地址本身的压栈和出栈是在 call 和 ret 的部分进行的。 你可以认为 call 的同时进行了一次 push rip 把PC寄存器里面的内容压栈了,而在 ret 的时候 pop 把这部分数据出栈写回到PC寄存器里面了。 这一部分的确是有不少同学表示写得不够清楚,我晚点看单独会在FAQ里面更详细地写一下这个过程。也再修订一下这一讲希望能讲解地更清楚一些。
2019-08-28413 - 秋天java程序应该不是那种分页的形式,在虚机起动的时候我们根据配置或者是起动参数指定需要的内存大小,应该是预先分配好一大段连续的内存供程序使用,所以在程序运行过程中如果超出啦,预分配大小的内存就会出现内存溢出的错误
作者回复: java虚拟机其实是一个应用层的程序,java虚拟机的内部内存分配其实是在虚拟内存地址层面的分配。的确不涉及到操作系统和硬件层面的分页问题。
2019-05-279 - 小猪老师,我觉得用goto就可以实现函数调用,起先跳转到函数,运行完,在用goto跳回来就行了
作者回复: 小猪同学你好, 那么被调用的函数运行完之后,怎么知道要跳回到哪一个地址呢?
2019-05-2289 - Alphalin请问地址34后面的地址怎么直接到39了? 35地址在哪呢
作者回复: 34的整个指令有长度啊,你数一数这条指令对应的机器码需要多少空间呢?
2019-05-1527