Макрос 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

Я вообще понимаю порядок байтов. Вот как я понимаю код:

  1. Создайте пример маленьких, средних и больших порядков байтов.
  2. Сравните контрольный пример с примерами с порядком байтов в порядке от нуля, среднего до большого и решите, к какому типу относится хост-машина

Что я не понимаю, так это следующие аспекты:

  1. Зачем нужен союз для хранения тест-кейса? не uint32_t гарантированно сможет хранить 32 бита /4 байта по мере необходимости? И что делает назначение { { 0, 1, 2, 3 } } имею в виду? Он присваивает значение объединению, но почему странная разметка с двумя фигурными скобками?
  2. Почему чек для 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,

Другие вопросы по тегам