когда endianess имеет значение — операции приведения

Возможный дубликат:
Когда Endianness становится фактором?

читая это Tuto Что касается endianess, я падаю на этот пример, где endianess имеет значение. Речь идет о записи символа *, заполненного 1 и 0. Затем он может быть преобразован в короткий, и результаты зависят от порядка байтов, малого или большого. Вот пример, цитируемый.

unsigned char endian [2] = {1, 0};
короткий х;

x = *(short *) endian;

Каково будет значение х? Давайте посмотрим, что делает этот код.
Вы создаете массив из двух байтов, а затем приводите этот массив
два байта в одном коротком. Используя массив, вы в основном вынуждены
определенный порядок байтов, и вы увидите, как система обрабатывает
эти два байта. Если это система с прямым порядком байтов, 0 и 1
интерпретируется в обратном направлении и рассматривается как 0,1. Поскольку старший байт
0, это не имеет значения, и младший байт равен 1, поэтому x равен 1. На
с другой стороны, если это система с прямым порядком байтов, старший байт равен 1, а
значение х составляет 256.

мне интересно: когда вы создаете экземпляр массива с заданным количеством выделенных байтов памяти (здесь два байта), как можно выполнить преобразование в любой тип (short, int …), если массиву выделено число байтов, соответствующих этому байту? если для «содержания этого типа» было выделено недостаточно памяти, будет ли считываться следующий адрес памяти? например, если я хочу привести endian к long, это будет выполнено, прочитав четыре байта из начала endian, или это не получится?

Затем возникает вопрос о порядке байтов: это характерная особенность процессора в отношении привычки записывать байты в память, причем наиболее значимый байт находится в самой младшей ячейке памяти (старшем порядке) или в самой старшей ячейке памяти (младшем порядке). в этом случае был выделен массив с двумя однобайтовыми элементами. почему 1 считается самым значимым байтом?

2

Решение

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

Я взял эту простую программу:

#include <iostream>

int main()
{
unsigned endian[2] = { 0, 0 } ;
long * casted_endian = reinterpret_cast<long*>( endian );
std::cout << *casted_endian << std::endl;
}

и я извлек этот код с помощью objdump, Давайте расшифруем это.

 804879c:   55                      push   %ebp
804879d:   89 e5                   mov    %esp,%ebp
804879f:   83 e4 f0                and    $0xfffffff0,%esp
80487a2:   83 ec 20                sub    $0x20,%esp

Эти строки являются просто прологом функции, игнорируйте их.

    unsigned endian[2] = { 0, 0 } ;
80487a5:   c7 44 24 14 00 00 00    movl   $0x0,0x14(%esp)
80487ac:   00
80487ad:   c7 44 24 18 00 00 00    movl   $0x0,0x18(%esp)
80487b4:   00

Из этих двух строк вы можете видеть, что (0x14)% esp инициализируется с 0. Итак, вы знаете, что массив endian находится в стеке, по адресу в регистре% ESP (указатель стека) + 0x14.

    long * casted_endian = reinterpret_cast<long*>( endian );
80487b5:   8d 44 24 14             lea    0x14(%esp),%eax

LEA — это просто арифметическая операция. EAX теперь содержит% ESP + 0x14, который является адресом массива в стеке.

 80487b9:   89 44 24 1c             mov    %eax,0x1c(%esp)

И по адресу ESP + 0x1c (где находится переменная casted_endian) мы ставим EAX, поэтому адрес первого байта endian.

    std::cout << *casted_endian << std::endl;
80487bd:   8b 44 24 1c             mov    0x1c(%esp),%eax
80487c1:   8b 00                   mov    (%eax),%eax
80487c3:   89 44 24 04             mov    %eax,0x4(%esp)
80487c7:   c7 04 24 40 a0 04 08    movl   $0x804a040,(%esp)
80487ce:   e8 1d fe ff ff          call   80485f0 <std::ostream::operator<<(long)@plt>

