Макрос Endianness в C
Я недавно видел этот пост о макросах endianness в C, и я не могу обернуться вокруг первого ответа.
Код, поддерживающий произвольные порядки байтов, готовый для помещения в файл с именем order32.h:
#ifndef ORDER32_H
#define ORDER32_H
#include <limits.h>
#include <stdint.h>
#if CHAR_BIT != 8
#error "unsupported char size"
#endif
enum
{
O32_LITTLE_ENDIAN = 0x03020100ul,
O32_BIG_ENDIAN = 0x00010203ul,
O32_PDP_ENDIAN = 0x01000302ul
};
static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
{ { 0, 1, 2, 3 } };
#define O32_HOST_ORDER (o32_host_order.value)
#endif
Вы бы проверить для систем с прямым порядком байтов через
O32_HOST_ORDER == O32_LITTLE_ENDIAN
Я вообще понимаю порядок байтов. Вот как я понимаю код:
- Создайте пример маленьких, средних и больших порядков байтов.
- Сравните контрольный пример с примерами с порядком байтов в порядке от нуля, среднего до большого и решите, к какому типу относится хост-машина
Что я не понимаю, так это следующие аспекты:
- Зачем нужен союз для хранения тест-кейса? не
uint32_t
гарантированно сможет хранить 32 бита /4 байта по мере необходимости? И что делает назначение{ { 0, 1, 2, 3 } }
имею в виду? Он присваивает значение объединению, но почему странная разметка с двумя фигурными скобками? - Почему чек для
CHAR_BIT
? Один комментарий упоминает, что было бы более полезно проверитьUINT8_MAX
? Почемуchar
даже используется здесь, когда он не гарантированно будет 8 бит в ширину? Почему бы просто не использоватьuint8_t
? Я нашел эту ссылку на Google-Devs GitHub. Они не полагаются на эту проверку... Может кто-нибудь уточнить?
3 ответа
Зачем нужен союз для хранения контрольного примера?
Весь смысл теста в том, чтобы присвоить массиву магическое значение, которое будет создавать массив.
не uint32_t
гарантированно сможет хранить 32 бита /4 байта по мере необходимости?
Ну, более или менее. Это будет, но кроме 32 бит нет никаких гарантий. Он потерпит неудачу только на действительно сложной архитектуре, с которой вы никогда не столкнетесь.
И что делает назначение { { 0, 1, 2, 3 } }
имею в виду? Он присваивает значение объединению, но почему странная разметка с двумя фигурными скобками?
Внутренняя скобка для массива.
Почему чек для CHAR_BIT?
Потому что это реальная гарантия. Если это не взорвется, все будет работать.
Один комментарий упоминает, что было бы более полезно проверить UINT8_MAX?
Почему здесь даже используется символ, когда он не гарантирует ширину 8 бит?
Потому что на самом деле это всегда так, в эти дни.
Почему бы просто не использовать uint8_t?
Я нашел эту ссылку на Google-Devs GitHub. Они не полагаются на эту проверку... Может кто-нибудь уточнить?
Много других вариантов будет работать также.
{{0, 1, 2, 3}}
является инициализатором для объединения, которое приведет к bytes
компонент заполняется [0, 1, 2, 3]
,
Теперь, так как bytes
массив и uint32_t
занимают то же место, вы можете прочитать то же значение, что и 32-битное целое число. Значение этого целого числа показывает, как массив был перетасован - что на самом деле означает, что вы используете систему с прямым порядком байтов.
Здесь есть только 3 популярных варианта - O32_LITTLE_ENDIAN
, O32_BIG_ENDIAN
, а также O32_PDP_ENDIAN
,
Для char
/ uint8_t
- Я не знаю. Я думаю, что имеет больше смысла просто использовать uint_8
без чеков.
Инициализация имеет два набора скобок, потому что внутренние скобки инициализируют bytes
массив. Так byte[0]
это 0, byte[1]
1 и т. д.
Союз позволяет uint32_t
лежать на тех же байтах, что и char
массив и быть интерпретированы в любой последовательности машины. Так что, если машина немного порядковая, 0
находится в младшем байте и 3
находится в старшем байте value
, И наоборот, если машина с прямым порядком байтов, 0
находится в старшем байте и 3
находится в младшем байте value
,