Порядок байтов

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску

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

В общем случае, для представления числа M, большего 255 (здесь  — максимальное целое число, записываемое одним байтом), приходится использовать несколько байтов (n). При этом число M записывается в позиционной системе счисления по основанию 256:

Набор целых чисел , каждое из которых лежит в интервале от 0 до 255, является последовательностью байтов, составляющих M. При этом называется младшим байтом, а  — старшим байтом числа M.

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

Варианты записи[править | править код]

Порядок от старшего к младшему[править | править код]

Порядок от старшего к младшему (англ. big-endian — с большого конца): . Этот порядок подобен привычному порядку записи (например арабскими цифрами) слева направо , например, число сто двадцать три было бы записано при таком порядке как 123. В этом же порядке принято записывать байты в технической и учебной литературе, если другой порядок явно не обозначен.

Этот порядок является стандартным для протоколов TCP/IP, он используется в заголовках пакетов данных и во многих протоколах более высокого уровня, разработанных для использования поверх TCP/IP. Поэтому порядок байтов от старшего к младшему часто называют «сетевым порядком байтов» (англ. network byte order). Этот порядок байтов используется процессорами IBM 360/370/390, SPARC, Motorola 68000 (отсюда третье название — порядок байтов Motorola, англ. Motorola byte order).

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

Порядок байтов от старшего к младшему применяется также во многих форматах файлов — например, PNG, FLV, EBML, JPEG.

Порядок от младшего к старшему[править | править код]

Порядок от младшего к старшему (англ. little-endian — с малого конца):

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

Этот порядок записи принят в памяти персональных компьютеров с процессорами архитектуры x86, в связи с чем иногда его называют интеловским порядком байтов (по названию компании-создателя архитектуры x86). Современные процессоры x86 позволяют работать с одно-, двух-, четырёх- и восьмибайтовыми операндами. В таком порядке следования байтов очень удобно то, что при увеличении размера (количества байтов) операнда, значение его первого байта неизменно: 3210 → 3210’0000. При порядке от старшего к младшему значение изменилось бы, например: 0123 → 0000’0123;

Кроме x86, такой порядок байтов применяется в архитектурах VAX (отсюда ещё одно название англ. VAX byte order[1]), DEC Alpha и многих других.

Также порядок «от младшего к старшему» применяется в USB, PCI, таблице разделов GUID, он рекомендован FidoNet. Но в целом соглашение little-endian поддерживают меньше кросс-платформенных протоколов и форматов данных, чем big-endian.

Переключаемый порядок[править | править код]

Многие процессоры могут работать и в порядке «от младшего к старшему», и в обратном, например, ARM (по умолчанию — little-endian), PowerPC (кроме PowerPC 970), DEC Alpha, MIPS, PA-RISC и IA-64. Обычно порядок байтов выбирается программно во время инициализации операционной системы, но может быть выбран и аппаратно перемычками на материнской плате. В этом случае правильнее говорить о порядке байтов на уровне операционной системы. Переключаемый порядок байтов иногда называют англ. bi-endian.

Смешанный порядок[править | править код]

Смешанный (комбинированный, гибридный) порядок байтов (англ. middle-endian) иногда используется при работе с числами, длина которых превышает машинное слово. Число представляется последовательностью машинных слов, которые записываются в формате, естественном для данной архитектуры, но сами машинные слова следуют в обратном порядке.

В процессорах VAX и ARM используется смешанное представление для длинных вещественных чисел.

Пример[править | править код]

Далее приведён пример, в котором описывается размещение 4-байтового числа в ОЗУ ЭВМ, доступ к которому может производиться и как к 32-разрядному слову, и побайтно.

Все числа записаны в 16-ричной системе счисления.

Число: 0xA1B2C3D4
Представление
Порядок от младшего к старшему (little-endian)
Порядок от старшего к младшему (big-endian)
Порядок, принятый в PDP-11 (PDP-endian)

Определение порядка байтов[править | править код]

Порядок байтов (endianness) в конкретной машине можно определить с помощью программы на языке Си (testbyteorder.c):

#include <stdio.h>
#include <stdint.h>

int main ()
{
  uint16_t x = 0x0001;
  printf("%s-endian\n", *((uint8_t *) &x) ? "little" : "big");
}

Результаты запуска на big-endian машине (SPARC):

 $ uname -m
 sparc64
 $ gcc -o testbyteorder testbyteorder.c 
 $ ./testbyteorder
 big-endian

Результаты запуска на little-endian машине (x86):

 $ uname -m
 i386
 $ gcc -o testbyteorder testbyteorder.c 
 $ ./testbyteorder
 little-endian

