C ++: преобразование __int64 в unsigned long - PullRequest
2 голосов
/ 14 сентября 2011

Я хотел бы использовать следующую функцию в Windows и Linux, но я не уверен, как преобразовать __int64 в unsigned long. Насколько безопасно использовать значение, как я?

getTimeInMilliseconds()
{
    #ifdef _WIN32


        static const __int64 magic = 116444736000000000; // 1970/1/1
        SYSTEMTIME st;
        GetSystemTime(&st);
        FILETIME   ft;
        SystemTimeToFileTime(&st,&ft); // in 100-nanosecs...
        __int64 t;
        memcpy(&t,&ft,sizeof t);
        return (unsigned long)((t - magic)/10000);
    #else
        struct timeval tv;
        gettimeofday(&tv, NULL);
        unsigned long s = tv.tv_sec * 1000;
        unsigned long us = tv.tv_usec / 1000;
        return s + us;
    #endif
}

1 Ответ

1 голос
/ 18 июля 2012

Да, это «безопасно» в том смысле, что вы можете делать синтаксически то, что написано в коде. При понижении 64-битного int до 32-битного int вы, естественно, теряете верхние 32 бита, что не проблема, если ваше значение достаточно мало, проблема заключается в том, что ваше значение на самом деле достаточно мал.

оба определения (WIN32 и нет) подвержены проблемам с переполнением не потому, что вы не можете привести 64-битное int к long, а потому, что вы пытаетесь вбить слишком большое значение в слишком маленький контейнер.

Проблема # 1, имя функции описывает результат как «время в миллисекундах», что ложно, обе реализации пытаются вернуть результат в микросекундах (тысячи секунд), а не в «миллисекундах», которые это миллионные доли секунды. Решите, к какому результату вы на самом деле хотите стрелять, и пометьте его соответствующим образом, чтобы избежать путаницы в долгосрочной перспективе. Обратите внимание, что интервалы в 100 наносекунд (миллиардная секунда) совпадают с 1/10 миллисекунды, что составляет 1/10000 микросекунды, просто для справки.

Задача № 2: "Memcpy", чтобы получить структуру FILETIME в длинном int, также неверен. FILETIME и __int64 имеют разные байтовые выравнивания, и вы получите таким образом биты в неправильных местах. Чтобы превратить FILETIME в _int64, вам нужно сделать следующее:

LARGE_INTEGER foo;
FILETIME baz;
__int64 bar;

foo.HighPart = baz.dwHighDateTime;
foo.LowPart = baz.dwLowDateTime;
bar = foo.Quadpart;

Задача № 3: 32-битное int может хранить только значения времени, достаточно большие, чтобы быть полезными в секундах и даже тогда только до начала 2038 года, поэтому у timeval есть отдельный элемент для «микросекунд» просто сделать вашу жизнь трудной.

Если вы хотите, чтобы тысячи секунд были в одном значении, вам нужно как минимум 36 бит, вероятно, больше 38-40, чтобы иметь времена, которые могут ссылаться на значения в более устойчивом временном интервале, чем 1970-2038 годы. Поскольку очень немногие компиляторы поддерживают 40-битные целые числа, я бы порекомендовал просто перейти на полные 64-битные, если только по какой-то причине вы не можете , и в этом случае вы должны найти лучшее решение для того, что вы Пытаешься делать все вокруг.

...