为什么访问违反cout和堆栈溢出printf

mah*_*esh 4 c c++ stack-overflow

我想知道为什么在以下两个代码片段中针对printf的cout和Stack Overflow发生访问冲突.

我想知道为什么第一个代码而不是Stack Overflow的Access Violation.

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

void Test();

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

    Test();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

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

void Test();

void Test()
{
    static int i = 0;
        printf("%d\n", i++);    
    Test();
}
int main() 
{

    Test();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

答案将受到高度赞赏.

提前致谢

Hea*_*utt 18

我假设您理解这两个函数在尝试无限递归后由于堆栈耗尽而崩溃.我想你要问的是:为什么cout示例不会因"Stack Overflow"崩溃?

我不认为答案与编译器检测尾递归有关.如果编译器优化了递归,那么这两个示例都不应该崩溃.

我猜想发生了什么事.在某些情况下(例如,Windows)实现了"堆栈溢出"异常,其中在堆栈末尾分配了单个虚拟内存"保护页".当堆栈访问命中该保护页面时,会生成一个特殊的异常类型.

由于英特尔小粒度页面的长度为4096字节,因此保护页面可以防范大小的内存范围.如果一个函数调用分配了超过4096个字节的局部变量,那么它的第一个堆栈访问可能实际上超出了保护页面.可以预期下一页是无保留的内存,因此在这种情况下访问冲突是有意义的.

当然,您没有在示例中显式声明任何局部变量.我假设其中一个operator <<()方法分配了多个局部变量页面.换句话说,访问冲突发生在operator <<()方法的开头附近或cout实现的某些其他部分(临时对象构造函数等)附近

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

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

是否存在访问冲突的堆栈跟踪以及故障操作码区域周围的反汇编?

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