繁体   English   中英

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

[英]Why Access Violation for cout and Stack Overflow for printf

我想知道为什么在以下两个代码片段中 cout 会发生访问冲突和 printf 会发生堆栈溢出。

我想知道为什么第一个代码的访问冲突而不是堆栈溢出。

我得到访问冲突的第一个代码:

void Test();

void Test()
{
    static int i = 0;
        cout << i++ << endl;    
    Test();
}
int main() 
{

    Test();
    
    return 0;
}

我得到堆栈溢出的第二个代码:

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 实现的其他部分(临时对象构造函数等)

此外,即使在您编写的函数中, operator<<() 实现也需要为中间结果创建一些存储空间。 该存储可能被编译器分配为本地存储。 不过,我怀疑在您的示例中它会增加到 4k。

真正理解的唯一方法是查看访问冲突的堆栈跟踪,以查看触发它的指令。

是否有访问冲突的堆栈跟踪和错误操作码区域周围的反汇编?

如果您使用的是 Microsoft C 编译器,另一种可能是 printf() 和您自己的函数是用 /Ge 编译的,而 operator<<() 不是,或者只有您的函数是用 /Ge 编译的,并且因素与所描述的类似以上巧合导致您看到的行为 - 因为在 printf() 示例中,崩溃发生在您的函数被调用时以及在 operator<<() 情况下,当您调用库时。

这两个递归函数永远不会停止。 似乎在第二种情况下,编译器没有做尾部优化,因此堆栈溢出。

这两个函数都会在我的机器上触发堆栈溢出。 我正在用 MS Visual Studio 2005 编译它。也许你应该指定你的平台和编译器,这将有助于调查......

也许你在调试模式下编译了一些东西,你的“cout”实现包括一些检查,由于堆栈损坏而无法执行? 也许您的编译器生成了试图从堆栈溢出中恢复并弹出无效返回地址的代码? 也许您正在移动设备上运行它? 不知道平台和编译器就很难说。

无限递归调用是针对堆栈溢出的。 至于访问冲突……这真的取决于STL流的实现。 您需要查看流的源代码以找出...

尽管大多数人误解了您的问题,但答案就在那里。

第二个示例以堆栈溢出结束,因为每个函数调用都将一个帧压入堆栈。 最终,它变得太大了。 我同意 Cătălin Pitiș 的观点,即很难知道为什么流示例以访问冲突结束而不查看源。

这让我想起了堆栈被破坏和调试器没有捕捉到失败的程序崩溃的问题

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM