В Open Source программа I написал, я читаю двоичные данные (написанные другой программой) из файла и выводя int, double, и другие типы данных. Одна из проблем заключается в том, что она должна работайте на 32-битных и 64-битных машинах обеих степеней, что означает, что я в конечном итоге приходится делать немного бит-бит. Я знаю (очень) немного о типе punning и строгом aliasing и хочу удостовериться, что я делая все правильно.
В принципе, легко конвертировать из char * в int различных размеров:
int64_t snativeint64_t(const char *buf)
{
/* Interpret the first 8 bytes of buf as a 64-bit int */
return *(int64_t *) buf;
}
и у меня есть набор функций поддержки для замены байтовых порядков по мере необходимости, таких как:
int64_t swappedint64_t(const int64_t wrongend)
{
/* Change the endianness of a 64-bit integer */
return (((wrongend & 0xff00000000000000LL) >> 56) |
((wrongend & 0x00ff000000000000LL) >> 40) |
((wrongend & 0x0000ff0000000000LL) >> 24) |
((wrongend & 0x000000ff00000000LL) >> 8) |
((wrongend & 0x00000000ff000000LL) << 8) |
((wrongend & 0x0000000000ff0000LL) << 24) |
((wrongend & 0x000000000000ff00LL) << 40) |
((wrongend & 0x00000000000000ffLL) << 56));
}
Во время выполнения программа обнаруживает конечность машины и назначает один из указанных выше указатель функции:
int64_t (*slittleint64_t)(const char *);
if(littleendian) {
slittleint64_t = snativeint64_t;
} else {
slittleint64_t = sswappedint64_t;
}
Теперь сложная часть возникает, когда я пытаюсь использовать char * для двойника. Я бы подобно повторному использованию кода замены под заголовком:
union
{
double d;
int64_t i;
} int64todouble;
int64todouble.i = slittleint64_t(bufoffset);
printf("%lf", int64todouble.d);
Однако некоторые компиляторы могли оптимизировать задание "int64todouble.i" и сломать программу. Существует ли более безопасный способ сделать это, учитывая что эта программа должна оставаться оптимизированной для производительности, а также что я предпочитают не писать параллельный набор преобразований для литья char * в двойной напрямую? Если профсоюзный метод наказания безопасен, должен ли я быть перезаписывать мои функции, такие как snativeint64_t, чтобы использовать его?
В итоге я использовал ответ Стив Джессоп, потому что функции преобразования переписаны для использования memcpy, например:
int64_t snativeint64_t(const char *buf)
{
/* Interpret the first 8 bytes of buf as a 64-bit int */
int64_t output;
memcpy(&output, buf, 8);
return output;
}
скомпилирован в тот же самый ассемблер, что и исходный код:
snativeint64_t:
movq (%rdi), %rax
ret
Из двух версий memcpy более явно выражает то, что я пытаюсь сделать, и должен работать даже с самыми наивными компиляторами.
Адам, ваш ответ тоже был замечательным, и я многому научился у него. Спасибо за публикацию!