Затем мы готовим звонок оператору << с соответствующим аргументом без каких-либо дополнительных проверок. Вот и все, программа больше не будет проверять. Тип переменной совершенно не имеет отношения к машине.

Теперь две вещи могут произойти, когда operator<< будет читать часть *casted_endian которые не в массиве.

Либо его адрес находится на странице памяти, которая в данный момент отображается, либо нет. В первом случае operator<< будет читать все, что по этому адресу без жалоб. Это, вероятно, напишет на экране что-то странное. Во втором случае ваша ОС будет жаловаться на то, что программа пытается прочитать что-то, что он не может прочитать, и провоцирует прерывание. Это знаменитая ошибка сегментации.

2

Другие решения

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

0

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

То, что вы делаете, это приведение массива endian коротко. Теперь массивы — это в основном указатели, имя массива фактически содержит адрес первого элемента. Единственное реальное отличие состоит в том, что массивы содержат более полезные метаданные, а некоторые операции с массивами отличаются (sizeof, например). Затем вы используете этот адрес (endian) и создание short указатель от него. Адрес памяти остается прежним, просто вы интерпретируете данные, на которые указывают по-разному. Затем вы разыменовываете этот указатель, чтобы вернуть значение и присвоить его x,

Быстрое примечание стороны. Это может не работать на всех системах. В С, int определяется как ширина, равная собственному размеру слова вашей архитектуры (4 байта на x86, 8 на x86_64). short тогда определяется только как короче, чем int (или равно, если память работает правильно). По этой причине этот код не будет работать на 8-битных архитектурах. Чтобы это работало, размер целевого типа данных в байтах должен быть равен или меньше размера массива.

В равной степени, long просто определено, чтобы быть длиннее, чем intобычно 8 или 16 байт на x86 и x86_64 соответственно. В этом случае этот код будет работать на x86:

unsigned char endian[8] = {1,2,3,4,5,6,7,8};
long x = *(long*)endian;

В любом случае, порядковый номер процессора полностью зависит от процессора. x86 является прямым порядком байтов (и в основном начал конвенцию устройств LE, IIRC). SPARC с прямым порядком байтов (до 9, который может быть и тем и другим). ARM и MIPS также можно настраивать, а Microblaze зависит от используемой шины (AXI или PLB). В любом случае, порядковый номер не ограничивается только процессорами, он также является проблемой при взаимодействии с оборудованием или другими компьютерами.

Для вашего последнего вопроса, самый значимый байт называется так, потому что значение представляет больше, чем наибольшее значение, которое могут представлять младшие байты. В случае 16-битного слова младший значащий байт может представлять 0-255, а старший значащий байт — 256-65535.

В любом случае, если вы не занимаетесь системным программированием низкого уровня (а я имею в виду прямое изменение памяти) или пишете протоколы связи, вы никогда не будете Когда-либо нужно беспокоиться о порядке байтов

0

unsigned char endian[2] = {1, 0};
short x;

x = *(short *) endian;

Этот код имеет неопределенное поведение. Результат может быть x установите значение 1, 256, 4000, иначе программа может аварийно завершить работу или что-то еще может произойти по закону. Это имеет место даже без учета того, является ли массив достаточно большим для типа, к которому он приведен.

Вот переписать код, чтобы сделать его легальным и сделать то, что задумал автор.

unsigned char endian[sizeof(short)] = {1};
short x;
std::memcpy(&x, endian, sizeof(short));

Если бы вы должны были написать код, который пытался получить int из этого массива он получит доступ за допустимые границы массива, и вы снова столкнетесь с неопределенным поведением; может произойти все, что угодно.

в этом случае был выделен массив с двумя однобайтовыми элементами. почему 1 считается самым значимым байтом?

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

Потому что в этом примере система имеет младший порядок байтов и, как вы говорите, определение байтов с прямым порядком байтов — это самый старший байт в ячейке памяти с самым высоким адресом. endian[1] имеет более высокий адрес, чем endian[0] так endian[1] будет содержать самый значимый байт.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector