Когда Endianness становится фактором?

Конечность из того, что я понимаю, - это когда байты, составляющие многобайтовое слово, различаются по своему порядку, по крайней мере, в наиболее типичном случае. Так что 16-разрядное целое число может быть сохранено как 0xHHLL или 0xLLHH.

Предполагая, что я не ошибаюсь, мне хотелось бы знать, когда Endianness становится основным фактором при отправке информации между двумя компьютерами, где Endian может быть или не быть другим.

  • Если я передаю короткое целое число 1 в виде массива char и без коррекции, оно получено и интерпретируется как 256?

  • Если я разлагаю и перекомпоную короткое целое с помощью следующего кода, будет ли endianness больше не фактором?

    // Sender:
    for(n=0, n < sizeof(uint16)*8; ++n) {
        stl_bitset[n] = (value >> n) & 1;
    };
    
    // Receiver:
    for(n=0, n < sizeof(uint16)*8; ++n) {
        value |= uint16(stl_bitset[n] & 1) << n;
    };
    
  • Существует ли стандартный способ компенсации судьбы?

Спасибо заранее!

Ответ 1

Очень абстрактно говоря, endianness является свойством переинтерпретации переменной как char -array.

Практически это имеет значение, когда вы read() от и write() во внешний поток байтов (например, файл или сокет). Или, говоря абстрактно снова, имеет значение endianness, когда вы сериализуете данные (по сути, потому что сериализованные данные не имеют системы типов и просто состоят из немых байтов); и endianness не имеет значения в вашем языке программирования, потому что язык работает только на значениях, а не на представлениях. Переход от одного к другому - это то, где вам нужно вникнуть в детали.

Ввод текста:

uint32_t n = get_number();

unsigned char bytesLE[4] = { n, n >> 8, n >> 16, n >> 24 };  // little-endian order
unsigned char bytesBE[4] = { n >> 24, n >> 16, n >> 8, n };  // big-endian order

write(bytes..., 4);

Здесь мы могли бы просто сказать, reinterpret_cast<unsigned char *>(&n), и результат зависел бы от континентности системы.

И чтение:

unsigned char buf[4] = read_data();

uint32_t n_LE = buf[0] + buf[1] << 8 + buf[2] << 16 + buf[3] << 24; // little-endian
uint32_t n_BE = buf[3] + buf[2] << 8 + buf[1] << 16 + buf[0] << 24; // big-endian

Опять же, здесь мы могли бы сказать, uint32_t n = *reinterpret_cast<uint32_t*>(buf), и результат зависел бы от конечности машины.


Как вы можете видеть, с интегральными типами вам никогда не нужно знать законность вашей собственной системы, только потока данных, если вы используете алгебраические операции ввода и вывода. С другими типами данных, такими как double, проблема сложнее.

Ответ 2

Для записи, если вы передаете данные между устройствами, вы почти всегда должны использовать упорядочение по сети с помощью ntohl, htonl, ntohs, htons. Он преобразуется в стандартный сетевой порядок байтов для Endianness независимо от того, что использует ваша система и целевая система. Конечно, обе системы должны быть запрограммированы следующим образом - но они обычно находятся в сетевых сценариях.

Ответ 3

  • Нет, хотя у вас есть правильная общая идея. Недостаток заключается в том, что, хотя обычно это последовательное соединение, сетевое соединение (по крайней мере, большинство сетевых подключений) по-прежнему гарантирует правильную согласованность на уровне октета (байта) - то есть, если вы отправляете байт со значением 0x12 на маленькой конечной машине, он все равно будет приниматься как 0x12 на машине большого конца.

    Глядя на короткий, если вы посмотрите на число в шестнадцатеричном виде, возможно, это поможет. Он начинается как 0x0001. Вы разбиваете его на два байта: 0x00 0x01. После получения, это будет считано как 0x0100, которое окажется равным 256.

  • Так как сеть имеет дело с endianess на октетном уровне, вы обычно должны только компенсировать порядок байтов, а не биты в байтах.

  • Вероятно, самый простой способ - использовать htons/htonl при отправке и ntohs/ntohl при приеме. Когда/если этого недостаточно, существует множество альтернатив, таких как XDR, ASN.1, CORBA IIOP, буферы протокола Google и т.д.

Ответ 4

"Стандартный способ" компенсации заключается в том, что понятие "порядок сетевого байта" определено почти всегда (AFAIK) как большой endian.

Отправители и получатели знают протокол проводов и, при необходимости, преобразуют перед передачей и после получения, чтобы предоставить приложениям нужные данные. Но этот перевод происходит внутри вашего сетевого уровня, а не в ваших приложениях.

Ответ 5

У обеих endianesses есть преимущество, о котором я знаю:

  • Большого конца концептуально легче понять, потому что он похож на нашу позиционную систему цифр: наиболее значимый для наименее значимых.
  • Мало-endian удобно при повторном использовании ссылки на память для нескольких размеров памяти. Проще говоря, если у вас есть указатель на little-endian unsigned int*, но вы знаете, что значение, хранящееся там, 256, вы можете направить указатель на unsigned char*.

Ответ 6

Endianness ВСЕГДА проблема. Некоторые скажут, что если вы знаете, что каждый хост, подключенный к сети, запускает одну и ту же ОС и т.д., То у вас не будет проблем. Это верно, пока это не так. Вам всегда нужно опубликовать спецификацию, в которой подробно описывается формат EXACT для проводных данных. Это может быть любой формат, который вы хотите, но каждая конечная точка должна понимать формат и быть в состоянии правильно его интерпретировать.

В общем, протоколы используют big-endian для числовых значений, но это имеет ограничения, если каждый не совместим с IEEE 754 и т.д. Если вы можете взять накладные расходы, используйте XDR (или ваше любимое решение) и будьте в безопасности.

Ответ 7

Ниже приведены некоторые рекомендации для кода, основанного на нейтральном тексте C/С++. Очевидно, что они написаны как "правила, которые следует избегать"... поэтому, если код имеет эти "функции", он может быть подвержен ошибкам, связанным с endian! (это из моей статьи о Endianness, опубликованной в Dr Dobbs)

  • Избегайте использования союзов, которые объединяют разные многобайтовые типы данных. (расположение союзов может иметь разные порядки, связанные с веществом)

  • Избегайте доступа к массивам байтов вне байтового типа данных. (порядок байтового массива имеет порядок, связанный с сущностью)

  • Избегайте использования бит-полей и байт-масок (поскольку компоновка хранилища зависит от конечности, маскировка байтов и выбор полей бит являются чувствительными к концу)

  • Избегайте указателей каста от многобайтового типа к другим типам байтов.
    (когда указатель переводится из одного типа в другой, конечная цель источника (т.е. исходная цель) теряется и последующая обработка может быть неправильной)

Ответ 8

Вам не следует беспокоиться, если вы не находитесь на границе системы. Обычно, если вы говорите в терминах stl, вы уже прошли эту границу.

Задача протокола сериализации указывать/определять, как последовательность байтов может быть преобразована в тип, который вы отправляете, с встроенным типом или настраиваемым типом.

Если вы говорите только о встроенном, вам может потребоваться машинная абстракция, предоставляемая инструментами, предоставляемыми вашей средой]