Co to jest time_t ostatecznie typedef?

195

Przeszukałem mój system Linux i zobaczyłem następujący typedef:

typedef __time_t time_t;

Ale nie mogłem znaleźć __time_t definicji.

kal
źródło
GNU C rozważ tolong int .
haccks 27.07.17

Odpowiedzi:

175

Artykuł w Wikipedii time_t rzuca na to trochę światła. Najważniejsze jest to, że typ time_tnie jest gwarantowany w specyfikacji C.

Typ time_tdanych to typ danych w bibliotece ISO C zdefiniowany do przechowywania wartości czasu systemowego. Takie wartości są zwracane ze standardowej time() funkcji biblioteki. Ten typ jest typedef zdefiniowanym w standardowym nagłówku. ISO C definiuje time_t jako typ arytmetyczny, ale nie określa żadnego konkretnego typu , zakresu, rozdzielczości ani kodowania dla niego. Nieokreślone są również znaczenia operacji arytmetycznych zastosowanych do wartości czasu.

Systemy zgodne z Unix i POSIX implementują ten time_ttyp jako signed integer(zwykle o szerokości 32 lub 64 bitów), który reprezentuje liczbę sekund od początku epoki Uniksa : północ UTC 1 stycznia 1970 r. (Nie licząc sekund przestępnych). Niektóre systemy poprawnie obsługują ujemne wartości czasu, podczas gdy inne nie. Systemy wykorzystujące time_ttyp 32-bitowy są podatne na problem z rokiem 2038 .

William Brendel
źródło
6
Należy jednak pamiętać, że wartości time_t są zwykle przechowywane tylko w pamięci, a nie na dysku. Zamiast tego time_t jest konwertowany na tekst lub inny przenośny format dla trwałego przechowywania. To sprawia, że ​​problem Y2038 nie jest tak naprawdę problemem.
11
@Heath: w określonym systemie, w którym te same osoby tworzą system operacyjny i bibliotekę C, time_tmoże się zdarzyć przy użyciu struktury danych na dysku. Ponieważ jednak systemy plików są często odczytywane przez inne systemy operacyjne, głupio byłoby zdefiniować system plików na podstawie typów zależnych od implementacji. Na przykład ten sam system plików może być używany zarówno w systemach 32-bitowych, jak i 64-bitowych i time_tmoże zmieniać rozmiar. Dlatego systemy plików muszą być zdefiniowane bardziej precyzyjnie („32-bitowa liczba całkowita ze znakiem, dająca liczbę sekund od początku 1970 r. W UTC”), niż tylko tak time_t.
1
Uwaga: połączony artykuł z Wikipedii został usunięty, a teraz przekierowuje do listy time.htreści. Ten artykuł prowadzi do strony cppreference.com, ale nigdzie nie można znaleźć cytowanej treści…
Michał Górny,
3
@ MichałGórny: Naprawiono, dopóki artykuły nie są usuwane, zawsze możesz zajrzeć do historii, aby znaleźć właściwą wersję.
Zeta,
4
-1; cytowane z Wikipedii twierdzenie, że POSIX gwarantuje, że time_tjest podpisane, jest nieprawidłowe. pubs.opengroup.org/onlinepubs/9699919799/basedefs/… dyktuje, że różne rzeczy muszą być „typem całkowitym ze znakiem” lub „typem całkowitym bez znaku”, ale z time_ttego wynika jedynie, że „powinien być typem całkowitym” . Implementacja może sprawić, że będzie time_tbez znaku i nadal będzie zgodna z 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;

Jest to zdefiniowane $INCDIR/bits/types.hpoprzez:

# 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
źródło
1
Widzę jak typedef __int32_t __time_t;i typedef __time_t time_t;w sposób 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. Twoje wyniki są jawnie ustawione w ten sposób w Linuksie (przynajmniej na 2.6.32-5-xen-amd64 z Debiana).
ssice
1
@Viet jest również możliwe w przypadku jednej linijki bez tworzenia pliku: stackoverflow.com/a/36096104/895245
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功
1
Po co grepować __time_ti nie time_tznajdować podstawowego typu time_t? Pomijasz krok?
chux - Przywróć Monikę
@ chux-ReinstateMonica - OP powiedział, że znalazł typedef od time_t do __time_t. Ta odpowiedź dotyczy tylko zadanego pytania o to, jak zdefiniowano __time_t. Ale zgadzam się, że w ogólnym przypadku (gdzie time_t nie może być wpisane jako __time_t), najpierw musisz grep dla time_t, a następnie prawdopodobnie grep dla tego, co zwraca
Michael Firth
@MichaelFirth Fair wystarczy. Przypominam sobie moją troskę, ponieważ chociaż OP znalazł typedef __time_t time_t;, badanie otaczającego kodu jest również potrzebne, aby upewnić się, że typedef faktycznie został użyty, a nie tylko część kompilacji warunkowej. typedef long time_t;też mógł zostać znaleziony.
chux - Przywróć Monikę
30

Standardy

William Brendel zacytował Wikipedię, ale wolę ją z pyska konia.

