Cos'è time_t alla fine un typedef?


Risposte:


175

L' articolo di Time_t su Wikipedia fa luce su questo. La linea di fondo è che il tipo di time_tnon è garantito nella specifica C.

Il time_ttipo di dati è un tipo di dati nella libreria ISO C definito per la memorizzazione dei valori di ora del sistema. Tali valori vengono restituiti dalla time() funzione di libreria standard . Questo tipo è un typedef definito nell'intestazione standard. ISO C definisce time_t come un tipo aritmetico, ma non specifica alcun tipo particolare , intervallo, risoluzione o codifica per esso. Inoltre non sono specificati i significati delle operazioni aritmetiche applicate ai valori temporali.

I sistemi compatibili con Unix e POSIX implementano il time_ttipo come signed integer(tipicamente largo 32 o 64 bit) che rappresenta il numero di secondi dall'inizio dell'epoca Unix : mezzanotte UTC del 1 ° gennaio 1970 (senza contare i secondi bisestili). Alcuni sistemi gestiscono correttamente valori di tempo negativi, mentre altri no. I sistemi che utilizzano un tipo a 32 bit time_tsono suscettibili al problema dell'anno 2038 .


6
Si noti, tuttavia, che i valori time_t sono generalmente memorizzati solo in memoria, non su disco. Invece, time_t viene convertito in testo o in qualche altro formato portatile per l'archiviazione persistente. Ciò rende il problema Y2038 non proprio un problema.

11
@Heath: su un sistema specifico, in cui le stesse persone creano il sistema operativo e la libreria C, time_tpuò avvenire l' utilizzo nella struttura dei dati su disco. Tuttavia, poiché i filesystem vengono spesso letti da altri sistemi operativi, sarebbe sciocco definire il filesystem in base a tali tipi dipendenti dall'implementazione. Ad esempio, lo stesso filesystem potrebbe essere utilizzato su entrambi i sistemi a 32 e 64 bit e time_tpotrebbe cambiare dimensione. Pertanto, i filesystem devono essere definiti in modo più preciso ("numero intero con segno a 32 bit che fornisce il numero di secondi dall'inizio del 1970, in UTC") rispetto a quello time_t.

1
Come nota: l'articolo di Wikipedia collegato è stato rimosso e ora reindirizza all'elenco dei time.hcontenuti. Quell'articolo si collega a cppreference.com ma il contenuto citato non si trova da nessuna parte ...
Michał Górny

3
@ MichałGórny: risolto, finché gli articoli non vengono eliminati puoi sempre dare un'occhiata alla cronologia per trovare la versione corretta.
Zeta,

4
-1; l'affermazione citata da Wikipedia secondo cui POSIX garantisce la time_tfirma è errata. pubs.opengroup.org/onlinepubs/9699919799/basedefs/… impone che varie cose debbano essere un "tipo intero con segno" o "tipo intero senza segno", ma time_tne dice semplicemente che "deve essere un tipo intero" . Un'implementazione può rendere time_tnon firmata ed essere ancora conforme a POSIX.
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;

È definito $INCDIR/bits/types.hattraverso:

# 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

1
Vedo entrambi typedef __int32_t __time_t;e typedef __time_t time_t;in a 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. I tuoi risultati sono esplicitamente impostati così in Linux (almeno su 2.6.32-5-xen-amd64 di Debian).
ssice,

1
@Viet anche possibile con un one-liner senza creare un file: stackoverflow.com/a/36096104/895245
Ciro Santilli郝海东冠状病六四事件法轮功

1
Perché grep per __time_te non time_ttrovare il tipo sottostante di time_t? Omettere un passo?
chux - Ripristina Monica il

@ chux-ReinstateMonica - L'OP ha dichiarato di aver trovato il typedef da time_t a __time_t. Questa risposta si rivolge solo alla domanda posta su cosa sia definito __time_t. Ma sono d'accordo che per un caso generico (in cui time_t potrebbe non essere digitato in __time_t), dovresti prima fare il grep per time_t, e quindi eventualmente di nuovo grep per ciò che ritorna
Michael Firth,

@MichaelFirth Abbastanza giusto. Ricordo la mia preoccupazione come anche se OP fosse stato scoperto typedef __time_t time_t;, anche l'esame del codice circostante era necessario per assicurare che typedef fosse effettivamente utilizzato e non solo parte di una compilazione condizionale. typedef long time_t;potrebbe essere stato trovato anche.
chux - Ripristina Monica

30

Standards

William Brendel ha citato Wikipedia, ma lo preferisco dalla bocca del cavallo.

Bozza standard C99 N1256 7.23.1 / 3 "Componenti del tempo" dice:

