Antworten:
Der Artikelartikel von time_t Wikipedia beleuchtet dies. Das Fazit ist, dass der Typ time_t
in der C-Spezifikation nicht garantiert ist.
Der
time_t
Datentyp ist ein Datentyp in der ISO C-Bibliothek, der zum Speichern von Systemzeitwerten definiert ist. Solche Werte werden von der Standardbibliotheksfunktion zurückgegebentime()
. Dieser Typ ist ein typedef, der im Standardheader definiert ist. ISO C definiert time_t als einen arithmetischen Typ, gibt jedoch keinen bestimmten Typ , Bereich, Auflösung oder Codierung dafür an. Nicht spezifiziert sind auch die Bedeutungen von arithmetischen Operationen, die auf Zeitwerte angewendet werden.Unix- und POSIX-kompatible Systeme implementieren den
time_t
Typ alssigned integer
(normalerweise 32 oder 64 Bit breit), der die Anzahl der Sekunden seit Beginn der Unix-Epoche darstellt : Mitternacht UTC vom 1. Januar 1970 (ohne Schaltsekunden). Einige Systeme verarbeiten negative Zeitwerte korrekt, andere nicht. Systeme, die einen 32-Bit-time_t
Typ verwenden, sind für das Problem des Jahres 2038 anfällig .
time_t
in der Datenstruktur auf der Festplatte vorkommen. Da Dateisysteme jedoch häufig von anderen Betriebssystemen gelesen werden, wäre es dumm, das Dateisystem basierend auf solchen implementierungsabhängigen Typen zu definieren. Beispielsweise kann dasselbe Dateisystem sowohl auf 32-Bit- als auch auf 64-Bit-Systemen verwendet werden und time_t
die Größe ändern. Daher müssen Dateisysteme genauer definiert werden ("32-Bit-Ganzzahl mit Vorzeichen, die die Anzahl der Sekunden seit Anfang 1970 in UTC angibt") als nur als time_t
.
time.h
Inhaltsverzeichnis weiter. Dieser Artikel verlinkt auf cppreference.com, aber der zitierte Inhalt ist nirgends zu finden ...
time_t
Signatur garantiert, ist falsch. pubs.opengroup.org/onlinepubs/9699919799/basedefs/… schreibt vor, dass verschiedene Dinge ein "vorzeichenbehafteter Integer-Typ" oder ein "vorzeichenloser Integer-Typ" sein müssen, aber time_t
nur, dass es "ein Integer-Typ sein soll" . Eine Implementierung kann time_t
unsigniert und dennoch POSIX-konform sein.
[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;
Es ist definiert $INCDIR/bits/types.h
durch:
# 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
typedef __int32_t __time_t;
und typedef __time_t time_t;
in einem 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
. Ihre Ergebnisse werden unter Linux explizit so festgelegt (zumindest unter 2.6.32-5-xen-amd64 von Debian).
__time_t
und ihn nicht time_t
finden time_t
? Einen Schritt weglassen?
typedef __time_t time_t;
, eine Überprüfung des umgebenden Codes erforderlich ist, um sicherzustellen, dass typedef tatsächlich verwendet wurde und nicht nur Teil einer bedingten Kompilierung ist. typedef long time_t;
kann auch gefunden worden sein.
Standards
William Brendel zitierte Wikipedia, aber ich bevorzuge es aus dem Maul des Pferdes.
C99 N1256 Standardentwurf 7.23.1 / 3 "Komponenten der Zeit" sagt:
Die deklarierten Typen sind size_t (beschrieben in 7.17) clock_t und time_t, arithmetische Typen, die Zeiten darstellen können
und 6.2.5 / 18 "Typen" sagt:
Ganzzahlige und schwebende Typen werden zusammen als arithmetische Typen bezeichnet.
POSIX 7 sys_types.h sagt:
[CX] time_t soll ein ganzzahliger Typ sein.
wo [CX]
ist definiert als :
[CX] Erweiterung des ISO C-Standards.
Es ist eine Erweiterung, weil es eine stärkere Garantie bietet: Gleitkommazahlen sind aus.
gcc Einzeiler
Sie müssen keine Datei erstellen, wie von Quassnoi erwähnt :
echo | gcc -E -xc -include 'time.h' - | grep time_t
Unter Ubuntu 15.10 GCC 5.2 sind die beiden obersten Zeilen:
typedef long int __time_t;
typedef __time_t time_t;
Befehlsaufschlüsselung mit einigen Zitaten aus man gcc
:
-E
: "Beenden Sie nach der Vorverarbeitungsphase. Führen Sie den Compiler nicht ordnungsgemäß aus."-xc
: Geben Sie die Sprache C an, da die Eingabe von stdin stammt, das keine Dateierweiterung hat.-include file
: "Datei so verarbeiten, als ob" #include "file" "als erste Zeile der primären Quelldatei angezeigt würde."-
: Eingabe von stdingcc -E -xc -include time.h /dev/null | grep time_t
Die Antwort ist definitiv implementierungsspezifisch. Um dies endgültig für Ihre Plattform / Ihren Compiler herauszufinden, fügen Sie diese Ausgabe einfach irgendwo in Ihren Code ein:
printf ("sizeof time_t is: %d\n", sizeof(time_t));
Wenn die Antwort 4 (32 Bit) lautet und Ihre Daten über 2038 hinausgehen sollen , haben Sie 25 Jahre Zeit, um Ihren Code zu migrieren.
Ihre Daten sind in Ordnung, wenn Sie Ihre Daten als Zeichenfolge speichern, auch wenn es so einfach ist wie:
FILE *stream = [stream file pointer that you've opened correctly];
fprintf (stream, "%d\n", (int)time_t);
Dann lesen Sie es einfach auf die gleiche Weise zurück (fread, fscanf usw. in ein int), und Sie haben Ihre Epochenversatzzeit. Eine ähnliche Problemumgehung gibt es in .Net. Ich übergebe problemlos 64-Bit-Epochennummern zwischen Win- und Linux-Systemen (über einen Kommunikationskanal). Das wirft Probleme mit der Reihenfolge der Bytes auf, aber das ist ein anderes Thema.
Um die Frage von paxdiablo zu beantworten, würde ich sagen, dass "19100" gedruckt wurde, weil das Programm so geschrieben wurde (und ich gebe zu, dass ich dies in den 80ern selbst getan habe):
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);
Die printf
Anweisung gibt die feste Zeichenfolge "Year is: 19" aus, gefolgt von einer mit Nullen aufgefüllten Zeichenfolge mit den "Jahren seit 1900" (Definition von tm->tm_year
). Im Jahr 2000 beträgt dieser Wert offensichtlich 100. "%02d"
Pads mit zwei Nullen, werden jedoch nicht abgeschnitten, wenn sie länger als zwei Ziffern sind.
Der richtige Weg ist (nur zur letzten Zeile wechseln):
printf ("Year is: %d\n", local_date_time.tm_year + 1900);
Neue Frage: Was ist der Grund für dieses Denken?
%zu
Formatbezeichner verwenden, um size_t
Werte (wie von sizeof
) zu formatieren , da sie ohne Vorzeichen ( u
) sind und die Größe size_t ( z
) ·
printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));
und vermeiden Sie das z
Problem.
Unter Visual Studio 2008 wird standardmäßig ein verwendet, __int64
sofern Sie dies nicht definieren _USE_32BIT_TIME_T
. Sie tun besser so, als ob Sie nicht wissen, wie es definiert ist, da es sich von Plattform zu Plattform ändern kann (und wird).
time_t
ist vom Typ long int
auf 64-Bit-Computern, sonst ist es long long int
.
Sie können dies in diesen Header-Dateien überprüfen:
time.h
: /usr/include
types.h
und typesizes.h
:/usr/include/x86_64-linux-gnu/bits
(Die folgenden Anweisungen sind nicht nacheinander. Sie können mit Strg + F-Suche in der jeweiligen Header-Datei gefunden werden.)
1 in time.h
typedef __time_t time_t;
2) In types.h
# define __STD_TYPE typedef
__STD_TYPE __TIME_T_TYPE __time_t;
3) In 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) Wieder in 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;
long int
überall. Siehe stackoverflow.com/questions/384502/…
Auf den meisten älteren Plattformen handelt es sich um einen 32-Bit-Integer-Typ mit Vorzeichen. Dies führt jedoch dazu, dass Ihr Code unter dem Fehler des Jahres 2038 leidet . Moderne C-Bibliotheken sollten es daher stattdessen als signiertes 64-Bit-Int definieren, was für einige Milliarden Jahre sicher ist.
Normalerweise finden Sie diese zugrunde liegenden implementierungsspezifischen Typedefs für gcc im Verzeichnis bits
oder asm
header. Für mich ist es/usr/include/x86_64-linux-gnu/bits/types.h
.
Sie können einfach grep oder einen Präprozessoraufruf wie den von Quassnoi vorgeschlagenen verwenden, um zu sehen, welcher spezifische Header vorhanden ist.
Was ist letztendlich ein time_t typedef?
Robuster Code ist es egal, um welchen Typ es sich handelt.
C-Arten time_t
, um ein echter Typ wie double, long long, int64_t, int
usw. zu sein.
Es könnte sogar sein unsigned
, dass die Rückgabewerte von vielen Zeitfunktionen Fehler anzeigen -1
, aber nicht(time_t)(-1)
- Diese Implementierungsauswahl ist ungewöhnlich.
Der Punkt ist, dass das "Bedürfnis zu wissen" des Typs selten ist. Code sollte geschrieben werden, um die Notwendigkeit zu vermeiden.
Ein häufiges "Need-to-Know" tritt jedoch auf, wenn Code das Raw drucken möchte time_t
. Das Casting auf den breitesten Integer-Typ wird den meisten modernen Fällen gerecht.
time_t now = 0;
time(&now);
printf("%jd", (intmax_t) now);
// or
printf("%lld", (long long) now);
Gießen eines double
oder long double
wird auch funktionieren, doch konnte liefern ungenaue dezimal ausgegeben
printf("%.16e", (double) now);
double difftime(time_t time1, time_t time0)
für einen einheitlichen Subtraktionsansatz.
time_t
ist nur typedef
für 8 Bytes ( long long/__int64
), die alle Compiler und Betriebssysteme verstehen. Früher war es nur für long int
(4 Bytes), jetzt aber nicht mehr. Wenn Sie sich das time_t
in ansehen crtdefs.h
, werden Sie beide Implementierungen finden, aber das Betriebssystem wird es verwenden long long
.
long int
.