Вывод произвольного разыменованного указателя

Я заполняю память следующим образом:

char buf[8] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};

А затем по очереди помещаем беззнаковый длинный указатель на первые 5 байтов и выводим результат:

char *c_ptr;
unsigned long *u_ptr;

c_ptr = buf;
for (int i=0;i<5;i++)
{
u_ptr = (unsigned long *)c_ptr;
printf("%X\n",*u_ptr);
c_ptr++;
}

Когда я выполняю этот код на моей платформе x64, я получаю то, что ожидал:

44332211
55443322
66554433
77665544
88776655

Но когда я выполняю тот же код на платформе ARM, я получаю следующее:

44332211
11443322
22114433
33221144
88776655

То есть он связывается каждые 4 байта и разыменовывает только 4 байта в этих пределах.

Поэтому я хочу спросить, если это поведение (когда pointer_value%4 != 0) ошибочный или специфичный для реализации?

UPD:
Я знаю о Endiannes, я хочу знать, это правильно, что я получаю

11443322

вместо

55443322

Т.е. когда у меня есть указатель например 0x10000001
Это делает unsigned long из байтов с адресами 0x10000001, 0x10000002, 0x10000003 и тогда 0x10000000, вместо 0x10000005,

3

Решение

Заподозрив выравнивание памяти я сделал быстрый гугл =)

http://awayitworks.blogspot.co.nz/2010/02/arm-memory-alignment.html

Заявлено в этой статье:

До архитектуры ARMv4 предполагается, что адрес указан для извлечения
содержимое выровнено по памяти … 32-битная выборка данных должна иметь адрес
выровнен по 32-битному и так далее. Как угадал, проблема только
для 32-битной и 16-битной выборки данных. ARM игнорирует младшие 2 бита
адрес, если выборка данных 32-битная, и игнорирует младший 1-битный, если данные
выборка 16-битная. Таким образом, во всех, если адрес не выровнен
тогда выборка данных будет ошибочной.

Обратите внимание на последнее предложение =)

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

// Endian-specific
inline unsigned long ulong_at( const char *p ) {
return ((unsigned long)p[0])
| (((unsigned long)p[1]) << 8)
| (((unsigned long)p[2]) << 16)
| (((unsigned long)p[3]) << 24);
}

Или возможно:

// Architecture-specific
inline unsigned long ulong_at( const char *p ) {
unsigned long val;
char *v = (char*)&val;
v[0] = p[0];
v[1] = p[1];
v[2] = p[2];
v[3] = p[3];
return val;
}
3

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

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

Несовместимый доступ, как правило, является плохой идеей для любой архитектуры. Некоторые выдают SEGFAULT, другие прозрачно обрабатывают ошибку и — очень медленно — синтезируют правильное значение, выбирая два слова, содержащие желаемое значение, и соединяя их вместе. Кажется (хотя я не эксперт), что ARM выбирает четырехбайтовый слот, который занимает указатель, и поворачивает результат так, чтобы младший бит регистра совпадал с указателем.

2

Проблема в том, что вы разыменовываете невыровненный указатель, который, в зависимости от оборудования, может быть неопределенным. Многие архитектуры предполагают, что long* будет выровнен до 32 бит памяти, то есть будет делиться на 4. Если это не так, результат не определен.

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

1

Endianness не объясняет это поведение. Кажется, процессор ARM не разрешает доступ к четырехбайтовой памяти, не выровненный четырехбайтовой границей, и выходные данные указывают, что процессор считывает память, как если бы он был подвергнут битовое вращение вправо 8 битов на байт, доступ к которым осуществляется за пределами четырехбайтовой границы. Увидеть эта статья в Википедии для получения дополнительной информации о выравнивании памяти.

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

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