Russian Qt Forum
Ноябрь 23, 2024, 18:26 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Проблемы с разыменованием  (Прочитано 6807 раз)
juvf
Программист
*****
Offline Offline

Сообщений: 570


Просмотр профиля
« : Январь 06, 2011, 14:19 »

вот код

Код:
//alt_u16 - это тип данных, беззнаковое двухбайтное, можно считать что unsigned short
char bufRecive[100];
alt_u16* p = reinterpret_cast<alt_u16*>(&bufRecive[0]);
*p = 0x1234;
p = reinterpret_cast<alt_u16*>(&bufRecive[1]);
*p = 0x7890;
p = reinterpret_cast<alt_u16*>(&bufRecive[2]);
*p = 0x4321;
p = reinterpret_cast<alt_u16*>(&bufRecive[3]);
*p = 0x0987;
p = reinterpret_cast<alt_u16*>(&bufRecive[4]);
*p = 0x6571;
p = reinterpret_cast<alt_u16*>(&bufRecive[5]);
*p = 0x4294;
p = reinterpret_cast<alt_u16*>(&bufRecive[6]);
*p = 0x5302;
p = reinterpret_cast<alt_u16*>(&bufRecive[7]);
*p = 0x0192;

шагаю дебагом по строчке. когда указатель р указывает на четный адрес, то изменяется значение по адресам р и р+1, когда р указывает на нечетный адрес, изменяется значение по адресам р-1 и р. Чё-то тут с выравниванием связанно. Как это победить? компилятор nios2-elf-gcc. Но в доках на компилятор нет ни каких ограничений по поводу разименовывания.
Записан
pastor
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 2901



Просмотр профиля WWW
« Ответ #1 : Январь 06, 2011, 15:39 »

Ну а как ты хотел. Все верно. У тебя же массив char же (char == 1 байт), а записываешь двухбайтные данные.

bufRecive[0] - адресс 0 элемента массива.

Цитировать
alt_u16* p = reinterpret_cast<alt_u16*>(&bufRecive[0]);
*p = 0x1234;

данные попали в bufRecive[0] и bufRecive[1]


bufRecive[1] - адресс 1 элемента массива. По этому адресу уже содержатся данные с первой операции (это ведь второй байт)

Цитировать
p = reinterpret_cast<alt_u16*>(&bufRecive[1]);
*p = 0x7890;

данные попали в bufRecive[1] и bufRecive[2]

и т.д.
Записан

Integrated Computer Solutions, Inc. (ICS)
http://www.ics.com/
Fat-Zer
Гость
« Ответ #2 : Январь 06, 2011, 15:53 »

//оффтоп
А можете рассказать, какой сокральный смысл в использование reinterpretate_cast в данном случае? Чем он правильней statiс_cast'а и чем лучше олдскульного приведения? (я так понял alt_u16 это тайпдеф или дефайн...)

Зарание спасибо.
Записан
brankovic
Гость
« Ответ #3 : Январь 06, 2011, 19:23 »

Скорее всего процессор игнорирует младший бит для указателя на слово. Но зачем вообще понадобилось писать двухбайтовые значения с интервалом в байт?!  Наверное имелось ввиду &buf[0], &buf[2], ...
Записан
juvf
Программист
*****
Offline Offline

Сообщений: 570


Просмотр профиля
« Ответ #4 : Январь 06, 2011, 22:38 »

Цитировать
Цитировать
p = reinterpret_cast<alt_u16*>(&bufRecive[1]);
*p = 0x7890;

данные попали в bufRecive[1] и bufRecive[2]
вся проблема в том что на этом этапе данные попали не в bufRecive[1] и bufRecive[2], а в bufRecive[0] и bufRecive[1]

Поэкспериментировал еще с прогой.... проблема не в касте, давайте без каста и без тайпдефов привиду пример

Код:
char array[10]; //в отладчике посмотрел адрес размещения массива, пусть будет 0x00801C4
short *p = (short *)array;//после этого р равен 0x00801C4
*p = 0x1234; //после этого по адресу 0x00801C4 размещается 0х34, по адресу 0x00801C5 0х12. Всё правильно
                     // т.е. array[0] = 0х34, array[1] = 0х12
//теперь сделаем указатель на нечетный адрес
p = 0x00801C5;//можно тем же reinterpretate_cast<short*>(&array[1]), у меня не было этой строчки,
                       //я сделал точку останова и руками выставил нужный адрес
*p = 0x5678;

после последней строчки я ожидаю изменение по адресам 0x00801C5 и 0x00801C6. Т.е. должно быть array[0] = 0х78, array[1] = 0х56. Но адрес 0x00801C6 (элемент array[2] ) остается без изменения а меняются 0x00801C4 и 0x00801C5, т.е. array[0] = 0х78, array[1] = 0х56. Вот в этом проблема.
Цитировать
Скорее всего процессор игнорирует младший бит для указателя на слово.
Ну вот это уже похоже на истину. Как бы это побороть?


ps
офтоп

