c 语言时间函数陷阱

  在编写C 语言的应 用程序时 ,为了获 取或者打 印一些跟 时间有关 的信息, 我们经常 会使用到 C语言自 带的一些 时间函数 ,诸如: time 、loc alti me、c time 、mkt ime和 asct ime等 。但你可 能没有注 意到这里 面含有一 些有趣的 现象,先 来看一个 例子:

  1 #include < stdio.h>
  2 #include < time.h>
  3
  4 int main ()
  5 {
  6
  7    time_t time_1, time_2;
  8    struct tm *tm_1, *tm_2, *tm_3;
  9    struct tm tm_4, tm_5;
 10
 11    printf(" ---- ---- ---- ---- ----  PART I ---- ---- ---- ---- ---\ n" );
 12
 13    time_1 = time(NULL);
 14    sleep(3);
 15    time_2 = time(NULL);
 16    printf(" time1:%d time2:%d\n" ,time_1,time_2);
 17
 18    tm_1 = (struct tm*)localtime(& time_1);
 19    tm_2 = (struct tm*)localtime(& time_2);
 20    tm_3 = (struct tm*)localtime(& time_1);
 21
 22    printf(" tm_1 ptr:%p tm_2 ptr:%p tm_3 ptr:%p\n" ,tm_1,tm_2,tm_3);
 23    printf(" asctime(tm_1):%s" ,asctime(tm_1));
 24    printf(" asctime(tm_2):%s" ,asctime(tm_2));
 25    printf(" asctime(tm_3):%s" ,asctime(tm_3));
 26 }

       在看这段 代码的输 出结果之 前,先问 大家两个 问题:
        (1) 第22行,struct tm结构体 tm_1 、tm_ 2和tm _3的值 有什么关 系?
        (2) 第23- 26行的 输出结果 中,tm _2的时 间真的比 tm_1 晚3秒吗
 
       接下来, 我们来看 一下这段 代码的输 出结果:
