Wofür ist time_t letztendlich ein typedef?

195

Ich habe meine Linux-Box durchsucht und dieses typedef gesehen:

typedef __time_t time_t;

Aber ich konnte die __time_t Definition nicht finden .

kal
quelle

Antworten:

175

Der Artikelartikel von time_t Wikipedia beleuchtet dies. Das Fazit ist, dass der Typ time_tin der C-Spezifikation nicht garantiert ist.

Der time_tDatentyp ist ein Datentyp in der ISO C-Bibliothek, der zum Speichern von Systemzeitwerten definiert ist. Solche Werte werden von der Standardbibliotheksfunktion zurückgegeben time() . 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_tTyp als signed 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_tTyp verwenden, sind für das Problem des Jahres 2038 anfällig .

William Brendel
quelle
6
Beachten Sie jedoch, dass time_t-Werte normalerweise nur im Speicher und nicht auf der Festplatte gespeichert werden. Stattdessen wird time_t zur dauerhaften Speicherung in Text oder ein anderes portables Format konvertiert. Das macht das Y2038-Problem nicht wirklich zu einem Problem.
11
@Heath: Auf einem bestimmten System, auf dem dieselben Personen das Betriebssystem und die C-Bibliothek erstellen, kann die Verwendung time_tin 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_tdie 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.
1
Hinweis: Der verknüpfte Wikipedia-Artikel wurde entfernt und leitet jetzt zum time.hInhaltsverzeichnis weiter. Dieser Artikel verlinkt auf cppreference.com, aber der zitierte Inhalt ist nirgends zu finden ...
Michał Górny
3
@ MichałGórny: Behoben, solange Artikel nicht gelöscht werden, können Sie immer einen Blick in den Verlauf werfen, um die richtige Version zu finden.
Zeta
4
-1; Die von Wikipedia zitierte Behauptung, dass POSIX die time_tSignatur 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_tnur, dass es "ein Integer-Typ sein soll" . Eine Implementierung kann time_tunsigniert und dennoch POSIX-konform sein.
Mark Amery
112

[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.hdurch:

# 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
Quassnoi
quelle
1
Ich sehe beides 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).
ssice
1
@Viet auch mit einem Einzeiler möglich, ohne eine Datei zu erstellen: stackoverflow.com/a/36096104/895245
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功
1
Warum nach dem zugrunde liegenden Typ suchen __time_tund ihn nicht time_tfinden time_t? Einen Schritt weglassen?
chux
@ chux-ReinstateMonica - Das OP sagte, dass er das typedef von time_t bis __time_t gefunden habe. Diese Antwort befasst sich nur mit der Frage, wie __time_t definiert ist. Aber ich bin damit einverstanden, dass Sie für einen generischen Fall (in dem time_t möglicherweise nicht auf __time_t typisiert ist) zuerst nach time_t und dann möglicherweise erneut nach dem suchen müssen, was zurückgegeben wird
Michael Firth,
@ MichaelFirth Fair genug. Ich erinnere mich an meine Besorgnis, dass, obwohl OP festgestellt hat 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.
chux - Monica
30

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 stdin
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
1
Keine Notwendigkeit für die Pfeife von Echo auch:gcc -E -xc -include time.h /dev/null | grep time_t
Rvighne
12

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 printfAnweisung 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?

pwrgreg007
quelle
2
Sie sollten wahrscheinlich den %zuFormatbezeichner verwenden, um size_tWerte (wie von sizeof) zu formatieren , da sie ohne Vorzeichen ( u) sind und die Größe size_t ( z) ·
Adrian Günter haben
... oder verwenden printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));und vermeiden Sie das zProblem.
chux Stellen Sie Monica am
6

Unter Visual Studio 2008 wird standardmäßig ein verwendet, __int64sofern 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).

