Про endianness работу в Си
Что: 4a1cde747924fa08548d1092f1c5e366cf2ce877
Когда: 2022-10-16 13:17:41+03:00
Темы: c hate
Про endianness работу в Си
https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
В b45f896af5ca60802cbae0a00d8223857a771eba я давал ссылки на тему про
endianness конвертацию в Си. Но на днях на работе мне прислали вопрос
уверен ли я что с endianness нет проблем в таком куске кода тривиальном:
size_t len = (size_t)(data[2] << 8) | data[3];
Отвечаю что возможно я какой-то очередной подвох в Си не знаю, но что в
нём не так? Мне кидают информацией об endian.h и be16toh(). Отвечаю что
в курсе их существования, но:
- конкретно у be16toh менее удобный API (указатели все эти)
- be16dec, который удобен для использования, не факт что есть в glibc,
как например в нём, как оказалось, не было be64dec
- endian.h include не портабельный: в BSD системах это sys/endian.h
- плюсом будет только отсутствие *нескольких* арифметических операций
при запуске на BE-архитектуре. Если бы речь шла про миллионы пакетов в
секунду, то возможно можно было бы начать задумываться об этом, но
речь про один пакет за нескольких секунд (это просто парсинг его размера)
Коллега никакого ответа на это так и не дал, только повторив что в одном
случае порядок меняется, а в другом нет. Но до сих пор не знаю к чему
этот ответ.
Можно или писать чуть-чуть более эффективный код, который, в зависимости
от endianness платформы, будет тасовать байты или будет использовать их
as-is без преобразований. Или писать код независимый от endianness
вообще, в котором несколько тривиальных инструкций выполняются всегда,
пускай даже когда это и не надо (совпадающий endianness платформы и данных).
Позже вспомнил про статью Роба Пайка, в которой он тоже считает всё это
фигнёй. Мол, если в коде есть #ifdef BIG_ENDIAN, то это не порядок.
Вместо того, чтобы написать:
i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
такие люди будут писать что-то типа:
i = *((int*)data);
#ifdef BIG_ENDIAN
i = ((i&0xFF)<<24) | (((i>>8)&0xFF)<<16) | (((i>>16)&0xFF)<<8) | (((i>>24)&0xFF)<<0);
#endif
или, для случая с моим 16-бит кодом:
#include <stdint.h>
#ifdef __FreeBSD__ || ...
#include <sys/endian.h>
#elif __linux__
#include <endian.h>
#endif
[...]
uint16_t *lenp = (uint16_t *)(&data[2]);
size_t len = (be16toh(*rtcplen);
Что, очевидно, гораздо больше года. Плюс не везде возможно целые числа
адресовать по любому адресу (выравнивания).
In fact, byte-swapping is the surest indicator the programmer
doesn't understand how byte order works. Why do people make the byte
order mistake so often? I think it's because they've seen a lot of
bad code that has convinced them byte order matters.
Plan 9 вообще без единого, как говорит Пайк, platform-specific #ifdef-а
был написан.
оставить комментарий
Сгенерирован: SGBlog 0.34.0