Standardowy projekt C99 N1256 7.23.1 / 3 „Składniki czasu” mówi:

Deklarowane typy to size_t (opisane w 7.17) clock_t i time_t, które są typami arytmetycznymi zdolnymi do reprezentowania czasów

a 6.2.5 / 18 „Typy” mówi:

Typy całkowite i zmiennoprzecinkowe są wspólnie nazywane typami arytmetycznymi.

POSIX 7 sys_types.h mówi:

[CX] time_t będzie liczbą całkowitą.

gdzie [CX]jest zdefiniowany jako :

[CX] Rozszerzenie standardu ISO C.

Jest to rozszerzenie, ponieważ daje silniejszą gwarancję: zmiennoprzecinkowe są wyłączone.

gcc one-liner

Nie ma potrzeby tworzenia pliku, jak wspomniano w Quassnoi :

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

W Ubuntu 15.10 GCC 5.2 dwie górne linie to:

typedef long int __time_t;
typedef __time_t time_t;

Podział poleceń z kilkoma cytatami z man gcc:

  • -E: „Zatrzymaj się po etapie wstępnego przetwarzania; nie uruchamiaj kompilatora poprawnie.”
  • -xc: Określ język C, ponieważ dane wejściowe pochodzą ze standardowego wejścia, które nie ma rozszerzenia pliku.
  • -include file: „Przetwarzaj plik tak, jakby„ #include ”file” pojawił się jako pierwszy wiersz podstawowego pliku źródłowego. ”
  • -: wejście ze standardowego wejścia
Ciro Santilli
źródło
1
Nie ma potrzeby używania rury z echa:gcc -E -xc -include time.h /dev/null | grep time_t
rvighne
12

Odpowiedź jest zdecydowanie specyficzna dla implementacji. Aby ostatecznie dowiedzieć się o swojej platformie / kompilatorze, po prostu dodaj to wyjście gdzieś w kodzie:

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

Jeśli odpowiedź to 4 (32 bity), a dane mają wykraczać poza 2038 r. , Masz 25 lat na migrację kodu.

Twoje dane będą w porządku, jeśli przechowujesz je jako ciąg, nawet jeśli jest to coś takiego jak:

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

Następnie po prostu przeczytaj go w ten sam sposób (fread, fscanf itp.) Do int), a uzyskasz czas przesunięcia epoki. Podobne obejście istnieje w .Net. Bez problemu przekazuję 64-bitowe numery epoki między systemami Win i Linux (kanałem komunikacji). To powoduje problemy z porządkowaniem bajtów, ale to już inny temat.

Aby odpowiedzieć na zapytanie paxdiablo, powiedziałbym, że wypisał „19100”, ponieważ program został napisany w ten sposób (i przyznaję, że zrobiłem to sam w latach 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);

printfOświadczenie drukuje stały ciąg „rok wynosi: 19”, po której następuje zero-wyściełane ciąg z „lat od 1900 roku” (definicja tm->tm_year). Oczywiście w 2000 r. Ta wartość wynosi 100. "%02d"pad z dwoma zerami, ale nie obcina, jeśli jest dłuższy niż dwie cyfry.

Prawidłowy sposób to (zmień tylko na ostatni wiersz):

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

Nowe pytanie: Jakie jest uzasadnienie tego myślenia?

pwrgreg007
źródło
2
Prawdopodobnie powinieneś użyć specyfikatora %zuformatu do sformatowania size_twartości ( podanych przez sizeof), ponieważ są one bez znaku ( u) i długości size_t ( z) ·
Adrian Günter
... lub użyj printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));i uniknij zproblemu.
chux - Przywróć Monikę
6

W Visual Studio 2008 domyślnie jest to __int64chyba, że ​​zdefiniujesz _USE_32BIT_TIME_T. Lepiej udawaj, że nie wiesz, jak to jest zdefiniowane, ponieważ może (i będzie) zmieniać się z platformy na platformę.

Zaćmienie
źródło
2
Zwykle to działa, ale jeśli twój program ma śledzić wydarzenia, które będą miały miejsce za 30 lat, bardzo ważne jest, aby nie mieć podpisanego 32-bitowego time_t.
Rob Kennedy
4
@ Rob, bah, zostaw to! Zaczniemy biegać jak bezgłowe kurczaki w 2036 roku, tak samo jak w przypadku Y2K. Niektórzy z nas zarobią mnóstwo pieniędzy, będąc konsultantami Y2k38, Leonard Nimoy wyda kolejną zabawną książkę o tym, jak wszyscy powinniśmy iść i ukryć się w lesie ...
paxdiablo
1
... i wszystko się rozwali, publiczność zastanawia się, o co tyle zamieszania. Mogę nawet wyjść z emerytury, aby zarobić trochę pieniędzy na dziedzictwo dzieci :-).
paxdiablo
2
BTW, znaleźliśmy tylko jeden błąd Y2K i była to strona internetowa z datą 1 stycznia 19100. Ćwicz dla czytelnika, dlaczego ...
paxdiablo
9
Jeśli zdarzeniem, które nastąpi za 30 lat, jest „wygasanie tej kopii zapasowej”, możesz mieć kłopoty TERAZ, a nie w 2038 r. Dodaj 30 lat do dzisiejszego 32-bitowego time_t, a otrzymasz datę w przeszłości. Twój program szuka zdarzeń do przetworzenia, znajduje takie, które są opóźnione (o 100 lat!), I wykonuje je. Ups, nie ma już kopii zapasowej.
Rob Kennedy
5

