localtime()函数的使用问题
本篇部分内容参考 http://blog.csdn.net/stpeace/article/details/24440329
1. 问题描述
同 “struct tm”的任务,谈谈另一个函数的实现中遇到的问题。
定义一个类Date:
class Date
{
public:
Date() {}
Date(int n_year, int n_month, int n_day)
{
if (!DateFormIsCorrect(n_year, n_month, n_day))
{
cout << "Construction failed, the date format is incorrect!\n";
return;
}
mn_year = n_year;
mn_month = n_month;
mn_day = n_day;
}
friend ostream& operator << (ostream &out, Date &date);
Date operator + (int n_day);
Date operator - (int n_day);
int operator - (Date &date);
private:
bool DateFormIsCorrect(int n_year, int n_month, int n_day);
int mn_year, mn_month, mn_day;
};
现在来考察 int operator - (Date &date);
的实现问题。
//作用:返回*this 与 date 相差的天数
int Date::operator - (Date &date)
{
//建立两个 tm 类型指针
time_t t_a(0), t_b(0);
struct tm *ptm_a = localtime(&t_a), *ptm_b = localtime(&t_b);
//两个指针分别代表 *this 与 date
ptm_a->tm_year = mn_year - 1900;
ptm_a->tm_mon = mn_month - 1;
ptm_a->tm_mday = mn_day;
ptm_b->tm_year = date.mn_year - 1900;
ptm_b->tm_mon = date.mn_month - 1;
ptm_b->tm_mday = date.mn_day;
//得出相差的时间
t_a = mktime(ptm_a);
t_b = mktime(ptm_b);
if (t_a < t_b)
{
t_a = t_a ^ t_b;
t_b = t_a ^ t_b;
t_a = t_a ^ t_b;
}
//转换为天数
double d_differ = difftime(t_a, t_b);
int n_day = static_cast<int>(d_differ / (24 * 3600 * 1.0));
return n_day;
}
这行代码看上去没什么问题,但事实上,无论输入什么,该函数永远返回 0.
2.原因分析
原因出在函数 localtime()
上。
localtime()函数实现有一个问题:
该函数返回的是一个指针,表示某一个地址。大家知道,如果是一个非静态的局部变量,返回它的地址是错误的做法,因为非静态的局部变量在函数返回时,已经被销毁了,它的地址成为无用的地址。因此localtime函数返回的指针只有以下三种可能:要么是一个静态变量的地址,要么是一个全局变量的地址,或者是使用malloc等函数在堆上分配的空间。
对于最后一种情况,因为标准并没有规定可以对localtime返回的地址进行free,所以如果localtime函数是使用malloc函数分配空间的话,程序员不会使用free函数去释放它,因此造成内存泄露,这是不好的做法。
而localtime()返回的就是一个指向静态变量的指针。 也就是说其内部实现大致如此:
struct tm* localtime(const time_t* ptr)
{
static struct tm ret;
// 在这里计算并得到ret的值
return &ret;
}
因此不论调用多少次localtime,则它返回的地址都是一样的。只是地址中保存的内容可能不同而已。
3.检验
在函数中插入两行代码:
struct tm *ptm_a = localtime(&t_a), *ptm_b = localtime(&t_b);
cout << "ptm_a = " << ptm_a << endl;
cout << "ptm_b = " << ptm_b << endl;
最终输出结果:
ptm_a | ptm_b |
---|---|
00A5FC98 | 00A5FC98 |
可知其实 ptm_a
与 ptm_b
指向同一 struct tm
的对象。所以最后得出相差天数为0。
在将 t_b
值改动一下,再做一次实验
time_t t_a(0), t_b(100);//将t_b由0改为100
struct tm *ptm_a = localtime(&t_a), *ptm_b = localtime(&t_b);
cout << "ptm_a = " << ptm_a << endl;
cout << "ptm_b = " << ptm_b << endl;
输出结果
ptm_a | ptm_b |
---|---|
006BFAB8 | 006BFAB8 |
由此可以得出结论:localtime()函数返回的指针全都指向同一个位置,因此如果在程序中连续使用,则很可能会出错!
4.解决办法
方法一:
一次只调用一个 localtime()
函数,调用完毕后立即取出返回值的内容。
即改为如下形式:
time_t t_a(0), t_b(0);
struct tm *ptm_a = localtime(&t_a), *ptm_b;
ptm_a->tm_year = mn_year - 1900;
ptm_a->tm_mon = mn_month - 1;
ptm_a->tm_mday = mn_day;
t_a = mktime(ptm_a);//立即提取出信息
ptm_b = localtime(&t_b);
ptm_b->tm_year = date.mn_year - 1900;
ptm_b->tm_mon = date.mn_month - 1;
ptm_b->tm_mday = date.mn_day;
//t_a = mktime(ptm_a);
t_b = mktime(ptm_b);
最终可得出正确结果。
方法二:
使用其他的函数 localtime_s()
以及 localtime()_s
①.errno_t localtime_s(struct tm* _tm,const time_t *time ); (since c11)
该函数是在windows下使用的函数,需要两个参数。其中 _tm指向要填充的时间结构的指针,time指向存储的时间。 使用如下:
time_t t_a(0), t_b(0);
struct tm ptm_a , ptm_b;//注意并没有用指针
localtime_s(&ptm_a, &t_a);
localtime_s(&ptm_b, &t_b);
ptm_a.tm_year = mn_year - 1900;
ptm_a.tm_mon = mn_month - 1;
ptm_a.tm_mday = mn_day;
t_a = mktime(ptm_a);
ptm_b.tm_year = date.mn_year - 1900;
ptm_b.tm_mon = date.mn_month - 1;
ptm_b.tm_mday = date.mn_day;
t_a = mktime(&ptm_a);
t_b = mktime(&ptm_b);
注意,这里并没有用 struct tm *
而是直接定义了两个 tm
的变量。 这是因为用指针的话会出现对未初始化的指针的访问问题。
②struct tm *localtime_r(const time_t*restrict timer, struct tm *restrict result);
大致相同,不过该函数是在linux下用的。