我想知道为什么在下面的两个代码片段中,cout 和 printf 会发生访问冲突和堆栈溢出。
我想知道为什么第一个代码的访问冲突而不是堆栈溢出。
我得到访问冲突的第一个代码:
void Test();
void Test()
{
static int i = 0;
cout << i++ << endl;
Test();
}
int main()
{
Test();
return 0;
}
我得到 Stack Overflow 的第二个代码:
void Test();
void Test()
{
static int i = 0;
printf("%d\n", i++);
Test();
}
int main()
{
Test();
return 0;
}
最佳答案
我假设您了解这两个函数在尝试无限递归后由于堆栈耗尽而崩溃。我认为您要问的是:为什么 cout 示例也不会因“堆栈溢出”而崩溃?
我认为答案与编译器对尾递归的检测无关。如果编译器优化了递归,则两个示例都不会崩溃。
我猜是怎么回事。 “堆栈溢出”异常在某些情况下(例如,Windows)通过在堆栈末尾分配的单个虚拟内存“保护页面”来实现。当堆栈访问命中此保护页时,会生成一种特殊的异常类型。
由于 Intel 小粒度页面的长度为 4096 字节,因此保护页面负责保护该大小的内存范围。如果函数调用分配了超过 4096 字节的局部变量,则从它进行的第一次堆栈访问实际上可能超出保护页。下一页可能是未保留的内存,因此在这种情况下访问冲突是有意义的。
当然,您没有在示例中显式声明任何局部变量。我假设其中一个 operator<<() 方法分配的局部变量不止一页。换句话说,访问冲突发生在 operator<<() 方法的开头附近或 cout 实现的某些其他部分(临时对象构造函数等)
此外,即使在您编写的函数中,运算符<<() 实现也需要为中间结果创建一些存储空间。该存储可能由编译器分配为本地存储。不过,我怀疑在您的示例中它会加起来达到 4k。
真正理解的唯一方法是查看访问冲突的堆栈跟踪,以了解是什么指令触发了它。
获得了访问冲突的堆栈跟踪和错误操作码区域周围的反汇编?
如果您使用的是 Microsoft C 编译器,另一种可能性是 printf() 和您自己的函数是用/Ge 编译的,而 operator<<() 不是,或者只有您的函数是用/Ge 和类似因素编译的上述那些巧合地导致了您看到的行为——因为在 printf() 示例中,崩溃发生在您的函数被调用时,而在 operator<<() 情况下,崩溃发生在您调用库时。
关于c++ - 为什么 cout 的访问冲突和 printf 的堆栈溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1570737/