Цитировать
Но зачем вообще понадобилось писать двухбайтовые значения с интервалом в байт?!
Такой протокол обмена, я его реализую. Пакет примерно такой: первый байт кадра - адрес, следующие два это длинна пакета, слудеющие два команда, следующий ....., следующие 4 байта параметр Х, следующие 4 байта - это температура в формате float, .... и т.д. Во многих протоколах подобное встречал. Раньше парсил подобными кодом

Код:
char array[100];
int count = f();
array[16] = count;
array[17] = count>>8;
array[18] = count>>16;
array[19] = count>>24;

но решил попробовать явным преобразованием типов (int*)(array+16) = count; , но не тут-то было.

Цитировать
А можете рассказать, какой сокральный смысл в использование reinterpretate_cast в данном случае? Чем он правильней statiс_cast'а и чем лучше олдскульного приведения?
Да сам толком не понимаю. НА си делал явные приведения, типа (int*)(bufRecive[1]). Но в с++ это типа плохой стиль. Есть касты. Сто раз перечитал про касты, запомнил тока одно - в с++ пользуй касты. А почему ... минут 10 помнил после прочтения учебников, потом просто лишняя инфа из головы пропадала. Осталось только "В с++ пользуй касты"
Почему не static_cast? А не работает статик каст с базовыми типами. Компилятор ругается на такой каст
Код:
char array[10];
int i;
i = *static_cast<int*>(array);
  Ну есть ещё статьи что reinterpretate_cast нельзя вообще использовать, а лучше использовать двойной static_cast через указатель на void, как-то static_cast<int*>(statuc_cast<void*>(array)). Но есть также обоснованные мнения, что этот способ не менее опасен, чем reinterpretate_cast.
Записан
brankovic
Гость
« Ответ #5 : Январь 06, 2011, 23:24 »

Цитировать
Скорее всего процессор игнорирует младший бит для указателя на слово.
Ну вот это уже похоже на истину. Как бы это побороть?

Никак. Т.е. на данной архитектуре невозможно записать по нечётному указателю слово. gcc об этом не знает и генерит неправильный код.

Цитировать
Но зачем вообще понадобилось писать двухбайтовые значения с интервалом в байт?!
Такой протокол обмена, я его реализую. Пакет примерно такой: первый байт кадра - адрес, следующие два это длинна пакета, слудеющие два команда, следующий ....., следующие 4 байта параметр Х, следующие 4 байта - это температура в формате float, .... и т.д. Во многих протоколах подобное встречал.

я примерно так делаю:
Код
C++ (Qt)
template <typename T>
void buf_write (char * & b, T data)
{
  union
  {
     T t;
     char bytes [sizeof (T)];
  } u = {data};
 
  for (int i = 0; i < sizeof (u.bytes); ++i)
     *b++ = u.bytes [i];
}
 
...
 
char buf [99];
char *b = buf;
buf_write (b, (char) address);
buf_write (b, (short) packet_length);
buf_write (b, (short) command);
buf_write (b, (int) X);
buf_write (b, (float) temper);
 

Вообще не хочу пугать, но reinterpret_cast не всегда работает к тому же. С ходу пример не приведу, но был случай, когда пришлось на union заменить (причина -- strict aliasing rules).
« Последнее редактирование: Январь 07, 2011, 23:10 от brankovic » Записан
Fat-Zer
Гость
« Ответ #6 : Январь 07, 2011, 02:22 »

Цитировать
Скорее всего процессор игнорирует младший бит для указателя на слово.
Ну вот это уже похоже на истину. Как бы это побороть?
Никак. Т.е. на данной архитектуре невозможно записать по нечётному указателю слово. gcc об этом не знает и генерит неправильный код.
Предупреждать надо, что дело не на x86 происходит...

Цитировать
Цитировать
А можете рассказать, какой сокральный смысл в использование reinterpretate_cast в данном случае? Чем он правильней statiс_cast'а и чем лучше олдскульного приведения?
Да сам толком не понимаю. НА си делал явные приведения, типа (int*)(bufRecive[1]). Но в с++ это типа плохой стиль. Есть касты. Сто раз перечитал про касты, запомнил тока одно - в с++ пользуй касты. А почему ... минут 10 помнил после прочтения учебников, потом просто лишняя инфа из головы пропадала. Осталось только "В с++ пользуй касты"
Ну и обычные массивы - тоже плохой для цпп стиль, не говоря уже о том, чтобы писать слова в байты...
Цитировать
Почему не static_cast? А не работает статик каст с базовыми типами. Компилятор ругается на такой каст
мда... тупил....
Записан
pastor
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 2901



Просмотр профиля WWW
« Ответ #7 : Январь 07, 2011, 05:35 »

А что мешает создать массив тех же alt_u16 и далее огранизовывать с ним работу?

Код
C++ (Qt)
alt_u16 bufRecive[100];
 
bufRecive[0] = 0x1234;
bufRecive[1] = 0x7890;
....
 
 
char *buf = (char *)bufRecive;
 
//do something with buf
Записан

Integrated Computer Solutions, Inc. (ICS)
http://www.ics.com/
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Январь 07, 2011, 09:25 »

