虚函数是否不太可能导致堆栈溢出?

Are virtual functions less likely to cause stack overflow?

在一次采访中有人问我函数参数是位于栈中还是堆中。我很确定这是作为示例给出的,以说明由于嵌入式内存规模的堆栈溢出风险如何避免递归函数。然而,这似乎是一个棘手的问题,因为我一直在审查虚函数,它允许 dynamic dispatch.

在搜索 之后,似乎普通老式常规函数参数的内存位置取决于实现。

其他答案对虚函数的说法几乎相同——无法保证虚函数参数在内存中的实现方式。

所以我想了解一下:

  1. 运行时函数实现如何以及在何处(堆栈?堆?两者都略知一二?)?

  2. 在对 ABI 所说的内容一无所知的情况下,了解此信息是否有用/是查找此类信息的好地方,还是有更好的地方可以查看?除了规格之外,是否有任何实验数据可以让我们“了解”内存在实践中的平均工作原理?

还有,先-post你好!

参数和 return 值是调用约定的保留,有许多调用约定,它们都有细微差别。 在 64 位 x86 计算中,只有 2 个调用约定值得考虑 Microsoft 和 SystemV。 两种调用约定都旨在尽可能多地传递寄存器。在我脑海中浮现的是 4 个整数参数和 4 个浮点参数。有特殊情况,但通常超出此范围的任何内容都会被推入堆栈。

您可以非虚拟地调用 virtual 函数。因此,决定虚拟性的不是函数的定义,而是调用的类型。因此,几乎不可能有不同的约定。

parameter/return 值的传递方式和位置由函数的调用约定定义,virtual 对此完全没有影响。一些调用约定仅使用堆栈,而其他约定混合使用堆栈和 CPU 寄存器。但从来没有堆。

virtual 所做的唯一一件事就是指示如何确定函数地址,以及在其隐藏的 this 参数中传递什么值。但是参数值(包括 this)和 return 值仅根据调用约定传递。

在 32 位上,跨实现标准化的两个调用约定是 __cdecl__stdcall。其他调用约定是实现定义的,例如 __fastcall__thiscall.

在 64 位上,ABI 定义了所有实现都必须遵循的单一调用约定。忽略 32 位调用约定。

"calling a function" 的整个想法与堆栈有着内在的联系。调用约定可以将一些参数移到寄存器中,间接传递参数(通过指针或引用)可以减少实际传递的数据量,但 return 地址仍然必须压入堆栈。

没有堆栈影响的合法方法是内联函数,有人可能会争辩说这本身并不完全是 "function call",并且现在越来越难以保证 - 也就是说,决定是取决于编译器。
可以内联尾递归,将其变成无限循环,因此不会 运行 永远出栈。

最后,virtual 连接到任何一个的唯一方法是动态链接调用更难内联 - 它们需要先去虚拟化,这仍然不是不可能的。

总体结论好像是"if there is any connection at all, it's in favor of non-virtual functions"。