localtime()函数的使用问题

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_aptm_b
00A5FC9800A5FC98

 可知其实 ptm_aptm_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_aptm_b
006BFAB8006BFAB8

 由此可以得出结论: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下用的。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值