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


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

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

  • Если я передаю a короткое целое число 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 68

8 ответов:

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

практически, это имеет значение именно тогда, когда вы 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), и результат зависел бы от endianness системы.

и значение:

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), и результат зависел бы от машины endianness.


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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