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

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

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

В общем случае, для представления числа M, большего 255 (здесь  — максимальное целое число, записываемое одним байтом), приходится использовать несколько байтов. При этом число 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 позволяют работать с одно-, двух-, четырёх- и восьмибайтовыми операндами. При таком порядке байтов очень удобно то обстоятельство, что при увеличении размера («байтовости») операнда его значение не изменится (при порядке от старшего к младшему значение изменилось бы, например: 0123 → 0123’0000; а при порядке от младшего к старшему значение остаётся прежним: 3210 → 3210’0000).

Кроме 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
Представление D4*0x01 + C3*0x100 + B2*0x10000 + A1*0x1000000
Порядок от младшего к старшему (little-endian) 0xD4, 0xC3, 0xB2, 0xA1
Порядок от старшего к младшему (big-endian) 0xA1, 0xB2, 0xC3, 0xD4
Порядок, принятый в PDP-11 (PDP-endian) 0xB2, 0xA1, 0xD4, 0xC3

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

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

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

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

Результаты запуска на 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
  2. DAV’s Endian FAQ (недоступная ссылка — историякопия)
  3. Danny Cohen. On Holy Wars and a Plea for Peace (англ.) (1 апреля 1980). Проверено 24 января 2010. Архивировано 15 февраля 2012 года.
  4. Таненбаум Э. Архитектура компьютера. — 5-е изд. — СПб.: Питер, 2007. — 844 с. — С. 89.

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