在编写C语言的应用程序时,为了获取或者打印一些跟时间有关的信息,我们经常会使用到C语言自带的一些时间函数,诸如:time、localtime、ctime、mktime和asctime等。但你可能没有注意到这里面含有一些有趣的现象,先来看一个例子:
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的信息了,因此,将打印tm_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结构
体,要么
是字符数
组。因此
,在对这
些函数的
调用时,
我们需要
额外的小
心,如果
不需要保
存其调用
结果,我
们可以及
时地打印
,如果需
要保存其
调用结果
,可以采
用本地结
构来临时
存放。
参考文献:
1. asctime函数说明 http
://l
inux
.die
.net
/man
/3/a
scti
me
2. localtime函数说明 http
://l
inux
.die
.net
/man
/3/l
ocal
time