Quel est finalement time_t un typedef?


Réponses:


175

L' article de Time_t sur Wikipédia apporte un éclairage à ce sujet. L'essentiel est que le type de time_tn'est pas garanti dans la spécification C.

Le time_ttype de données est un type de données dans la bibliothèque ISO C défini pour stocker les valeurs d'heure système. Ces valeurs sont renvoyées par la time() fonction de bibliothèque standard . Ce type est un typedef défini dans l'en-tête standard. ISO C définit time_t comme un type arithmétique, mais ne spécifie aucun type , plage, résolution ou encodage particulier pour lui. La signification des opérations arithmétiques appliquées aux valeurs temporelles n'est pas non plus spécifiée.

Les systèmes compatibles Unix et POSIX implémentent le time_ttype en tant que signed integer(généralement de 32 ou 64 bits de large) qui représente le nombre de secondes depuis le début de l'époque Unix : minuit UTC du 1er janvier 1970 (sans compter les secondes intercalaires). Certains systèmes gèrent correctement les valeurs de temps négatives, tandis que d'autres ne le font pas. Les systèmes utilisant un type 32 bits time_tsont sensibles au problème de l' an 2038 .


6
Notez, cependant, que les valeurs de time_t ne sont généralement stockées qu'en mémoire, pas sur disque. Au lieu de cela, time_t est converti en texte ou en un autre format portable pour un stockage persistant. Cela fait que le problème Y2038 n'est pas vraiment un problème.

11
@Heath: sur un système spécifique, où les mêmes personnes créent le système d'exploitation et la bibliothèque C, l'utilisation time_tdans la structure de données sur disque peut se produire. Cependant, comme les systèmes de fichiers sont souvent lus par d'autres systèmes d'exploitation, il serait idiot de définir le système de fichiers en fonction de ces types dépendants de l'implémentation. Par exemple, le même système de fichiers peut être utilisé à la fois sur des systèmes 32 bits et 64 bits et time_tpeut changer de taille. Ainsi, les systèmes de fichiers doivent être définis plus précisément ("Entier signé 32 bits donnant le nombre de secondes depuis le début de 1970, en UTC") que tout comme time_t.

1
Remarque: l'article Wikipedia lié a été supprimé et il redirige désormais vers la liste des time.hcontenus. Cet article renvoie à cppreference.com mais le contenu cité est introuvable…
Michał Górny

3
@ MichałGórny: Correction, tant que les articles ne sont pas supprimés, vous pouvez toujours consulter l'historique afin de trouver la bonne version.
Zeta

4
-1; l'affirmation citée sur Wikipedia selon laquelle POSIX garantit la time_tsignature est incorrecte. pubs.opengroup.org/onlinepubs/9699919799/basedefs/… dicte que diverses choses doivent être un "type entier signé" ou "type entier non signé", mais time_tcela dit simplement qu'il "doit être un type entier" . Une implémentation peut être time_tnon signée et toujours conforme à POSIX.
Mark Amery du

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;

Il est défini $INCDIR/bits/types.hpar:

# 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
Je vois les deux typedef __int32_t __time_t;et typedef __time_t time_t;dans un 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. Vos résultats sont explicitement définis comme cela sous Linux (au moins sur 2.6.32-5-xen-amd64 de Debian).
ssice

1
@Viet également possible avec une ligne sans créer de fichier: stackoverflow.com/a/36096104/895245
Ciro Santilli 郝海东 郝海东 病 六四 事件 法轮功

1
Pourquoi chercher __time_tet ne time_tpas trouver le type sous-jacent de time_t? Omettre une étape?
chux

@ chux-ReinstateMonica - L'OP a dit qu'il avait trouvé le typedef de time_t à __time_t. Cette réponse ne fait que répondre à la question posée sur la définition de __time_t. Mais je suis d'accord que pour un cas générique (où time_t ne peut pas être tapé à __time_t), vous devrez d'abord chercher gre time_t, puis éventuellement encore grep pour ce que cela renvoie
Michael Firth

@MichaelFirth Assez juste. Je me souviens de ma préoccupation car même si OP l'a trouvé typedef __time_t time_t;, l'examen du code environnant est également nécessaire pour s'assurer que typedef a bien été utilisé et pas seulement une partie d'une compilation conditionnelle. typedef long time_t;peut avoir été trouvé aussi.
chux

30

Normes

