|
Keil. Не работает код, typedef struct |
|
|
|
May 11 2014, 13:41
|
Участник

Группа: Участник
Сообщений: 67
Регистрация: 17-06-12
Пользователь №: 72 370

|
Здравствуйте, товарищи! Прошу помочь разобраться в проблеме. Сам новичок и для написания программы решил разобраться в такой теме как "typedef struct", которая очень полезна при реализации различных интерфейсов (spi, ethernet и прочее). Написал простенькую программу, дабы протестировать и найти возможные подводные камни. Код следующий Код void f1(uint16_t *f1_var1);
typedef struct type1 { uint8_t var1; uint8_t var2; } type1_t; int __main(void) { type1_t *main_var; main_var->var1 = 0xFF; main_var->var2 = 0xFF;
f1((uint16_t*)main_var);
}
void f1(uint16_t *f1_var1) { *f1_var1 = *f1_var1>>1; } Суть программы заключается в формировании пакета данных, состоящего из двух наборов данных (в данном случае var1 и var2) формата uint8_t вызов функции, которая эти данные обрабатывает. Проблема заключается в том, что данные 0xFF не записываются в var1 и var2. С чем это связанно не знаю. Тут значения переменных на этапах отладки
Прошу помочь с проблемой.
Сообщение отредактировал MySOL - May 11 2014, 13:42
|
|
|
|
|
May 11 2014, 14:13
|
Участник

Группа: Участник
Сообщений: 67
Регистрация: 17-06-12
Пользователь №: 72 370

|
Цитата(Палыч @ May 11 2014, 17:55)  Вы забыли свою структуру где либо разместить (выделить под неё память). Уважаемый, вы не могли бы пояснить как это делается? Просто я впервые слышу про такое. Цитата(kolobok0 @ May 11 2014, 18:00)  ответ прозвучал уже, ну и ышо пять копеек: о выравнивании не забудьте. либо прямо в коде сохраняете старое, ставите прагмой новое, после возвращаете старое. либо глобально на весь проект через ключики компиляции... Простите, но я вас немного не понял. Если можете, ткните носом в статейку, где можно про это почитать, что бы вас не утруждать Цитата(KnightIgor @ May 11 2014, 18:03)  Код typedef struct type1 { uint8_t var1; uint8_t var2; } type1_t; int __main(void) { type1_t main_var; main_var.var1 = 0xFF; main_var.var2 = 0xFF;
f1(&main_var);
}
void f1(type1_t *f1_var1) { f1_var1->var1 >>= 1;
} По-моему вы ошиблись=)
|
|
|
|
|
May 11 2014, 14:27
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
когда вы объявляется my_str * str; str - это указатель на структуру, на место в памяти где она лежит, но сам по себе указатель ничто, структуры нет. my_str str; так вы создадите структуру и сможете обращаться к ее полям как str.field, то есть через точку. далее можно сделать указатель my_str *str2 = &str; и уже обращаться к полям через стрелку str2->field, при это такое обращение будет к полю именно первой структуры str. структуры периферии проца описаны через указатели так (условно) GPIO_STR *PORT1 = (GPIO_STR *) 0x434002300; потому что в памяти уже есть данные лежащие как поля в структуре, это собственно регистры управляющие периферией, и вы создаете явный указатель на эту область памяти Цитата Простите, но я вас немного не понял. Если можете, ткните носом в статейку, где можно про это почитать, что бы вас не утруждать есть разное выравнивание памяти, и иногда структура вида struct... { char field1; char field2; } может быть в памяти представлена 2 байтами, лежащими друг за другом, а может 8 байтами, где в 0 лежит field1, потом 3 байта пропуска, и в 4 лежит filed2. Это сделано потому что некоторые процы умеют брать данные только 32 битными или 16 битными словами, и в принципе не могут обращаться в области памяти с адресами не кратными их размеру слова. так вот есть специальные слова позволяющие подсказывать как создавать структуру, типа __packed Цитата По-моему вы ошиблись=) он не ошибся, он заменил указатель структуры на экземпляр type1_t main_var; вместо ваших type1_t *main_var; и соответственно вызовы через стрелку на вызовы через точку. а также добавил функцию обработки данных структуры через указатель... И в целом это все не относиться к кейлу или арму, это вообще вопросы базового знания языка С....
|
|
|
|
|
May 11 2014, 16:55
|
Участник