Вещественные числа[править | править код]

Хранение вещественных чисел также может зависеть от порядка байтов. Так, на x86 используются форматы IEEE 754 со знаком и порядком числа в старших байтах.

Юникод[править | править код]

Если Юникод записан в формате UTF-16 или UTF-32, то порядок байтов уже существенен. Одним из способов обозначения порядка байтов в юникодовых текстах является постановка в начале специального символа BOM (byte order mark, маркер последовательности байтов, U+FEFF) — «перевёрнутый» вариант этого символа (U+FFFE) не существует и не допускается в текстах.

Символ U+FEFF изображается в UTF-16 последовательностью байтов 0xFE 0xFF (big-endian) или 0xFF 0xFE (little-endian), а в UTF-32 — последовательностью 0x00 0x00 0xFE 0xFF (big-endian) или 0xFF 0xFE 0x00 0x00 (little-endian).

Проблемы совместимости и конверсии[править | править код]

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

Для преобразования между сетевым порядком байтов (англ. network byte order), который всегда big-endian, и порядком байтов, использующимся на машине (англ. host byte order), стандарт POSIX предусматривает функции htonl(), htons(), ntohl(), ntohs():

  • uint32_t htonl(uint32_t hostlong); — конвертирует 32-битную беззнаковую величину из локального порядка байтов в сетевой;
  • uint16_t htons(uint16_t hostshort); — конвертирует 16-битную беззнаковую величину из локального порядка байтов в сетевой;
  • uint32_t ntohl(uint32_t netlong); — конвертирует 32-битную беззнаковую величину из сетевого порядка байтов в локальный;
  • uint16_t ntohs(uint16_t netshort); — конвертирует 16-битную беззнаковую величину из сетевого порядка байтов в локальный.

В случае совпадения текущего порядка байтов и сетевого функции отработают как «пустые» — то есть порядок байтов не поменяется. Стандарт также допускает, чтобы эти функции были реализованы в виде макросов.

Существует много языков и библиотек со средствами конвертации в оба основных порядка байтов и обратно.

Ядро Linux: le16_to_cpu(), cpu_to_be32(), cpu_to_le16p(), и так далее;

Ядро FreeBSD: htobe16(), le32toh(), и так далее;

Erlang:

 <<Count:32/big-unsigned-integer, Average:64/big-float>> = Chunk

 Message = <<Length:32/little-unsigned-integer,
        MType:16/little-unsigned-integer, MessageBody>>

Python:

 import struct
 Count, Average = struct.unpack(">Ld", Chunk)
 Message = struct.pack("<LH", Length, MType) + MessageBody

Perl:

 ($Count, $Average) = unpack('L>d>', $Chunk);
 $Message = pack('(LS)<', $Length, $MType) . $MessageBody;
 (или то же самое: $Message = pack('Vv', $Length, $MType) . $MessageBody;)

данные примеры для Erlang, Python, Perl содержат идентичную функциональность.

Процессоры Intel x86-64 имеют инструкцию BSWAP для смены порядка байтов.

Этимология названия[править | править код]

Термины big-endian и little-endian первоначально не имели отношения к информатике. В сатирическом произведении Джонатана Свифта «Путешествия Гулливера» описываются вымышленные государства Лилипутия и Блефуску, в течение многих лет ведущие между собой войны из-за разногласия по поводу того, с какого конца следует разбивать варёные яйца. Тех, кто считает, что их нужно разбивать с тупого конца, в произведении называют Big-endians («тупоконечники»).

Споры между сторонниками big-endian и little-endian в информатике также часто носят характер т. н. «религиозных войн».[2] Термины big-endian и little-endian ввёл Коэн (англ. Danny Cohen) в 1980 году в своей статье «On Holy Wars and a Plea for Peace» («О священных войнах и призыв к миру»).[3][4]

См. также[править | править код]

Примечания[править | править код]

  1. pack() в Perl. Дата обращения: 20 декабря 2010. Архивировано 13 декабря 2010 года.
  2. DAV’s Endian FAQ. Дата обращения: 3 августа 2008. Архивировано из оригинала 10 ноября 2006 года.
  3. Danny Cohen. On Holy Wars and a Plea for Peace (англ.) (1 апреля 1980). Дата обращения: 24 января 2010. Архивировано 15 февраля 2012 года.
  4. Таненбаум Э. Архитектура компьютера. — 5-е изд. — СПб.: Питер, 2007. — 844 с. — С. 89.

Ссылки[править | править код]