localtime()函数的使用问题

来源:互联网 发布:人工智能用什么语言写 编辑:程序博客网 时间:2024/06/05 05:58

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_aptm_b 指向同一 struct tm 的对象。所以最后得出相差天数为0。

 在将 t_b 值改动一下,再做一次实验

time_t t_a(0), t_b(100);//将t_b由0改为100struct 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下用的。