Цитировать
Скорее всего процессор игнорирует младший бит для указателя на слово.
Ну вот это уже похоже на истину. Как бы это побороть?
Никак. Т.е. на данной архитектуре невозможно записать по нечётному указателю слово. gcc об этом не знает и генерит неправильный код.
Я сталкивался с тем что это производит дикий оверхед (тормоза в неск. раз). На очень старых процессорах действительно нельзя было записать по нечету - но тогда возбуждалось исключение "address must be even". На Intel это работает нормально

Код
C++ (Qt)
char buf[200];
memset(buf, 0xAA, sizeof(buf));
unsigned short * test = (unsigned short *)(buf + 1);
*test = 0x1234;
 
содержимое buf

AA 34 12 AA ..

Вполне может врать отладчик - стоит проверить
Записан
Fat-Zer
Гость
« Ответ #9 : Январь 07, 2011, 09:48 »

А меня тут ещё озарение посетило... А разве не для таких вещей изобрели структуры?
Записан
juvf
Программист
*****
Offline Offline

Сообщений: 570


Просмотр профиля
« Ответ #10 : Январь 07, 2011, 11:04 »

О!!!  Шокированный На сколько мир стал понятней! Всем спасибо.  Улыбающийся

brankovic спасибо за совет, попробую ваш пример.

А что мешает создать массив тех же alt_u16 и далее огранизовывать с ним работу?
Ни чего не мешает. Привычка. Обычно пишу на 8-ми разрядных микроЭВМ, типа AVR или MSP430. Там ОЗУ как правило байтами исчисляется, например 256 байт. Поэтому приходится экономить на спичках. А тут NIOS II, у него 8 Мб озу. Вполне можно пожертвовать несколькими кб памяти. Тоже хорошие решение. Спасибо за совет.

Цитировать
Предупреждать надо, что дело не на x86 происходит...
Цитировать
Предупреждать надо, что муж волшебник.
Улыбающийся сори, но я и не говорил что на х86. Тем более я указал компилятор nios2-elf-gcc - уже понятно что не х86.

Цитировать
Ну и обычные массивы - тоже плохой для цпп стиль, не говоря уже о том, чтобы писать слова в байты...
На х86 для этих целей использую QByteArray. В ниосе нет Qt. Вначале в качестве ByteArray использовал std::string, но чё то какие-то глюки были с стрингом, временно решил массив сделать. Для маленьких процессоров обычно с++ это экзотика, не говоря уже о стл. Поэтому иногда стл может быть реализован с багами.  А вообще мне тоже массивы не нравятся. А что можно использовать в качестве ByteArray? Можно конечно самому класс написать, но ....
Записан
brankovic
Гость
« Ответ #11 : Январь 07, 2011, 13:04 »

А что мешает создать массив тех же alt_u16 и далее огранизовывать с ним работу?

После первого поста тоже так подумал, но потом последовало описания "протокола":

Пакет примерно такой: первый байт кадра - адрес, следующие два это длинна пакета, слудеющие два команда, следующий ....., следующие 4 байта параметр Х, следующие 4 байта - это температура в формате float, .... и т.д.

-- пакуются элементы переменной длины. А про экономию на спичках сам не вполне понял.

А меня тут ещё озарение посетило... А разве не для таких вещей изобрели структуры?

казалось бы, можно сделать так:

struct packet
{
   char address;
//здесь дырка 1 байт
   short length;
   short command;
//а здесь дырка 2 байта
   float temper;
...
} p = {'0', 1, 2, .3, ...};

send (socket, (void*) &p, sizeof (p)); //посылаем байты структуры, как они лежат в памяти

Но нет, если так делать, то в пакет попадут дырки и формат пакета будет нарушен.
Записан
BRE
Гость
« Ответ #12 : Январь 07, 2011, 13:40 »

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

Вообще мне не совсем понятно почему в данном случае не использовать потоки (stream)? QDataStream специально сделан для создания безопасного пакета данных, который можно спокойно передавать между разными архитектурами и декодировать его на них.
Записан
brankovic
Гость
« Ответ #13 : Январь 07, 2011, 13:50 »

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

целых 50% шанс угадать! Если серьёзно, то там какая-то nios2, я думал там типа usb-протокола, send написал, потому что больше умных слов не знаю Улыбающийся

Вообще мне не совсем понятно почему в данном случае не использовать потоки (stream)? QDataStream

кажется упоминалось "нет qt"

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

Непонимающий оффтоп конечно, но нельзя ли пояснить?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Январь 07, 2011, 14:01 »

из-за выравнивания полей структуры, его всегда можно сделать равным одному байту
Непонимающий оффтоп конечно, но нельзя ли пояснить?
Ну почему оффтоп..

Код
C++ (Qt)
#pragma pack(push, 1)
struct packet
{
  char address;
  short length;
  short command;
  float temper;
...
} p = {'0', 1, 2, .3, ...};
#pragma pack(pop)
 
Хотя если речь идет об экзотических процессорах (напр не умеющих писать по нечетному адресу) - это не спасет

А вообще конечно - передача "структурой" не метод, придется затем возиться с индианой, все равно ничего не выиграем. Конечно QDataStream или др. класс умеющий читать/писать в поток
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.206 секунд. Запросов: 23.