Finsternis
quelle
2
Das funktioniert normalerweise, aber wenn Ihr Programm die Dinge verfolgen soll, die in 30 Jahren passieren werden, ist es ziemlich wichtig, dass Sie kein signiertes 32-Bit-time_t haben.
Rob Kennedy
4
@ Rob, bah, lass es! Wir werden erst 2036 anfangen, wie kopflose Hühner herumzulaufen, genau wie im Jahr 2000. Einige von uns werden eine Menge Geld damit verdienen, Berater im Jahr 2000 zu sein. Leonard Nimoy wird ein weiteres lustiges Buch darüber herausbringen, wie wir uns alle im Wald verstecken sollten ...
paxdiablo
1
... und alles wird vorbei sein, die Öffentlichkeit fragt sich, worum es in der ganzen Aufregung ging. Ich könnte sogar aus dem Ruhestand kommen, um etwas Geld für das Erbe der Kinder zu verdienen :-).
Paxdiablo
2
Übrigens, wir haben nur einen Y2K-Fehler gefunden und das war eine Webseite, auf der das Datum als 1. Januar 19100 aufgeführt war. Übung für den Leser, warum ...
paxdiablo
9
Wenn das Ereignis in 30 Jahren "Diese Sicherung ablaufen lassen" lautet, sind Sie möglicherweise JETZT in Schwierigkeiten, nicht in 2038. Fügen Sie der heutigen 32-Bit-Zeit 30 Jahre 30 Jahre hinzu, und Sie erhalten ein Datum in der Vergangenheit. Ihr Programm sucht nach zu verarbeitenden Ereignissen, findet ein überfälliges Ereignis (um 100 Jahre!) Und führt es aus. Ups, kein Backup mehr.
Rob Kennedy
5

time_tist vom Typ long intauf 64-Bit-Computern, sonst ist es long long int.

Sie können dies in diesen Header-Dateien überprüfen:

time.h: /usr/include
types.hund 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;
abcoep
quelle
Diese Dateien werden von glibc unter Ubuntu 15.10 BTW bereitgestellt.
Ciro Santilli 法轮功 冠状 病 六四 事件 19
3
Es ist nicht long intüberall. Siehe stackoverflow.com/questions/384502/…
Zan Lynx
4

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.


quelle
3

Normalerweise finden Sie diese zugrunde liegenden implementierungsspezifischen Typedefs für gcc im Verzeichnis bitsoder asmheader. 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.

Poolie
quelle
1

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, intusw. 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 doubleoder long doublewird auch funktionieren, doch konnte liefern ungenaue dezimal ausgegeben

printf("%.16e", (double) now);
chux - Monica wieder einsetzen
quelle
Ich muss die Situation kennen, weil ich Zeit von einem ARM-System auf ein AMD64-System übertragen muss. time_t ist 32 Bit am Arm und 64 Bit am Server. Wenn ich die Zeit in ein Format übersetze und eine Zeichenfolge sende, ist dies ineffizient und langsam. Daher ist es viel besser, einfach das gesamte time_t zu senden und es auf der Serverseite zu sortieren. Allerdings muss ich den Typ ein bisschen besser verstehen, weil ich nicht möchte, dass die Zahl durch unterschiedliche Endianness zwischen den Systemen verstümmelt wird, also muss ich htonl verwenden ... aber zuerst möchte ich es wissen um den zugrunde liegenden Typ herauszufinden;)
Owl
Ein weiterer "Need to Know" -Fall, zumindest für signierte und nicht signierte, ist, ob Sie beim Subtrahieren von Zeiten vorsichtig sein müssen. Wenn Sie nur "das Ergebnis subtrahieren und drucken", erhalten Sie möglicherweise das, was Sie auf einem System mit einer vorzeichenbehafteten Zeit_t erwarten, jedoch nicht mit einer vorzeichenlosen Zeit_t.
Michael Firth
@ MichaelFirth Es gibt Fälle sowohl für die ganzzahlige vorzeichenbehaftete Zeit_t als auch für die vorzeichenlose Zeit_t, bei denen die rohe Subtraktion zu unerwarteten Ergebnissen führt. C sorgt double difftime(time_t time1, time_t time0)für einen einheitlichen Subtraktionsansatz.
chux - Monica
-3

time_tist nur typedeffü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_tin ansehen crtdefs.h, werden Sie beide Implementierungen finden, aber das Betriebssystem wird es verwenden long long.

bvrwoo_3376
quelle
5
alle Compiler und Betriebssysteme? Nein. Auf meinem Linux-System übernimmt der Compiler die mit 4 Bytes signierte Implementierung.
Vincent
Auf Zynq 7010-Systemen beträgt time_t 4 Byte.
Eule
1
Auf den eingebetteten Systemen, an denen ich arbeite, beträgt time_t fast immer 32 Bit oder 4 Bytes. Der Standard besagt ausdrücklich, dass es implementierungsspezifisch ist, was diese Antwort einfach falsch macht.
Cobusve