---- ---- ---- ---- ----  PART I -------------------
time1:1340256774 time2:1340256777
tm_1 ptr:0xfec6f48 tm_2 ptr:0xfec6f48 tm_3 ptr:0xfec6f48
asctime(tm_1):Thu Jun 21 01:32:54 2012
asctime(tm_2):Thu Jun 21 01:32:54 2012
asctime(tm_3):Thu Jun 21 01:32:54 2012
       
       这里的打 印结果是 否跟你前 面预想的 一样呢? 没错,第 22行中 的tm_ 1、tm _2和t m_3其 实指向的 是同一个 地址。在 loca ltim e函数的 实现中, 采用了一 个静态内 部str uct tm结构 体来存储 对应的时 间信息。 每次对l ocal time 函数的调 用,都将 会修改内 部这个s truc t tm结构 体,也就 是说,这 个结构体 将只会保 存最新的 调用结果 。因此, loca ltim e每次返 回的st ruct  tm结构 体也将是 同一个, 即指向的 地址是同 一个。这 也就意味 着,后续 第23行 到第25 行对as ctim e的调用 中,实际 上传入的 都是同一 个结构体 (指向同 一个地址 的指针) ,结果它 们打出来 的时间一 样也就不 足为奇了

       我们再来看以下这段代码:
  1 #include < stdio.h>
  2 #include < time.h>
  3
  4 int main ()
  5 {
  6
  7    time_t time_1, time_2;
  8    struct tm *tm_1, *tm_2, *tm_3;
  9    struct tm tm_4, tm_5;
 10
 11    printf(" ---- ---- ---- ---- ----  PART I ---- ---- ---- ---- ---\ n" );
 12
 13    time_1 = time(NULL);
 14    sleep(3);
 15    time_2 = time(NULL);
 16    printf(" time1:%d time2:%d\n" ,time_1,time_2);
 17
 18    tm_1 = (struct tm*)localtime(& time_1);
 19    tm_2 = (struct tm*)localtime(& time_2);
 20    tm_3 = (struct tm*)localtime(& time_1);
 21
 22    printf(" tm_1 ptr:%p tm_2 ptr:%p tm_3 ptr:%p\n" ,tm_1,tm_2,tm_3);
 23    printf(" asctime(tm_1):%s" ,asctime(tm_1));
 24    printf(" asctime(tm_2):%s" ,asctime(tm_2));
 25    printf(" asctime(tm_3):%s" ,asctime(tm_3));
 26    
 27
 28    printf(" ---- ---- ---- ---- ----  PART II ---- ---- ---- ---- ---\ n" );
 29
 30    time_1 = time(NULL);
 31    sleep(3);
 32    time_2 = time(NULL);
 33    printf(" time1:%d time2:%d\n" ,time_1,time_2);
 34
 35    tm_4 = *((struct tm*)localtime(& time_1));
 36    tm_5 = *((struct tm*)localtime(& time_2));
 37
 38    printf(" tm_4 ptr:%p tm_5 ptr:%p\n" ,& tm_4,& tm_5);
 39    printf(" tm_4 sec:%d tm_5 sec:%d\n" ,tm_ 4.tm _sec ,tm_ 5.tm _sec );
 40
 41    printf(" asctime(& tm_4):%sasctime(& tm_5):%s" ,asctime(& tm_4),asctime(& tm_5));
 42    printf(" asctime(& tm_4) ptr:%p asctime(& tm_5) ptr:%p\n" ,asctime(& tm_4),asctime(& tm_5));
 43
 44    printf(" asctime(& tm_4):%s" ,asctime(& tm_4));
 45    printf(" asctime(& tm_5):%s" ,asctime(& tm_5));

       在第28 行之前跟 上面的示 例代码是 一样,这 里就不再 冗述,我 们主要来 看剩余部 分。既然 在前面我 们已经知 道了lo calt ime返 回的是同 一个内部 静态结构 的地址, 那么我们 不禁想到 可以将它 赋值给本 地结构体 ,这样前 面对lo calt ime函 数的返回 值可以得 到保存, 不会被后 面的调用 所覆盖, 如第35 和36行 所示。那 么,我们 不禁想问 :第41 行打印出 来的结构 体tm_ 4和tm _5对应 的时间结 果跟第4 4-45 行打印出 来的一样 么?

       我们来看一下PART II的输出结果:

---- ---- ---- ---- ----  PART II -------------------
time1:1340256777 time2:1340256780
tm_4 ptr:0xffe82dd8 tm_5 ptr:0xffe82e08
tm_4 sec:57 tm_5 sec:0
asctime(& tm_4):Thu Jun 21 01:33:00 2012
asctime(& tm_5):Thu Jun 21 01:33:00 2012
asctime(& tm_4) ptr:0xfec59dc asctime(& tm_5) ptr:0xfec59dc
asctime(& tm_4):Thu Jun 21 01:32:57 2012
asctime(& tm_5):Thu Jun 21 01:33:00 2012

       输出结果 跟你的预 期一样么 ?我们惊 奇地发现 ,第41 行打印的 结果中, tm_4 和tm_ 5的时间 字符串是 一样的。 但我们通 过打印出 tm_4 和tm_ 5结构体 的地址, 以及对应 的秒数( tm.s ec), 都可以让 我们确信 这次tm _4和t m_5是 两个不同 的时间结 构体。问 题其实出 在asc time 函数中, 通过打印 asct ime调 用tm_ 4和tm _5结构 体后返回 的指针地 址,我们 发现这两 个地址是 一样的。 从asc time 函数的行 为,我们 不难联想 到,其实 它的内部 实现跟l ocal time 类似,它 也使用了 一个内部 静态字符 数组来保 存转换好 的时间字 符串。每 次对as ctim e的调用 ,都将修 改这个字 符串,而 函数每次 都将返回 同一个地 址,即内 部静态字 符数组的 地址。

       下面我们来分析一下第41行的行为:
  1) 调用asctime(& tm_4 ),按照 tm_4 的信息更 新内部静 态字符数 组,返回 字符数组 的地址;
  2) 调用asctime(& tm_5 ),按照 tm_5 的信息更 新内部静 态字符数 组,返回 字符数组 的地址。 在这一步 中,新的 tm_5 的信息将 会覆盖掉 原来tm _4的那 些信息。
  3) 打印第一 个参数指 向地址的 字符串, 即该内部 静态字符 数组。此 时这个字 符串已经 被更新为 tm_5 的信息了 ,因此, 将打印t m_5对 应的时间 信息。
  4) 打印第二 个参数指 向地址的 字符串, 即tm_ 5对应的 时间信息
 
       而在第4 4行中, 我们在调 用asc time 函数后, 随即将信 息打印出 来,此时 则为tm _4的时 间信息。 同理,跟 我们对l ocal time 函数的操 作类似, 我们也可 以通过本 地字符数 组来保存 asct ime函 数的调用 结果,从 而避免结 果被后续 的调用所 覆盖。

       根据PO SIX标 准,时间 函数as ctim e()、 ctim e()、 gmti me() 和loc alti me() 函数都将 返回内部 静态对象 ,要么是 stru ct tm结构 体,要么 是字符数 组。因此 ,在对这 些函数的 调用时, 我们需要 额外的小 心,如果 不需要保 存其调用 结果,我 们可以及 时地打印 ,如果需 要保存其 调用结果 ,可以采 用本地结 构来临时 存放。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值