阅读《C专家编程》中,描述一个Sun的Pascal编译器出现的Bug——编译器日期被破坏。这个Bug点出C/C++中的函数返回局部变量可能存在的问题:
1、如果函数返回的是局部变量的值,程序不会出现问题;
2、如果函数返回的是局部变量的指针(地址),可能会出现问题。
书中给出的代码如下:
<span style="font-size:14px;">char * localized_time(char *filename)
{
struct tm *tm_ptr;
struct stat stat_block;
char buffer[120];
stat(filename,&stat_block);
tm_ptr=localtime(&stat_block.st_mtime);
strftime(buffer,sizeof(buffer),"%a %b %e %T %Y",tm_ptr);
return buffer;
}</span>
然而,在实际的编译(32bit-Win7+VS2008环境)中,会出现问题,可能是Windows和Unix之间的差异,具体留后面讨论。
书中讲到:在C语言中,自动变量在堆栈中分配内存。当包含自动变量的函数或代码块退出时,它们所占用的内存便被回收,它们的内容肯定会被下一个所调用的函数覆盖。这一切取决于堆栈中先前的自动变量位于何处,活动函数声明了什么变量,写入了什么内容等。原先自动变量地址的内容可能被立即覆盖,也可能稍后才被覆盖。
解决方案如下几种:
1、返回一个指向字符串常量的指针。(此方案太过于局限)
2、使用全局声明的数组。
<span style="font-size:14px;">char *func()
{
...
global_array[i]=
...
return global_array;
}</span>
缺点:<1>、任何人都有可能在任何时刻修改这个全局数组;
<2>、该函数的下一次调用也会覆盖该数组的内容;
<3>、浪费内存空间(尤其是数组比较大的情况)。
3、使用静态数组。
char *func()
{
static char buffer[20];
...
return buffer;
}
优点:可以防止任何人修改该数组,只有拥有指向该数组的指针的函数才能修改该静态数组(通过参数传递)。
缺点:<1>、该函数的下一次调用也会覆盖该数组的内容;
<2>、浪费内存空间(尤其是数组比较大的情况)。
4、显示分配一些内存,保存返回的值。
char *func()
{
char *s =malloc(120);
...
return s;
}
优点: <1>、可以防止任何人修改该数据,且该函数在下次被调用不会覆盖以前的返回值;
<2>、适用于多线程的代码。
缺点:需要承担内存管理的工作,容易产生内存泄漏。
5、出参的方式返回数组,由调用者管理内存分配与回收(推荐)
void func(char *result,int size)
{
strncpy(result,"just for test",size);
}
buffer =malloc(size);
func(buffer,size);
...
free(buffer);
最好在同一代码块同时进行“malloc”和“free”操作,遵循谁分配,谁释放的原则。
好了,分享了作者的阐述,下面是我的通过编译的测试代码。
#include "stdafx.h"
#include <time.h>
#include <sys/stat.h>
#include <string.h>
char * localized_time(char *filename)
{
struct tm *tm_ptr;
struct stat stat_block;
char buffer[120];
stat(filename,&stat_block);
tm_ptr=localtime(&stat_block.st_mtime);
int iErrorCode=strftime(buffer,sizeof(buffer),"%a %b %Y %c ",tm_ptr);
printf("strftime:%s\n",buffer);
return buffer;
}
int main(void)
{
char *szFileName="test.cpp";
printf("return :%s\n",localized_time(szFileName));
return 0;
}
遇到两个问题:
1、书中给出的代码段:strftime(buffer,sizeof(buffer),"%a %b %e %T %Y",tm_ptr);运行是产生崩溃。
原因是strftmie.c第832行抛出异常,_ASSERTE( ( "Invalid format directive" , 0 ) );
结合strftmie.c中的源代码及MSDN。是因为strftime()函数没有“%e和%T”,这里可能是Windows和Unix的差异?没有深究。
2、运行上述修改过的代码后,发现:
<1>、调用函数可以打印出正确的返回值;
<2>、如果把buffer[120]改为buffer[90],调用函数可以打印出正确返回值的一部分+乱码;
<3>、如果把buffer[120]改为buffer[90],调用函数打印的结果为空;
<4>、注释掉 “printf("strftime:%s\n",buffer);”,调用函数打印的结果为乱码。
这也印证了 如果函数返回的是局部变量的指针(地址),可能会出现问题。
ZhaiPillary
2015/05/21 于上海
博客地址:http://blog.youkuaiyun.com/pillary