Odpowiedzi:
Artykuł w Wikipedii time_t rzuca na to trochę światła. Najważniejsze jest to, że typ time_t
nie jest gwarantowany w specyfikacji C.
Typ
time_t
danych to typ danych w bibliotece ISO C zdefiniowany do przechowywania wartości czasu systemowego. Takie wartości są zwracane ze standardowejtime()
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_t
typ jakosigned 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ącetime_t
typ 32-bitowy są podatne na problem z rokiem 2038 .
time_t
moż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_t
moż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
.
time.h
treści. Ten artykuł prowadzi do strony cppreference.com, ale nigdzie nie można znaleźć cytowanej treści…
time_t
jest 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_t
tego wynika jedynie, że „powinien być typem całkowitym” . Implementacja może sprawić, że będzie time_t
bez znaku i nadal będzie zgodna z POSIX.
[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.h
poprzez:
# 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;
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).
__time_t
i nie time_t
znajdować podstawowego typu time_t
? Pomijasz krok?
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.
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ściagcc -E -xc -include time.h /dev/null | grep time_t
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);
printf
Oś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?
%zu
formatu do sformatowania size_t
wartości ( podanych przez sizeof
), ponieważ są one bez znaku ( u
) i długości size_t ( z
) ·
printf ("sizeof time_t is: %d\n", (int) sizeof(time_t));
i uniknij z
problemu.
W Visual Studio 2008 domyślnie jest to __int64
chyba, ż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ę.
time_t
jest typu long int
na maszynach 64-bitowych, w przeciwnym razie jest long long int
.
Możesz to sprawdzić w tych plikach nagłówka:
time.h
: /usr/include
types.h
i 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;
long int
wszędzie jest. Zobacz stackoverflow.com/questions/384502/…
Zazwyczaj te leżące u podstaw implementacji typedefs dla gcc znajdują się w katalogu bits
lub asm
nagłó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.
Jaki jest ostatecznie typ_czasu?
Solidny kod nie dba o typ.
Gatunek C time_t
się typ rzeczywisty jak double, long long, int64_t, int
itp
Może nawet być unsigned
tak, 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 double
czy long double
zadziała też jeszcze może zapewnić niedokładnymi wyjście dziesiętny
printf("%.16e", (double) now);
double difftime(time_t time1, time_t time0)
jednolite podejście odejmowania.
time_t
jest tylko typedef
dla 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_t
w crtdefs.h
Was znajdzie obie implementacje ale OS użyje long long
.
long int
.