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-&gt;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/includetypes.htypesizes.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】:

通常,您会在 bitsasm 标头目录中找到这些底层实现特定的 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);

转换为doublelong 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 涉及 LPWSTR 是啥意思?

C++中typedef是啥意思啊

typedef是啥意思?

typedef enum与typedef struct分别是啥意思

“typedef __u16 __bitwise __le16;”是啥意思?在Linux内核中是啥意思?

typedef的基本用法