К чему в конечном счете относится type_t?
Я искал в окне Linux и увидел, что это typedef для
typedef __time_t time_t;
Но не смог найти определение __time_t.
11 ответов
Статья в Википедии time_t проливает свет на это. Суть в том, что тип time_t
не гарантируется в спецификации C.
time_t
Тип данных - это тип данных в библиотеке ISO C, определенный для хранения значений системного времени. Такие значения возвращаются из стандартаtime()
библиотечная функция. Этот тип является typedef, определенным в стандартном заголовке. ISO C определяет time_t как арифметический тип, но не определяет какой-либо конкретный тип, диапазон, разрешение или кодировку для него. Также не определены значения арифметических операций, применяемых к значениям времени.Unix и POSIX-совместимые системы реализуют
time_t
введите какsigned integer
(обычно 32 или 64 бита), который представляет количество секунд с начала эпохи Unix: полночь UTC 1 января 1970 года (не считая високосных секунд). Некоторые системы правильно обрабатывают отрицательные значения времени, а другие нет. Системы, использующие 32-разрядныеtime_t
типа подвержены проблеме 2038 года.
[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;
Это определено в $INCDIR/bits/types.h
через:
# 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
стандарты
Уильям Брендель цитировал Википедию, но я предпочитаю ее изо рта лошади.
Проект стандарта C99 N1256 7.23.1/3 "Составляющие времени" гласит:
Объявленные типы: size_t (описан в 7.17) clock_t и time_t, которые являются арифметическими типами, способными представлять время
и 6.2.5/18 "Типы" говорит:
Целочисленные и плавающие типы вместе называются арифметическими типами.
POSIX 7 sys_types.h говорит:
[CX] time_t должен быть целочисленным типом.
где [CX]
определяется как:
[CX] Расширение до стандарта ISO C.
Это расширение, потому что оно дает более сильную гарантию: плавающие точки отсутствуют.
gcc однострочник
Нет необходимости создавать файл, как упомянуто Quassnoi:
echo | gcc -E -xc -include 'time.h' - | grep time_t
В Ubuntu 15.10 GCC 5.2 две верхние строки:
typedef long int __time_t;
typedef __time_t time_t;
Разбивка команд с некоторыми цитатами из man gcc
:
-E
: "Остановитесь после этапа предварительной обработки; не запускайте должным образом компилятор."-xc
: Укажите язык C, поскольку входные данные поступают из стандартного ввода, которое не имеет расширения файла.-include file
: "Обрабатывать файл так, как если бы"#include "file""появилось как первая строка первичного исходного файла."-
: вход от стандартного ввода
Ответ определенно зависит от реализации. Чтобы выяснить это окончательно для вашей платформы / компилятора, просто добавьте этот вывод где-нибудь в вашем коде:
printf ("sizeof time_t is: %d\n", sizeof(time_t));
Если ответ 4 (32 бита) и ваши данные выходят за пределы 2038 года, то у вас есть 25 лет для переноса кода.
Ваши данные будут в порядке, если вы сохраните свои данные в виде строки, даже если это будет что-то простое, например:
FILE *stream = [stream file pointer that you've opened correctly];
fprintf (stream, "%d\n", (int)time_t);
Затем просто прочитайте его обратно тем же способом (fread, fscanf и т. Д. В int), и у вас будет время смещения вашей эпохи. Подобный обходной путь существует в.Net. Я без проблем передаю 64-битные числа эпох между системами Win и Linux (по каналу связи). Это поднимает проблемы с порядком байтов, но это уже другая тема.
Чтобы ответить на запрос Паксдиабло, я бы сказал, что он напечатал "19100", потому что программа была написана таким образом (и я признаю, что сделал это сам в 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
оператор выводит фиксированную строку "Year is: 19", за которой следует строка, дополненная нулями, с "Years from 1900" (определение tm->tm_year
). В 2000 году это значение, очевидно, равно 100. "%02d"
колодки с двумя нулями, но не усекаются, если они длиннее двух цифр.
Правильный путь (изменить только на последнюю строку):
printf ("Year is: %d\n", local_date_time.tm_year + 1900);
Новый вопрос: в чем причина такого мышления?
В Visual Studio 2008 по умолчанию используется __int64
если вы не определите _USE_32BIT_TIME_T
, Вам лучше всего лишь притвориться, что вы не знаете, как оно определяется, поскольку оно может (и будет) меняться от платформы к платформе.
time_t
имеет тип long int
на 64-битных машинах, иначе это long long int
,
Вы можете проверить это в следующих заголовочных файлах:
time.h
: /usr/include
types.h
а также typesizes.h
: /usr/include/x86_64-linux-gnu/bits
(Приведенные ниже утверждения не являются одно за другим. Их можно найти в заголовочном файле соответственно с помощью Ctrl + f search.)
1) В time.h
typedef __time_t time_t;
2) В types.h
# define __STD_TYPE typedef
__STD_TYPE __TIME_T_TYPE __time_t;
3) В 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) Опять в 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;
Это 32-разрядное целое число со знаком на большинстве устаревших платформ. Однако это приводит к тому, что ваш код страдает от ошибки 2038 года. Поэтому современные библиотеки C должны определять его как 64-битное целое число со знаком, что безопасно в течение нескольких миллиардов лет.
Обычно вы найдете эти базовые специфичные для реализации typedefs для gcc в bits
или же asm
Заголовок каталога. Для меня это /usr/include/x86_64-linux-gnu/bits/types.h
,
Вы можете просто выполнить grep или использовать вызов препроцессора, подобный предложенному Quassnoi, чтобы увидеть, какой именно заголовок.
К чему в конечном счете относится type_t?
Надежный код не заботится о типе.
Виды C time_t
быть настоящим типом, как double, long long, int64_t, int
, так далее.
Это даже может быть unsigned
в качестве возвращаемых значений от многих функций времени, указывающих на ошибку, нет -1
, но (time_t)(-1)
- Такой выбор реализации встречается редко.
Дело в том, что "необходимость знать" тип встречается редко. Код должен быть написан, чтобы избежать необходимости.
Тем не менее, общая "необходимость знать" возникает, когда код хочет напечатать необработанный time_t
, Приведение к самому широкому целочисленному типу будет соответствовать большинству современных случаев.
time_t now = 0;
time(&now);
printf("%jd", (intmax_t) now);
// or
printf("%lld", (long long) now);
Приведение к double
или же long double
тоже будет работать, но может дать неточный десятичный вывод
printf("%.16e", (double) now);
Вы могли бы использовать
typeid
узнать, как
time_t
определяется в вашей системе.
#include <iostream> // cout
#include <ctime> // time_t
#include <typeinfo> // typeid, name
using namespace std;
int main()
{
cout << "Test 1: The type of time_t is: \t\t"
<< typeid(time_t).name() << endl;
cout << "Test 2: time_t is a signed long?: \t"
<< (typeid(time_t) == typeid(signed long) ? "true" : "false") << endl;
cout << "Test 3: time_t is an unsigned long?: \t"
<< (typeid(time_t) == typeid(unsigned long) ? "true" : "false") << endl;
return 0;
}
В случае моей системы вывод:
Тест 1: Тип time_t: l Тест 2: time_t является длинным со знаком?: true Тест 3: time_t является беззнаковым длинным?: false
time_t
просто typedef
для 8 байт (long long/__int64
) которые понимают все компиляторы и ОС. В те времена это было только для long int
(4 байта) но не сейчас. Если вы посмотрите на time_t
в crtdefs.h
вы найдете обе реализации, но ОС будет использовать long long
,