I tipi dichiarati sono size_t (descritti in 7.17) clock_t e time_t che sono tipi aritmetici in grado di rappresentare i tempi

e 6.2.5 / 18 "Tipi" dice:

I tipi interi e mobili sono chiamati collettivamente tipi aritmetici.

POSIX 7 sys_types.h dice:

[CX] time_t deve essere un tipo intero.

dove [CX]è definito come :

[CX] Estensione allo standard ISO C.

È un'estensione perché fornisce una garanzia più forte: i punti mobili sono fuori.

gcc one-liner

Non è necessario creare un file come indicato da Quassnoi :

echo | gcc -E -xc -include 'time.h' - | grep time_t

Su Ubuntu 15.10 GCC 5.2 le prime due linee sono:

typedef long int __time_t;
typedef __time_t time_t;

Ripartizione dei comandi con alcune citazioni da man gcc:

  • -E: "Stop dopo la fase di preelaborazione; non eseguire correttamente il compilatore."
  • -xc: Specifica il linguaggio C, poiché l'input proviene da stdin che non ha estensione file.
  • -include file: "File di processo come se" #include "file" "apparisse come prima riga del file di origine principale."
  • -: input da stdin

1
Non è necessario nemmeno il tubo dell'eco:gcc -E -xc -include time.h /dev/null | grep time_t
rvighne,

12

La risposta è sicuramente specifica per l'implementazione. Per scoprirlo definitivamente per la tua piattaforma / compilatore, aggiungi questo output da qualche parte nel tuo codice:

printf ("sizeof time_t is: %d\n", sizeof(time_t));

Se la risposta è 4 (32 bit) e i tuoi dati dovrebbero andare oltre il 2038 , hai 25 anni per migrare il tuo codice.

I tuoi dati andranno bene se li memorizzi come una stringa, anche se è qualcosa di semplice come:

FILE *stream = [stream file pointer that you've opened correctly];
fprintf (stream, "%d\n", (int)time_t);

Quindi rileggilo nello stesso modo (fread, fscanf, ecc. In un int) e avrai il tuo tempo di offset dell'epoca. Una soluzione simile esiste in .Net. Passo i numeri di epoca a 64 bit tra i sistemi Win e Linux senza alcun problema (su un canale di comunicazione). Ciò solleva problemi di ordinamento dei byte, ma questo è un altro argomento.

Per rispondere alla domanda di paxdiablo, direi che ha stampato "19100" perché il programma è stato scritto in questo modo (e ammetto di averlo fatto da solo negli anni '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);

L' printfistruzione stampa la stringa fissa "L'anno è: 19" seguita da una stringa con spaziatura zero con gli "anni dal 1900" (definizione di tm->tm_year). Nel 2000, quel valore è 100, ovviamente. "%02d"pad con due zeri ma non si tronca se più di due cifre.

Il modo corretto è (cambia solo all'ultima riga):

printf ("Year is: %d\n", local_date_time.tm_year + 1900);

Nuova domanda: qual è la logica di questo pensiero?


2
Probabilmente dovresti usare l' %zuidentificatore di formato per formattare i size_tvalori (come resi da sizeof), poiché sono unsigned ( u) e di lunghezza size_t ( z) ·
Adrian Günter

... oppure usa printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));ed evita il zproblema.
chux - Ripristina Monica il

6

In Visual Studio 2008, per impostazione predefinita è un a __int64meno che non venga definito _USE_32BIT_TIME_T. Stai meglio solo fingendo di non sapere come è definito, dal momento che può (e cambierà) da una piattaforma all'altra.


2
Di solito funziona, ma se il tuo programma ha lo scopo di tenere traccia delle cose che accadranno tra 30 anni, è piuttosto importante che tu non abbia un time_t a 32 bit firmato.
Rob Kennedy,

4
@Rob, bah, lascialo! Inizieremo a correre come polli senza testa nel 2036, come abbiamo fatto per Y2K. Alcuni di noi faranno un sacco di soldi da consulenti Y2k38, Leonard Nimoy pubblicherà un altro libro divertente su come dovremmo andare tutti a nasconderci nella foresta ...
paxdiablo,

1
... e tutto esploderà, il pubblico si chiederà di cosa si trattasse. Potrei anche uscire dalla pensione per guadagnare un po 'di soldi per l'eredità dei bambini :-).
paxdiablo,

2
A proposito, abbiamo trovato solo un bug Y2K e quella era una pagina web che elencava la data come 1 gennaio 19100. Esercizio per il lettore sul perché ...
paxdiablo,

