time_t 最终的 typedef 是啥?
Posted
技术标签:
【中文标题】time_t 最终的 typedef 是啥?【英文标题】:What is time_t ultimately a typedef to?time_t 最终的 typedef 是什么? 【发布时间】:2010-10-03 01:02:53 【问题描述】:我搜索了我的 Linux 机器并看到了这个 typedef:
typedef __time_t time_t;
但我找不到__time_t
定义。
【问题讨论】:
【参考方案1】:time_t Wikipedia article 文章对此有所了解。底线是 time_t
的类型在 C 规范中没有得到保证。
time_t
数据类型是 为存储而定义的 ISO C 库 系统时间值。这些值是 从标准返回time()
库函数。这种类型是 标准中定义的 typedef 标题。 ISO C 定义 time_t 作为算术类型,但确实 不指定任何特定类型, 范围、分辨率或编码。 也未指定的含义是 应用于时间的算术运算 价值观。Unix 和 POSIX 兼容系统将
time_t
类型实现为signed integer
(通常为 32 或 64 位宽) 代表秒数 自 Unix 时代开始: UTC 时间 1970 年 1 月 1 日午夜(不是 计算闰秒)。一些系统 正确处理负时间值, 而其他人则没有。系统使用 32 位time_t
类型易受 Year 2038 problem。
【讨论】:
但是请注意,time_t 值通常只存储在内存中,而不是磁盘上。相反,time_t 被转换为文本或其他可移植格式以进行持久存储。这使得 Y2038 问题并不是真正的问题。 @Heath:在特定系统上,同一个人创建操作系统和 C 库,在磁盘数据结构中使用time_t
可能会发生。但是,由于文件系统经常被其他操作系统读取,因此基于这种依赖于实现的类型来定义文件系统是很愚蠢的。例如,相同的文件系统可能同时用于 32 位和 64 位系统,time_t
可能会改变大小。因此,需要更准确地定义文件系统(“32 位有符号整数,给出自 1970 年开始以来的秒数,以 UTC 表示”),而不是像 time_t
那样定义。
请注意:链接的***文章已被删除,它现在重定向到time.h
内容列表。那篇文章链接到 cppreference.com,但引用的内容无处可寻……
@MichałGórny:已修复,只要文章不被删除,您始终可以查看历史记录以找到正确的版本。
-1;从 Wikipedia 引用的 POSIX 保证 time_t
已签名的声明是不正确的。 pubs.opengroup.org/onlinepubs/9699919799/basedefs/… 规定各种事物必须是“有符号整数类型”或“无符号整数类型”,但 time_t
仅表示它“应为整数类型”。一个实现可以使 time_t
无符号并且仍然符合 POSIX。【参考方案2】:
[root]# cat time.c
#include <time.h>
int main(int argc, char** argv)
time_t test;
return 0;
[root]# gcc -E time.c | grep __time_t
typedef long int __time_t;
在$INCDIR/bits/types.h
中定义通过:
# 131 "/usr/include/bits/types.h" 3 4
# 1 "/usr/include/bits/typesizes.h" 1 3 4
# 132 "/usr/include/bits/types.h" 2 3 4
【讨论】:
我在FreeBSD freebsd-test 8.2-RELEASE-p2 FreeBSD 8.2-RELEASE-p2 #8: Sun Aug 7 18:23:48 UTC 2011 root@freebsd-test:/usr/obj/usr/src/sys/MYXEN i386
中同时看到了typedef __int32_t __time_t;
和typedef __time_t time_t;
。你的结果是在 Linux 中明确设置的(至少在 Debian 的 2.6.32-5-xen-amd64 上)。
@Viet 也可以使用单行而不创建文件:***.com/a/36096104/895245
为什么用 grep 搜索 __time_t
而不是 time_t
来查找 time_t
的底层类型?省略一个步骤?
@chux-ReinstateMonica - OP 说他找到了从 time_t 到 __time_t 的 typedef。这个答案只是解决了关于 __time_t 定义的问题。但我同意,对于一般情况(其中 time_t 可能不会被类型化为 __time_t),您需要先 grep 获取 time_t,然后可能再次 grep 获取返回的内容
@MichaelFirth 够公平的。我记得我的担忧,因为即使 OP 找到了typedef __time_t time_t;
,也需要检查周围的代码以确保实际上使用了 typedef,而不仅仅是条件编译的一部分。 typedef long time_t;
可能也被发现了。【参考方案3】:
标准
William Brendel引用了***,但我更喜欢从马的嘴里说出来。
C99 N1256 standard draft 7.23.1/3 “时间的组成部分” 说:
声明的类型是 size_t(在 7.17 中描述)clock_t 和 time_t,它们是能够表示时间的算术类型
和 6.2.5/18 “类型” 说:
整数和浮点类型统称为算术类型。
POSIX 7 sys_types.h 说:
[CX] time_t 应为整数类型。
其中[CX]
是defined as:
[CX] 对 ISO C 标准的扩展。
它是一个扩展,因为它提供了一个更强大的保证:浮点数不存在。
gcc 单线
不需要像by Quassnoi提到的那样创建文件:
echo | gcc -E -xc -include 'time.h' - | grep time_t
在 Ubuntu 15.10 GCC 5.2 上,前两行是:
typedef long int __time_t;
typedef __time_t time_t;
带有来自man gcc
的一些引号的命令细分:
-E
: "在预处理阶段后停止;不要正确运行编译器。"
-xc
:指定 C 语言,因为输入来自没有文件扩展名的标准输入。
-include file
: "处理文件好像 "#include "file"" 出现在主要源文件的第一行。"
-
: 标准输入输入
【讨论】:
也不需要来自 echo 的管道:gcc -E -xc -include time.h /dev/null | grep time_t
【参考方案4】:
答案肯定是特定于实现的。要明确了解您的平台/编译器,只需在代码中的某处添加此输出:
printf ("sizeof time_t is: %d\n", sizeof(time_t));
如果答案是 4(32 位)并且您的数据超出了2038,那么您有 25 年的时间来迁移您的代码。
如果您将数据存储为字符串,您的数据会很好,即使它很简单,例如:
FILE *stream = [stream file pointer that you've opened correctly];
fprintf (stream, "%d\n", (int)time_t);
然后以相同的方式将其读回(fread、fscanf 等到 int 中),你就有了你的 epoch 偏移时间。 .Net 中存在类似的解决方法。我在 Win 和 Linux 系统之间传递 64 位纪元数没有问题(通过通信通道)。这会带来字节顺序问题,但这是另一个主题。
为了回答 paxdiablo 的问题,我想说它打印了“19100”,因为程序是这样编写的(我承认我自己在 80 年代就是这样做的):
time_t now;
struct tm local_date_time;
now = time(NULL);
// convert, then copy internal object to our object
memcpy (&local_date_time, localtime(&now), sizeof(local_date_time));
printf ("Year is: 19%02d\n", local_date_time.tm_year);
printf
语句打印固定字符串“Year is: 19”,后跟带有“years since 1900”的零填充字符串(tm->tm_year
的定义)。在 2000 年,这个值显然是 100。 "%02d"
用两个零填充,但如果超过两位数,则不会截断。
正确的做法是(只改到最后一行):
printf ("Year is: %d\n", local_date_time.tm_year + 1900);
新问题:这种想法的基本原理是什么?
【讨论】:
您可能应该使用%zu
格式说明符来格式化size_t
值(由sizeof
产生),因为它们是无符号的(u
) 并且长度为size_t (z
)·
... 或使用printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));
并避免z
问题。【参考方案5】:
在 Visual Studio 2008 下,它默认为 __int64
,除非您定义 _USE_32BIT_TIME_T
。你最好假装你不知道它的定义是什么,因为它可以(并且将会)随着平台的变化而变化。
【讨论】:
这通常有效,但如果您的程序旨在跟踪 30 年后将发生的事情,那么您没有有一个签名的 32 位是非常重要的time_t. @Rob,呸,别说了!我们将在 2036 年开始像无头鸡一样到处乱跑,就像我们为千年虫所做的那样。我们中的一些人将从 Y2k38 顾问中赚到一大笔钱,Leonard Nimoy 将推出另一本有趣的书,讲述我们应该如何去森林里躲藏...... ...这一切都会过去的,公众想知道所有的大惊小怪是什么。我什至可能会退休后为孩子们的遗产赚点钱:-)。 顺便说一句,我们只发现了一个 Y2K 错误,那是一个将日期列为 19100 年 1 月 1 日的网页。请读者练习一下为什么... 如果 30 年后发生的事件是“使备份过期”,那么您现在可能遇到麻烦,而不是在 2038 年。将 30 年添加到今天的 32 位 time_t,您会得到一个日期在过去。您的程序查找要处理的事件,找到一个过期的(100 年!),然后执行它。糟糕,没有更多备份了。【参考方案6】:time_t
在 64 位机器上为 long int
类型,否则为 long long int
。
您可以在这些头文件中验证这一点:
time.h
:/usr/include
types.h
和 typesizes.h
:/usr/include/x86_64-linux-gnu/bits
(下面的语句不是一个接一个,可以在相应的头文件中使用Ctrl+f搜索找到。)
1)在time.h
typedef __time_t time_t;
2)在types.h
# define __STD_TYPE typedef
__STD_TYPE __TIME_T_TYPE __time_t;
3)在typesizes.h
#define __TIME_T_TYPE __SYSCALL_SLONG_TYPE
#if defined __x86_64__ && defined __ILP32__
# define __SYSCALL_SLONG_TYPE __SQUAD_TYPE
#else
# define __SYSCALL_SLONG_TYPE __SLONGWORD_TYPE
#endif
4) 再次在types.h
#define __SLONGWORD_TYPE long int
#if __WORDSIZE == 32
# define __SQUAD_TYPE __quad_t
#elif __WORDSIZE == 64
# define __SQUAD_TYPE long int
#if __WORDSIZE == 64
typedef long int __quad_t;
#else
__extension__ typedef long long int __quad_t;
【讨论】:
这些文件由 glibc 在 Ubuntu 15.10 BTW 上提供。 不是long int
无处不在。见***.com/questions/384502/…【参考方案7】:
它是大多数旧平台上的 32 位有符号整数类型。但是,这会导致您的代码遭受year 2038 bug 的影响。因此,现代 C 库应该将其定义为有符号的 64 位 int,这在数十亿年内都是安全的。
【讨论】:
2038 指日可待【参考方案8】:通常,您会在 bits
或 asm
标头目录中找到这些底层实现特定的 gcc 类型定义。对我来说,它是/usr/include/x86_64-linux-gnu/bits/types.h
。
您可以只用 grep 或使用 preprocessor invocation like that suggested by Quassnoi 来查看具体的标头。
【讨论】:
【参考方案9】:time_t typedef 最终是什么?
健壮的代码不关心类型是什么。
C 种time_t
是一个真正的类型 比如double, long long, int64_t, int
等等。
它甚至可以是unsigned
,因为多次指示错误的函数的返回值不是-1
,而是(time_t)(-1)
——这种实现选择并不常见。
关键是“需要知道”的类型很少见。应编写代码以避免这种需要。
然而,当代码想要打印原始 time_t
时,会出现一个常见的“需要知道”。转换为最宽的整数类型将适应大多数现代情况。
time_t now = 0;
time(&now);
printf("%jd", (intmax_t) now);
// or
printf("%lld", (long long) now);
转换为double
或long double
也可以,但可以提供不精确十进制输出
printf("%.16e", (double) now);
【讨论】:
我需要了解情况,因为我需要将时间从 ARM 系统转移到 AMD64 系统。 time_t 在 arm 上是 32 位,在服务器上是 64 位。如果我将时间转换为一种格式并发送一个字符串,那么它效率低下且速度慢。因此,最好只发送整个 time_t 并在服务器端对其进行排序。但是,我需要更多地了解类型,因为我不希望数字因系统之间的不同字节顺序而被破坏,所以我需要使用 htonl ...但首先,在需要知道的基础上,我想要找出底层类型;) 另一个“需要知道”的情况,至少对于有符号与无符号而言,是在减去时间时是否需要小心。如果您只是“减去并打印结果”,那么您可能会在带有签名 time_t 的系统上得到您期望的结果,但不会在未签名 time_t 上得到。 @MichaelFirth 整数有符号 time_t 和无符号 time_t 都存在原始减法将导致意外结果的情况。 C 为统一减法提供了double difftime(time_t time1, time_t time0)
。【参考方案10】:
您可以使用typeid
来了解time_t
在您的系统中是如何定义的。
#include <iostream> // cout
#include <ctime> // time_t
#include <typeinfo> // typeid, name
using namespace std;
int main()
cout << "Test 1: The type of time_t is: \t\t"
<< typeid(time_t).name() << endl;
cout << "Test 2: time_t is a signed long?: \t"
<< (typeid(time_t) == typeid(signed long) ? "true" : "false") << endl;
cout << "Test 3: time_t is an unsigned long?: \t"
<< (typeid(time_t) == typeid(unsigned long) ? "true" : "false") << endl;
return 0;
在我的系统中,输出是:
测试一:time_t的类型为:l 测试 2:time_t 是有符号的 long?:true 测试 3:time_t 是无符号长整数?:假【讨论】:
【参考方案11】:time_t
只是 typedef
8 个字节 (long long/__int64
),所有编译器和操作系统都可以理解。过去,它曾经只用于long int
(4 个字节),但现在不是。如果您查看crtdefs.h
中的time_t
,您会发现这两种实现方式,但操作系统将使用long long
。
【讨论】:
所有编译器和操作系统?不。在我的 linux 系统上,编译器采用 4 字节签名实现。 在 Zynq 7010 系统上 time_t 是 4 字节。 在我使用的嵌入式系统上,time_t 几乎总是 32 位或 4 字节。该标准明确指出,它是特定于实现的,这使得这个答案是错误的。以上是关于time_t 最终的 typedef 是啥?的主要内容,如果未能解决你的问题,请参考以下文章
typedef enum与typedef struct分别是啥意思