William Brendel a cité Wikipedia, mais je le préfère de la bouche du cheval.

Le projet de norme C99 N1256 7.23.1 / 3 "Composantes du temps" dit:

Les types déclarés sont size_t (décrit en 7.17) clock_t et time_t qui sont des types arithmétiques capables de représenter des temps

et 6.2.5 / 18 "Types" dit:

Les types entiers et flottants sont appelés collectivement types arithmétiques.

POSIX 7 sys_types.h dit:

[CX] time_t doit être un type entier.

[CX]est défini comme :

[CX] Extension à la norme ISO C.

C'est une extension car elle apporte une garantie plus forte: les virgules flottantes sont sorties.

gcc one-liner

Pas besoin de créer un fichier comme mentionné par Quassnoi :

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

Sur Ubuntu 15.10 GCC 5.2, les deux premières lignes sont:

typedef long int __time_t;
typedef __time_t time_t;

Répartition des commandes avec quelques citations de man gcc:

  • -E: "Arrêtez après l'étape de prétraitement; n'exécutez pas le compilateur correctement."
  • -xc: Spécifiez le langage C, car l'entrée provient de stdin qui n'a pas d'extension de fichier.
  • -include file: "Traiter le fichier comme si" #include "file" "apparaissait comme la première ligne du fichier source principal."
  • -: entrée de stdin

1
Pas besoin non plus de la pipe d'echo:gcc -E -xc -include time.h /dev/null | grep time_t
rvighne

12

La réponse est définitivement spécifique à l'implémentation. Pour connaître définitivement votre plateforme / compilateur, ajoutez simplement cette sortie quelque part dans votre code:

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

Si la réponse est 4 (32 bits) et que vos données doivent dépasser 2038 , vous disposez de 25 ans pour migrer votre code.

Vos données seront correctes si vous stockez vos données sous forme de chaîne, même si c'est quelque chose de simple comme:

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

Ensuite, lisez-le de la même manière (fread, fscanf, etc. dans un int), et vous avez votre temps de décalage d'époque. Une solution de contournement similaire existe dans .Net. Je passe des numéros d'époque 64 bits entre les systèmes Win et Linux sans problème (sur un canal de communication). Cela soulève des problèmes d'ordre des octets, mais c'est un autre sujet.

Pour répondre à la requête de paxdiablo, je dirais qu'il a imprimé "19100" parce que le programme a été écrit de cette façon (et j'avoue que je l'ai fait moi-même dans les années 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' printfinstruction imprime la chaîne fixe "Year is: 19" suivie d'une chaîne à zéro avec les "années depuis 1900" (définition de tm->tm_year). En 2000, cette valeur est 100, évidemment. "%02d"tampons avec deux zéros mais ne tronquent pas si plus de deux chiffres.

La façon correcte est (passer à la dernière ligne uniquement):

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

Nouvelle question: quelle est la justification de cette réflexion?


2
Vous devriez probablement utiliser le %zuspécificateur de format pour formater les size_tvaleurs (telles que fournies par sizeof), car elles sont non signées ( u) et de longueur size_t ( z) ·
Adrian Günter

... ou utilisez printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));et évitez le zproblème.
chux

6

Sous Visual Studio 2008, il est par défaut un __int64sauf si vous le définissez _USE_32BIT_TIME_T. Vous feriez mieux de prétendre simplement que vous ne savez pas ce qu'elle est définie, car elle peut (et va) changer de plateforme en plateforme.


2
Cela fonctionne généralement, mais si votre programme est destiné à garder une trace des événements qui se produiront dans 30 ans, il est assez important que vous n'ayez pas de time_t 32 bits signé.
Rob Kennedy

4
@Rob, bah, laisse-le! Nous allons commencer à courir comme des poulets sans tête en 2036, comme nous l'avons fait pour Y2K. Certains d'entre nous gagneront beaucoup d'argent en étant des consultants Y2k38, Leonard Nimoy sortira un autre livre hilarant sur la façon dont nous devrions tous aller nous cacher dans la forêt ...
paxdiablo

1
... et tout va exploser, le public se demandant de quoi il s'agit. Je peux même sortir de la retraite pour gagner de l'argent pour l'héritage des enfants :-).
paxdiablo

2
BTW, nous n'avons trouvé qu'un seul bug Y2K et c'était une page Web qui indiquait la date au 1er janvier 19100. Exercice pour le lecteur pour savoir pourquoi ...
paxdiablo