9
Se l'evento che si verifica tra 30 anni "scade questo backup", allora potresti essere nei guai ADESSO, non nel 2038. Aggiungi 30 anni al time_t a 32 bit di oggi e ottieni una data in passato. Il tuo programma cerca gli eventi da elaborare, ne trova uno in ritardo (di 100 anni!) E lo esegue. Oops, non più backup.
Rob Kennedy,

5

time_tè di tipo long intsu macchine a 64 bit, altrimenti lo è long long int.

Puoi verificarlo in questi file di intestazione:

time.h: /usr/include
types.he typesizes.h:/usr/include/x86_64-linux-gnu/bits

(Le seguenti istruzioni non sono una dopo l'altra. Potrebbero essere trovate nel rispettivo file di intestazione usando Ctrl + f search.)

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) Ancora una volta 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;

Questi file sono forniti da glibc su Ubuntu 15.10 BTW.
Ciro Santilli 19 冠状 病 六四 事件 法轮功

3
Non è long intdappertutto. Vedi stackoverflow.com/questions/384502/…
Zan Lynx,

4

È un tipo intero con segno a 32 bit sulla maggior parte delle piattaforme legacy. Tuttavia, ciò causa al tuo codice il bug dell'anno 2038 . Quindi le librerie C moderne dovrebbero definirlo invece un int a 64 bit con segno, che è sicuro per alcuni miliardi di anni.


3

In genere troverai questi typedef specifici per l'implementazione sottostanti per gcc nella directory dell'intestazione bitso asm. Per me lo è /usr/include/x86_64-linux-gnu/bits/types.h.

Puoi semplicemente grep o utilizzare una chiamata al preprocessore come quella suggerita da Quassnoi per vedere quale intestazione specifica.


1

In cosa consiste in definitiva un typedef time_t?

Il codice robusto non importa quale sia il tipo.

Le specie C time_tdevono essere un tipo reale come double, long long, int64_t, int, ecc.

Potrebbe anche essere unsignedche i valori restituiti da molte funzioni temporali indicano che l'errore non lo è -1, ma (time_t)(-1)- Questa scelta di implementazione non è comune.

Il punto è che il "bisogno di sapere" il tipo è raro. Il codice deve essere scritto per evitare la necessità.


Tuttavia si verifica un "bisogno di sapere" comune quando il codice vuole stampare il raw time_t. La trasmissione al tipo intero più largo si adatta alla maggior parte dei casi moderni.

time_t now = 0;
time(&now);
printf("%jd", (intmax_t) now);
// or 
printf("%lld", (long long) now);

Anche il cast su un doubleo long doublefunzionerà, ma potrebbe fornire un risultato decimale inesatto

printf("%.16e", (double) now);

Ho bisogno di sapere la situazione perché devo trasferire il tempo da un sistema ARM a un sistema AMD64. time_t è 32 bit sul braccio e 64 bit sul server. se traduco il tempo in un formato e invio una stringa, è inefficiente e lento. Pertanto è molto meglio inviare l'intero time_t e ordinarlo sul lato server. Tuttavia, devo capire un po 'di più il tipo perché non voglio che il numero venga alterato dalla diversa endianness tra i sistemi, quindi ho bisogno di usare htonl ... ma prima, sulla base di una necessità di conoscere, voglio per scoprire il tipo sottostante;)
Gufo

Un altro caso "necessario sapere", almeno per quanto riguarda i segni firmati e quelli non firmati, è se è necessario prestare attenzione durante la sottrazione dei tempi. Se semplicemente "sottrai e stampi il risultato", otterrai probabilmente quello che ti aspetti su un sistema con un time_t con segno, ma non con un time_t senza segno.
Michael Firth,

@MichaelFirth Esistono casi sia per time_t con segno intero che per time_t senza segno in cui la sottrazione non elaborata comporterà risultati imprevisti. C prevede double difftime(time_t time1, time_t time0)un approccio di sottrazione uniforme.
chux - Ripristina Monica

-3

time_tè solo typedefper 8 byte ( long long/__int64) che tutti i compilatori e i sistemi operativi comprendono. Ai tempi, era solo per long int(4 byte) ma non ora. Se guardi time_tdentro crtdefs.htroverai entrambe le implementazioni ma il sistema operativo userà long long.


5
tutti i compilatori e sistemi operativi? No. Sul mio sistema Linux, il compilatore accetta l'implementazione firmata con 4 byte.
Vincent,

Sui sistemi Zynq 7010 time_t è 4 byte.
Gufo

1
Sui sistemi embedded che lavoro su time_t è quasi sempre a 32 bit o 4 byte. La norma afferma specificamente che è specifica dell'implementazione che rende questa risposta sbagliata.
Cobusve,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.