Группа: Участник
Сообщений: 67
Регистрация: 17-06-12
Пользователь №: 72 370

|
Цитата(Golikov A. @ May 11 2014, 18:27)  .... Благодарю, что указали на ошибку при инициализации указателя! Благодарю всех за помощь! Проблема решена
Сообщение отредактировал IgorKossak - May 12 2014, 09:02
Причина редактирования: бездумное цитирование
|
|
|
|
|
May 11 2014, 17:02
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
Выравнивание - с этим ничего не сделаешь, с этим надо жить  .. вот на пальцах смысл. берем проц типа кортекс м0, если не ошибаюсь этот процессор не умеет обращаться к памяти без выравнивания. делаем в нем структуру struct str { int8_t A; int8_t B; } он выделяет 8 байт памяти и кладет данные A 0 0 0 B 0 0 0 работаем внутри проца и все счастливы. Теперь мы добавляем какой-то интерфейс с внешним миром, например с PC или делаем сохранение параметров в память. Радостно делаем функцию которая посылает данные, с указателем на данные и размером данных, вызываем SaveSendData(MY_STR, 2) и что? облом, структура то не 2 а 8 байт, половина не сохранилась. вызываем SaveSendData(MY_STR, sizeof(MY_STR)), все хорошо, но в памяти отъели 8 байт если мы про это не знаем может затереть данные. Теперь на РС делаем ответную структуру struct str{char A; char B} и что дальше? в компе то нет выравнивания, структура 2 байта, прямое копирования и пересылка - опять облом... казалось, ок, если такая структура плохо, делаем ее struct __packed str { int8_t A; int8_t B; } пересылка - сохранение в память - прием на компе - супер! вот оно счастье, но! Проц то не умеет обращаться в не выровненые адреса, и обращение my_str.B будет всегда вести в my_str.A, хоть убейся об него, пишите В, меняете А, читаете В, получаете А. И один способ записать или изменить В, это считать 32 бита A,B,0,0, вырезать маской нужную часть, сдвинуть и считать данные, для изменения обратно, считать 32 бита, масками изменить нужный кусок, записать обратно... То есть либо теряем скорость, либо удобство, и об этом надо думать и понимать что у вас происходит....
|
|
|
|
|
May 11 2014, 18:17
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Не очень понял как мешает выравнивание структуре struct str { int8_t A; int8_t B; } в ней-же все элементы - байт. На любом CPU, который умеет работать с байтами (и PC и Cortex-M0), с такой структурой никаких проблем выравнивания не будет. Цитата(Golikov A. @ May 11 2014, 23:02)  в компе то нет выравнивания В компе-то - нет, но в си-компиляторах есть. Вне зависимости от CPU. Насчёт пакованных структур - тоже не понял. Компиляторы (IAR во всяком случае) при работе с пакованными структурами на CPU не умеющем невыровненный доступ, знают это прекрасно и выполняют доступ к членам структуры в соответствии с величиной пакованности (1,2,...) - разбивают обращения на несколько 8-и или 16-и-битных (загляните в листинг!). И си-программисту эта кухня невидима. Скорость конечно будет ниже. Но на удобство почти никак не повлияет. Единственно - будет проблема с указателями на такие структуры.
|
|
|
|
|
May 11 2014, 18:49
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
да..., вот потому и хорошо даже для себя много раз прописывать даже очевидные вещи, я правда был не корректен и не точен в структуре struct { char A; char B; } наверное, правда, проблем не будет, а в структуре struct { char A; char B; int C; } проблемы точно должны быть. Также должны быть проблемы между 2 соседними структурами, в массиве, например, если думать что начало второй через sizeof(str) после начала первой.... Цитата В компе-то - нет, но в си-компиляторах есть. Вне зависимости от CPU. Тут я опять не совсем корректен. Я имел ввиду глобально, что обычно на компьютере в силу архитектуры их процов (блин сейчас правда и для компа процов уже не 1, но будем мыслить шире) обычно (по умолчанию) память пакована до байтовой границы. Опять же более строго для все обще принятых компиляторов и языков. Естественно спец компиляторы под платформу с С язык скрывают всю эту кухню, и никогда написав в коде str.B, вы не измените и не получите соседнего поля, но за этим обращением может быть скрыто много действий, то есть как минимум оно может стать не атомарным. Сейчас процы все быстрее, и пару тактов туда суда погоды не делают, но не атомарность может здорово что-то загадить при наличии прерываний. А в 32 битном микроблайзе попытка копирования или печати массива с 1, 2, 3 адреса всегда копировала или печатала его с 0, это не относится к упаковке, но относиться к выравниванию. Иногда разбирая или наоборот заполняю структуру удобнее копировать ее не по полям, а большими кусками из других кусков, данных и памяти, и при таких операциях надо всегда четко знать что как лежит. Мне кажется что кто пишет под процы, должен очень хорошо знать эту кухню изнутри, мы особая каста это не в виндус thread-ов наделать, а потом писать в форумах что программ без ошибок не бывает. И я пытался на примерах (возможно не очень корректно изложенных) показать с чем мы имеем дело.
|
|
|
|
|
May 11 2014, 19:20
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Golikov A. @ May 12 2014, 00:49)  struct { char A; char B; int C; } проблемы точно должны быть. Также должны быть проблемы между 2 соседними структурами, в массиве, например, если думать что начало второй через sizeof(str) после начала первой.... В чём-же? Ну разве, что если использовать её на разных системах, с разным размером int.... Вам известно, что в структурах (классах) си по умолчанию выполняется выравнивание (вне зависимости от CPU; это правило языка си)? Ну только если конечно не предпринять спец. мер (pack). Также и начало второй в массиве - всегда после sizeof() от начала первой как ни странно... Размер (sizeof()) также выравнивается на размер выравнивания структуры (который в свою очередь равен размеру максимального члена).
|
|
|
|
|
May 12 2014, 03:56
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Хм... Я разве где-то подобное говорил?? Я вообще-то говорил почти противоположное: 1. Структуры в си по умолчанию не упакованные. 2. Выравнивание структуры равно выравниванию максимального члена структуры. 3. Максимальное выравнивание для встроенных типов данных как правило не превышает основной разрядности CPU (хотя это не могу утверждать с полной уверенностью). (т.е. - если CPU 32-разрядный, то выравнивание 64-битного встроенного типа будет всё равно ==32). Про упакованность - тоже Вы что-то не поняли. Я как раз писал, что пакование может выполняться не обязательно до уровня байт, может быть до уровня 16-бит или др. см. #pragma pack(push, N) в IAR к примеру. Пример IAR: #pragma pack(push, 2) struct T { u32 a; u8 b; }; #pragma pack(pop) В данной структуре будет sizeof(T)==6. И выравнивание её будет равно ==16бит. И вообще - упаковка не входит в язык си и может выполняться по-разному на разных компиляторах. Цитата(Golikov A. @ May 12 2014, 02:55)  А вы уверены что упакованные 6 байтные структуры идут в памяти через 6 байт на всех типах процов? Очевидно, что в си, если имеется структура (не важно - пакованная или нет), то в массиве элементов этой структуры начало каждого следующего элемента будет находиться по смещению sizeof() от предыдущего. А вот уже величина этого sizeof(T) будет зависеть от наличия pack() в её атрибутах и величины пакования этого самого pack().
|
|
|
|
|
May 12 2014, 06:07
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(Golikov A. @ May 12 2014, 07:40)  А вот объяснения и примера я чет хорошего придумать не могу%(.... вы все врем меня стандартами языка придавливаете  .... Объяснения чего? Чреватости? Пожалуйста: MSP430, 16-битный, доступ к 16-битному значению по невыровненному адресу игнорирует младший бит адреса, результат - идет обращение к одному байту переменной и одному сосенднему. ARM7TDMI - обычно аналогично. В Cortex-M0 - исключение HardFault. Cortex-M3 - Исключение UsageFault или нормальный доступ для некоторых инструкций в зависимости от бита UNALIGN_TRP, для остальных инструкций - UsageFault. Пример в первом же сообщении этой темы: Код void f1(uint16_t *f1_var1);
typedef struct type1 { uint8_t var1; uint8_t var2; } type1_t;
... type1_t *main_var; ..... f1((uint16_t*)main_var);
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|