下面内容不合适Java等高级语言程序员,如果你是Java等高级语言程序员,觉得这个问题很没有意义是正常的。
#include <iostream>
#include <unistd.h>
using namespace std;
void goodToreturn(){
long x=0;
long* xp=&x;//地址+1
for(x=6;x<10;x++)
cout<<"index of "<<x<<" : "<<*(xp+x)<<endl;
x=4+1; //局部变量和返回地址之间需要保存调用者寄存器相关信息地址+4,通过察看汇编代码可知正确
cout<<"Old Return Address:"<<*(xp+x)<<endl;//拥有错误的返回地址
*(xp+x)=reinterpret_cast<long>(&goodToreturn);
cout<<"Current Return Address"<<*(xp+x)<<endl;//纠正返回地址
sleep(5);
}
int main()
{
long array[10]={20,21,22,23,24,25,26,27,28,29};
goodToreturn();
return 0;
}
在Intel CPU,ubuntu x64,GCC编译器下,这段代码会产生多次递归,然后挂掉。适当修改X的数值可以在window平台GCC编译器下递归多次。
请特别注意:在其他操作系统,或其他编译器,或其他CPU都有可能导致本代码行为改变。
对于为什么产生递归?是因为函数原来的返回地址,被修改为自身函数指针了。C/C++允许你这样乱搞。
----------------------------------------
在window平台,栈帧结构为
函数参数 地址:高
返回地址
保存原有寄存器
局部变量 地址:低
-----------------------------------------
在Linux平台,栈帧结构为
返回地址 地址:高
保存原有寄存器
局部变量
函数参数 地址:低
-----------------------------------------
通过函数的局部变量地址,加上一个offset就可以访问函数的返回指令地址。但此法在mac os上多次测试无效。
我们还可以发现另一个问题,Old Return Address逐渐从20增加,每次加1。为什么是20,21,22,23...呢,是不是有点眼熟呢,没错就是main函数中的数组,那么如何解释?
函数返回时,栈帧上移一次,每次移动一个指针的长度,64位操作系统指针8个字节,long型正好也是8个字节。随着栈帧的不断上移就会移动到main函数的栈帧里,破坏main函数原有的数据。
对于更详细的参考和学习,请你去读一下图书。
《深入理解计算机系统》
《Unix内核剖析》
《征服C指针》