c++ - 为什么 cout 的访问冲突和 printf 的堆栈溢出

标签 c++ c stack-overflow

我想知道为什么在下面的两个代码片段中,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/

相关文章:

c++ - 支持多态的Stored-by-Value Pool,如何使用智能指针?

c - if 条件语句中的等待函数

c - 如何在启动时从共享库加载所有符号?

c - 使用指针字符串会导致段错误

android - 方法 supportInvalidateOptionsMenu 在 android 2.3 抛出 stackoverflowerror

javascript - IE 中的 Google Maps API v3 不显示 map 或标记

c++ - 这个 bin-to-dec 代码有什么问题?

c++ - 从 VBA 调用 xll UDF

c++ - Windows Unicode C++ 流输出失败

java - stackoverflow 和 arrayindexoutofbounds 有什么区别?