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()示例中,在调用函数时发生崩溃,而在调用库时发生运算符<<()的情况.