time_tjest typu long intna maszynach 64-bitowych, w przeciwnym razie jest long long int.

Możesz to sprawdzić w tych plikach nagłówka:

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

(Poniższe instrukcje nie są jeden po drugim. Można je znaleźć w pliku nagłówkowym lub przy użyciu wyszukiwania Ctrl + f.)

1 w time.h

typedef __time_t time_t;

2) W types.h

# define __STD_TYPE     typedef  
__STD_TYPE __TIME_T_TYPE __time_t;  

3) W 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) Ponownie w 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
źródło
Pliki te są dostarczane przez glibc na Ubuntu 15.10 BTW.
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功
3
Nie long intwszędzie jest. Zobacz stackoverflow.com/questions/384502/…
Zan Lynx
4

Jest to 32-bitowa liczba całkowita ze znakiem na większości starszych platform. Jednak powoduje to, że Twój kod cierpi na błąd z roku 2038 . Dlatego współczesne biblioteki C powinny definiować ją jako 64-bitową int, która jest bezpieczna przez kilka miliardów lat.


źródło
3

Zazwyczaj te leżące u podstaw implementacji typedefs dla gcc znajdują się w katalogu bitslub asmnagłówku. Dla mnie to jest /usr/include/x86_64-linux-gnu/bits/types.h.

Możesz po prostu grep lub użyć wywołania preprocesora, takiego jak to sugerowane przez Quassnoi, aby zobaczyć, który konkretny nagłówek.

poolie
źródło
1

Jaki jest ostatecznie typ_czasu?

Solidny kod nie dba o typ.

Gatunek C time_tsię typ rzeczywisty jak double, long long, int64_t, intitp

Może nawet być unsignedtak, ponieważ zwracane wartości z wielu funkcji czasu wskazujące błąd nie są -1, ale (time_t)(-1)- Ten wybór implementacji jest rzadki.

Chodzi o to, że ten „typ wiedzy” jest rzadki. Kod powinien być napisany, aby uniknąć potrzeby.


Jednak często pojawia się konieczność „wiedzieć”, gdy kod chce wydrukować plik raw time_t. Rzutowanie na najszerszą liczbę całkowitą pomieści większość współczesnych przypadków.

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

Casting Do doubleczy long doublezadziała też jeszcze może zapewnić niedokładnymi wyjście dziesiętny

printf("%.16e", (double) now);
chux - Przywróć Monikę
źródło
Potrzebuję poznać sytuację, ponieważ muszę przenieść czas z systemu ARM do systemu AMD64. time_t to 32 bity na ramieniu i 64 bity na serwerze. jeśli przetłumaczę czas na format i wyślę ciąg, będzie on nieefektywny i powolny. Dlatego o wiele lepiej jest po prostu wysłać cały time_t i uporządkować go na serwerze. Muszę jednak trochę lepiej zrozumieć ten typ, ponieważ nie chcę, aby liczba była zniekształcona przez różnicowanie endianizmu między systemami, więc muszę użyć htonl ... ale najpierw potrzebuję znać, chcę aby dowiedzieć się, jaki jest typ podstawowy;)
Owl
Innym przypadkiem „musisz wiedzieć”, przynajmniej w przypadku podpisania vs niepodpisania, jest to, czy musisz zachować ostrożność, odejmując czasy. Jeśli po prostu „odejmiesz i wydrukujesz wynik”, prawdopodobnie uzyskasz to, czego oczekujesz w systemie ze znakiem time_t ze znakiem, ale bez podpisu time_t.
Michael Firth
@MichaelFirth Przypadki istnieją zarówno dla liczb całkowitych ze znakiem time_t, jak i dla znaku bez znaku time_t, gdzie odjęcie surowe spowoduje nieoczekiwane wyniki. C zapewnia double difftime(time_t time1, time_t time0)jednolite podejście odejmowania.
chux - Przywróć Monikę
-3

time_tjest tylko typedefdla 8 bajtów ( long long/__int64), które rozumieją wszystkie kompilatory i system operacyjny. Kiedyś to było tylko long int(4 bajty), ale nie teraz. Jeśli spojrzeć na time_tw crtdefs.hWas znajdzie obie implementacje ale OS użyje long long.

bvrwoo_3376
źródło
5
wszystkie kompilatory i systemy operacyjne? Nie. W moim systemie Linux kompilator przyjmuje 4-bajtową implementację ze znakiem.
Vincent
W systemach Zynq 7010 czas_t wynosi 4 bajty.
Sowa
1
W systemach wbudowanych pracuję na time_t prawie zawsze ma 32 bity lub 4 bajty. Norma wyraźnie stwierdza, że ​​to konkretna implementacja sprawia, że ​​ta odpowiedź jest po prostu błędna.
Cobusve