旧事重提:再谈局部变量返回

 

在做内存分配函数hook过程中,出现了一个导致程序崩溃的问题,最终定位到问题产生的罪魁祸首居然又是不知哪位大侠写的函数返回局部变量导致的。

 

该函数的实现大致是这样的:

char * GetDateTime ()

{

    char ret[64] = {0};

    SYSTEMTIME st;

       GetLocalTime(&st);

       sprintf(ret, "Current Time: %d-%d-%d, %d:%d:%d %d\n", st.wYear, st.wMonth,

              st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);

   

    return ret;

}

 

这就是大家常说的不能返回局部变量的问题,所谓不能返回局部变量,是指局部变量是存在于栈中,函数被调用时会先在栈中为变量申请内存,在函数执行完所有代码返回时,会先把返回的值(注意:这个值可能是普通的数值,也可能是变量的地址)放到寄存器eax中,接着退出该调用函数(在退出被调用函数后,为局部变量申请的内存就会被系统标记为可占用,就是说其他函数或代码都可以使用那块内存),然后回到主函数中取出eax的值放到主函数中的其他变量中。

如果被调函数返回的值是普通的数值的话,那么不会有问题。

如果被调函数返回的值是地址的话,那么回到主函数后,系统会把这个地址赋值给主函数中的指针变量,也就是说主函数中的指针变量指向了一个已经被标记为可供其他函数或代码占用的内存空间,结果可想而知,如果那块内存没有被改写,那它的值确实是对的;而如果曾被改写过的话,那就是不可预见的了。

所以所谓的不能返回局部变量,其实是并非那块内存被销毁而是相当于可被再利用而导致结果的不确定性。

 

如果函数返回值非要是一个局部变量的地址,那么怎么办?也是有办法的,将该局部变量声明为static类型,如下蓝色部分:

char * GetDateTime()

{

    static char ret[64] = {0};

    SYSTEMTIME st;

       GetLocalTime(&st);

       sprintf(ret, "Current Time: %d-%d-%d, %d:%d:%d %d\n", st.wYear, st.wMonth,

              st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);

   

    return ret;

}

这样就不会有上面说的问题了,原因是通过将局部变量声明为静态局部变量,那么该局部变量就发生了本质的变化,其变量地址由动态数据区转移到静态数据区。静态数据区存放的是全局变量和静态变量。这里说的静态数据区其实是相对于堆栈中动态分配内存来说的。

你可能感兴趣的:(旧事重提:再谈局部变量返回)