9
Si l'événement qui se produira dans 30 ans est "expirer cette sauvegarde", alors vous pourriez avoir des problèmes MAINTENANT, pas en 2038. Ajoutez 30 ans au time_t 32 bits d'aujourd'hui, et vous obtenez une date dans le passé. Votre programme recherche les événements à traiter, en trouve un qui est en retard (de 100 ans!) Et l'exécute. Oups, plus de sauvegarde.
Rob Kennedy

5

time_test de type long intsur les machines 64 bits, sinon il l'est long long int.

Vous pouvez le vérifier dans ces fichiers d'en-tête:

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

(Les instructions ci-dessous ne sont pas les unes après les autres. Elles peuvent être trouvées dans le fichier d'en-tête resp. En utilisant la recherche Ctrl + f.)

1) Dans time.h

typedef __time_t time_t;

2) Dans types.h

# define __STD_TYPE     typedef  
__STD_TYPE __TIME_T_TYPE __time_t;  

3) Dans 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) Encore une fois 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;

Ces fichiers sont fournis par glibc sur Ubuntu 15.10 BTW.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3
Ce n'est pas long intpartout. Voir stackoverflow.com/questions/384502/…
Zan Lynx

4

Il s'agit d'un type entier signé 32 bits sur la plupart des plates-formes héritées. Cependant, cela fait souffrir votre code du bogue de l' année 2038 . Ainsi, les bibliothèques C modernes devraient plutôt le définir comme un int 64 bits signé, ce qui est sûr pendant quelques milliards d'années.



1

À quoi sert finalement un typedef time_t?

Le code robuste ne se soucie pas du type.

Espèce C time_tpour être un vrai type commedouble, long long, int64_t, int , etc.

Cela pourrait même être unsignedcomme les valeurs de retour de nombreuses fonctions de temps indiquant que l'erreur ne l'est pas -1, mais(time_t)(-1) - Ce choix d'implémentation est rare.

Le fait est que le "besoin de connaître" le type est rare. Le code doit être écrit pour éviter le besoin.


Pourtant, un «besoin de savoir» commun se produit lorsque le code veut imprimer le brut time_t. La conversion vers le type entier le plus large s'adaptera à la plupart des cas modernes.

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

La conversion vers un doubleou long doublefonctionnera également, mais pourrait fournir une sortie décimale inexacte

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

J'ai besoin de connaître la situation car j'ai besoin de transférer du temps d'un système ARM vers un système AMD64. time_t représente 32 bits sur le bras et 64 bits sur le serveur. si je traduis l'heure dans un format et envoie une chaîne, c'est inefficace et lent. Par conséquent, il est préférable d'envoyer simplement le time_t entier et de le trier du côté serveur. Cependant, je dois comprendre un peu plus le type parce que je ne veux pas que le nombre soit altéré par une endianité différente entre les systèmes, donc j'ai besoin d'utiliser htonl ... mais d'abord, sur une base de besoin de savoir, je veux pour découvrir le type sous-jacent;)
Chouette

Un autre cas de «besoin de savoir», du moins pour les signés et les non signés, est de savoir si vous devez faire attention lors de la soustraction des temps. Si vous "soustrayez et imprimez le résultat", vous obtiendrez probablement ce que vous attendez sur un système avec un time_t signé, mais pas avec un time_t non signé.
Michael Firth

@MichaelFirth Il existe des cas pour time_t entier signé et time_t non signé où la soustraction brute entraînera des résultats inattendus. C prévoit double difftime(time_t time1, time_t time0)une approche de soustraction uniforme.
chux

-3

time_test juste typedefpour 8 octets ( long long/__int64) que tous les compilateurs et les systèmes d'exploitation comprennent. À l'époque, c'était juste pour long int(4 octets) mais pas maintenant. Si vous regardez time_tdans, crtdefs.hvous trouverez les deux implémentations mais le système d'exploitation les utilisera long long.


5
tous les compilateurs et OS? Non. Sur mon système Linux, le compilateur prend l'implémentation signée de 4 octets.
Vincent

Sur les systèmes Zynq 7010, time_t est de 4 octets.
Owl

1
Sur les systèmes embarqués, je travaille sur time_t soit presque toujours 32 bits ou 4 octets. La norme indique spécifiquement qu'elle est spécifique à l'implémentation, ce qui rend cette réponse tout simplement fausse.
Cobusve
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.