Когда Endianness становится фактором? - PullRequest
68 голосов
/ 24 августа 2011

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

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

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

  • Если яразложить и перекомпоновать короткое целое число, используя следующий код, будет ли порядковый номер больше не быть фактором?

    // 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;
    };
    
  • Существует ли стандартный способ компенсации порядкового номера?

Заранее спасибо!

Ответы [ 8 ]

50 голосов
/ 24 августа 2011

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

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

35 голосов
/ 24 августа 2011

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

7 голосов
/ 24 августа 2011
  1. Нет, хотя у вас есть правильная общая идея.Что вам не хватает, так это тот факт, что, хотя обычно это последовательное соединение, сетевое соединение (по крайней мере, большинство сетевых подключений) все еще гарантирует правильную последовательность на уровне октетов (байтов), т. Е. Если вы отправляете байт со значением0x12 на машине с прямым порядком байтов, она все равно будет принята как 0x12 на машине с прямым порядком байтов.

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

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

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

6 голосов
/ 30 августа 2011

Обе порядковые номера имеют одно преимущество, о котором я знаю:

  1. Символ с прямым порядком байтов концептуально легче понять, поскольку он похож на нашу позиционную систему счисления: от наиболее значимой до наименее значимой.
  2. Little-endian удобен при повторном использовании ссылки на память для нескольких размеров памяти.Проще говоря, если у вас есть указатель на порядковый номер unsigned int*, но вы знаете, что сохраненное там значение <256, вы можете привести свой указатель к <code>unsigned char*.
6 голосов
/ 24 августа 2011

«Стандартный способ» компенсации состоит в том, что понятие «сетевой порядок байтов» было определено почти всегда (AFAIK) как big endian.

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

5 голосов
/ 24 августа 2011

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

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

4 голосов
/ 25 августа 2011

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

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

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

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

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

3 голосов
/ 24 августа 2011

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

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

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

...