Порядок байтов
Шаблон:Другой термин Шаблон:Другой термин В современной вычислительной технике и цифровых системах связи информация обычно представлена в виде последовательности байтов. В том случае, если число не может быть представлено одним байтом, имеет значение, в каком порядке байты записываются в памяти компьютера или передаются по линиям связи. Часто выбор порядка записи байтов произволен и определяется только соглашениями.
В общем случае, для представления числа M, большего 255 (здесь <math>255=2^8-1</math> — максимальное целое число, записываемое одним байтом), приходится использовать несколько байтов (n). При этом число M записывается в позиционной системе счисления по основанию 256:
- <math>M = \sum_{i=0}^{n-1}A_i\cdot 256^i=A_0\cdot 256^0+A_1\cdot 256^1+A_2\cdot 256^2+\dots+A_{n-1}\cdot 256^{n-1}.</math>
Набор целых чисел <math>A_0,\dots,A_{n-1}</math>, каждое из которых лежит в интервале от 0 до 255, является последовательностью байтов, составляющих M. При этом <math>A_0</math> называется младшим байтом, а <math>A_{n-1}</math> — старшим байтом числа M.
Поскольку компьютер не адресует отдельных битов (их можно получать только через битовые поля), порядок битов в байте важен только при физической организации хранения и передачи данных, может различаться от устройства к устройству и прикладному программисту обычно не нужен.
Содержание |
Варианты записи
Порядок от старшего к младшему
Порядок от старшего к младшему (англ. big-endian — с большого конца): <math>A_{n-1},\dots,A_0</math>. Этот порядок подобен привычному порядку записи (например арабскими цифрами) «слева-направо» , например, число сто двадцать три было бы записано при таком порядке как 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 — с малого конца): <math>A_0,\dots,A_{n-1}</math>
Это обратный привычному порядку записи чисел арабскими цифрами, например, число сто двадцать три было бы записано при таком порядке как 321. Иными словами этот порядок подобен правилу записи «справа-налево».
Этот порядок записи принят в памяти персональных компьютеров с процессорами архитектуры x86, в связи с чем иногда его называют интеловским порядком байтов (по названию компании-создателя архитектуры x86). Современные процессоры x86 позволяют работать с одно-, двух-, четырёх- и восьмибайтовыми операндами. В таком порядке следования байтов очень удобно то, что при увеличении размера (количества байтов) операнда, значение его первого байта неизменно: 3210 → 3210’0000. При порядке от старшего к младшему значение изменилось бы, например: 0123 → 0000’0123;
Кроме x86, такой порядок байтов применяется в архитектурах VAX (отсюда ещё одно название англ. VAX byte order, 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-ричной системе счисления.
Представление | <math>A1*1000000_{16} + B2*10000_{16} + C3*100_{16} + D4*1_{16} = A1B2C3D4</math> | <math>161*16^6_{10} + 178*16^4_{10} + 195*16^2_{10} + 212_{10} = 2712847316_{10}</math> | |
Порядок от младшего к старшему | (little-endian) | <math>D4_{16}, C3_{16}, B2_{16}, A1_{16}</math> | <math>212_{10}, 195_{10}, 178_{10}, 161_{10}</math> |
Порядок от старшего к младшему | (big-endian) | <math>A1_{16}, B2_{16}, C3_{16}, D4_{16}</math> | <math>161_{10}, 178_{10}, 195_{10}, 212_{10}</math> |
Порядок, принятый в PDP-11 | (PDP-endian) | <math>B2_{16}, A1_{16}, D4_{16}, C3_{16}</math> | <math>178_{10}, 161_{10}, 212_{10}, 195_{10}</math> |
Определение порядка байтов
Порядок байтов (endianness) в конкретной машине можно определить с помощью программы на языке Си (testbyteorder.c): <source lang="c">
- include <stdio.h>
- include <stdint.h>
int main () {
uint16_t x = 0x0001; printf("%s-endian\n", *((uint8_t *) &x) ? "little" : "big");
}
</source>Результаты запуска на big-endian машине (SPARC): <source lang="text">
$ uname -m sparc64 $ gcc -o testbyteorder testbyteorder.c $ ./testbyteorder big-endian
</source>
Результаты запуска на little-endian машине (x86): <source lang="text">
$ uname -m i386 $ gcc -o testbyteorder testbyteorder.c $ ./testbyteorder little-endian
</source>
Вещественные числа
Хранение вещественных чисел также может зависеть от порядка байтов. Так, на 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()
, и так далее;
<source lang="erlang">
<<Count:32/big-unsigned-integer, Average:64/big-float>> = Chunk
Message = <<Length:32/little-unsigned-integer, MType:16/little-unsigned-integer, MessageBody>>
</source>
<source lang="python">
import struct Count, Average = struct.unpack(">Ld", Chunk) Message = struct.pack("<LH", Length, MType) + MessageBody
</source>
Perl:
<source lang="perl">
($Count, $Average) = unpack('L>d>', $Chunk); $Message = pack('(LS)<', $Length, $MType) . $MessageBody; (или то же самое: $Message = pack('Vv', $Length, $MType) . $MessageBody;)
</source>
данные примеры для Erlang, Python, Perl содержат идентичную функциональность.
Процессоры Intel x86-64 имеют инструкцию BSWAP для смены порядка байтов.
Этимология названия
Термины big-endian и little-endian первоначально не имели отношения к информатике. В сатирическом произведении Джонатана Свифта «Путешествия Гулливера» описываются вымышленные государства Лилипутия и Блефуску, в течение многих лет ведущие между собой войны из-за разногласия по поводу того, с какого конца следует разбивать варёные яйца. Тех, кто считает, что их нужно разбивать с тупого конца, в произведении называют Big-endians («тупоконечники»).
Споры между сторонниками big-endian и little-endian в информатике также часто носят характер т. н. «религиозных войн». Термины big-endian и little-endian ввёл Шаблон:Нп2 в 1980 году в своей статье «On Holy Wars and a Plea for Peace» («О священных войнах и призыв к миру»).