В этой статье мы поговорим о понятиях big-endian и little-endian в computer science.
Эта запись также доступна в канале Telegram “DEV: Рубиновые тона”, а обсудить же эту тему можно в нашем чате Telegram.
Дом, который построил Свифт
Частенько в руководствах и документации можно встретить термины big-endian и little-endian — да хотя бы в статье про кодировки UTF8 и ASCII. Но что эти понятия вообще значат? На самом деле, всё довольно просто: это буквально война тупоконечников и остроконечников (я серьёзно).
Когда-то давно Джонатан Свифт написал роман “Путешествия Гулливера” — помните такой? Там была история, что, дескать, изначально варёные яйца лилипуты разбивали с тупого конца, но потом один из императоров умудрился себе порезать руку за завтраком, очищая яичко, после чего был издан указ: разбивать яйца только с острого конца, иначе будут применяться санкции.
Это привело к кровавым бунтам, тысячи человек пошли на казнь, так как, следуя заветам предков, всё равно разбивали яйца с тупого конца. По этой теме писались книги (хотя тупоконечную литературу запрещали), устраивались диспуты, и так далее, хотя в главном трактате некоего пророка было написано просто “разбивайте так, как вам больше нравится”. Скажем прямо, в реальной жизни подобный идиотизм тоже встречается частенько. Это, конечно, едкая сатира, но она напрямую связана с нашим вопросом.
В оригинале “тупоконечники” — это “big endians”, а их противники — “little endians”. Отсюда и пошли эти термины; впервые в далёком 80-м году их употребил Дэнни Коэн, один из участников проекта ARPANet. Это была во многом шутка, но в итоге словечки прижились.
Порядок следования байтов
Предположим, у нас есть шестнадцатеричное число 0x12345678
, и оно размещается в памяти, начиная с адреса 0x100
(про организацию памяти можно глянуть стрим). Каждая двойка шестнадцатеричных чисел (12
, 34
и так далее) занимает по 8 бит, потому что каждое число hex занимает 4 бита: 0x0 = 0b0000
, 0xf = 0b1111
. Следовательно, для хранения этого числа потребуется 4 байта или 32 бита (это называется “слово”, “word”). Если принять, что каждый адрес в памяти указывает на 8 бит информации, то наше число займёт адреса с 0x100
по 0x103
. Но вопрос в том, как эти пары там разместить: справа налево или слева направо?
Можно сделать так:
100 101 102 103
12 34 56 78
Но можно сделать и так:
100 101 102 103
78 56 34 12
То есть сначала может идти “самая значимая” пара (это 0x12
), а может, наоборот, наименее значимая (0x78
). Казалось бы, выбор очевиден, но можно вспомнить хотя бы о том, что некоторые народы читают справа налево, а кто-то вообще пишет столбиком. Так что, на самом деле, всё не так однозначно™.
Поэтому подход, когда наиболее значимый байт идёт на первой позиции, называется “big endian” или BE (большинству из нас это куда привычнее), а “little endian” или LE — это когда сначала идёт наименее значимый байт. В разных машинах может задействоваться один из двух подходов, к примеру, многие устройства (но не все) от Oracle используют режим big endian. Впрочем, есть чипы вида bi-endian, которые умеют работать в обоих режимах.
Кроме того, многое зависит от самой операционной системы. Так, Android и iOS используют именно little-endian, с Windows в целом аналогичная история. В сетевых же протоколах, напротив, используется вариант big endian (принятую по сети информацию может потребоваться переставить обратно в вариант little endian, в зависимости от машины).
Есть ли принципиальная разница?
На самом деле, с технической точки зрения нет особенной разницы, какой вариант представления использовать, равно как и нет разницы, с какой стороны разбивать яйцо. Для обычных программистов знать, какой режим использует ОС, особенно не нужно, потому что это должны учитывать компиляторы.
Однако этот термин может встречаться в других местах — например, в документации Solidity и Ethereum, где рассказывается о хранении данных в слотах (к примеру, uint хранится в виде big endian, равно как и селектор функции в calldata).
Интересно, что сказал бы по этому поводу Свифт…